Implemented the basic hand-shaking mechanism; direct data exchange is
now working, but everything that would have to be temporarily buffered is discarded right now (will use cbufs for this). Pipes can now actually be created and removed - this didn't work before. Changed some lock types to better suit their needs. Small fixes. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3708 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+208
-58
@@ -22,13 +22,10 @@
|
||||
|
||||
#include "builtin_fs.h"
|
||||
|
||||
|
||||
namespace pipefs {
|
||||
|
||||
// ToDo: handles file names suboptimally - it has all file names
|
||||
// in a single linked list, no hash lookups or whatever.
|
||||
|
||||
#define PIPEFS_TRACE 1
|
||||
#define PIPEFS_TRACE 0
|
||||
|
||||
#if PIPEFS_TRACE
|
||||
# define TRACE(x) dprintf x
|
||||
@@ -36,11 +33,23 @@ namespace pipefs {
|
||||
# define TRACE(x)
|
||||
#endif
|
||||
|
||||
|
||||
namespace pipefs {
|
||||
|
||||
class Volume;
|
||||
class Inode;
|
||||
struct dir_cookie;
|
||||
|
||||
struct read_request {
|
||||
read_request *prev;
|
||||
read_request *next;
|
||||
void *buffer;
|
||||
size_t buffer_size;
|
||||
size_t bytes_read;
|
||||
|
||||
size_t SpaceLeft() const { return buffer_size - bytes_read; }
|
||||
void Fill(Volume *volume);
|
||||
status_t PutBuffer(const void **_buffer, size_t *_bufferSize);
|
||||
};
|
||||
|
||||
class ReadRequests {
|
||||
@@ -48,20 +57,20 @@ class ReadRequests {
|
||||
ReadRequests();
|
||||
~ReadRequests();
|
||||
|
||||
status_t Lock();
|
||||
status_t Unlock();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
status_t Add(read_request &request);
|
||||
status_t Remove(read_request &request);
|
||||
|
||||
read_request *GetCurrent() const { return fCurrent; }
|
||||
void SkipCurrent() { if (fCurrent) fCurrent = fCurrent->next; }
|
||||
|
||||
private:
|
||||
benaphore fLock;
|
||||
mutex fLock;
|
||||
read_request *fFirst, *fCurrent, *fLast;
|
||||
};
|
||||
|
||||
class Inode;
|
||||
struct dir_cookie;
|
||||
|
||||
class Volume {
|
||||
public:
|
||||
Volume(mount_id id);
|
||||
@@ -120,8 +129,8 @@ class Inode {
|
||||
Inode *Next() const { return fNext; }
|
||||
void SetNext(Inode *inode) { fNext = inode; }
|
||||
|
||||
benaphore *ReadLock() { return &fReadLock; }
|
||||
benaphore *WriteLock() { return &fWriteLock; }
|
||||
sem_id ReadLock() { return fReadLock; }
|
||||
mutex *WriteMutex() { return &fWriteMutex; }
|
||||
|
||||
static int32 HashNextOffset();
|
||||
static uint32 hash_func(void *_node, const void *_key, uint32 range);
|
||||
@@ -135,8 +144,8 @@ class Inode {
|
||||
int32 fType;
|
||||
const char *fName;
|
||||
|
||||
benaphore fReadLock;
|
||||
benaphore fWriteLock;
|
||||
sem_id fReadLock;
|
||||
mutex fWriteMutex;
|
||||
};
|
||||
|
||||
|
||||
@@ -241,7 +250,9 @@ Volume::CreateNode(const char *name, int32 type)
|
||||
|
||||
if (type == S_IFIFO)
|
||||
InsertNode(inode);
|
||||
|
||||
hash_insert(fNodeHash, inode);
|
||||
vfs_new_vnode(ID(), inode->ID(), inode);
|
||||
|
||||
return inode;
|
||||
}
|
||||
@@ -386,7 +397,8 @@ err:
|
||||
|
||||
Inode::Inode(Volume *volume, const char *name, int32 type)
|
||||
:
|
||||
fNext(NULL)
|
||||
fNext(NULL),
|
||||
fHashNext(NULL)
|
||||
{
|
||||
fName = strdup(name);
|
||||
if (fName == NULL)
|
||||
@@ -395,14 +407,21 @@ Inode::Inode(Volume *volume, const char *name, int32 type)
|
||||
fID = volume->GetNextNodeID();
|
||||
fType = type;
|
||||
|
||||
if (benaphore_init(&fReadLock, "pipe read") == B_OK)
|
||||
benaphore_init(&fWriteLock, "pipe write");
|
||||
if (type == S_IFIFO) {
|
||||
fReadLock = create_sem(0, "pipe read");
|
||||
mutex_init(&fWriteMutex, "pipe write");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Inode::~Inode()
|
||||
{
|
||||
free(const_cast<char *>(fName));
|
||||
|
||||
if (fType == S_IFIFO) {
|
||||
delete_sem(fReadLock);
|
||||
mutex_destroy(&fWriteMutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -410,8 +429,7 @@ status_t
|
||||
Inode::InitCheck()
|
||||
{
|
||||
if (fName == NULL
|
||||
|| fReadLock.sem < B_OK
|
||||
|| fWriteLock.sem < B_OK)
|
||||
|| fType == S_IFIFO && (fReadLock < B_OK || fWriteMutex.sem < B_OK))
|
||||
return B_ERROR;
|
||||
|
||||
return B_OK;
|
||||
@@ -455,33 +473,66 @@ Inode::compare_func(void *_node, const void *_key)
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
void
|
||||
read_request::Fill(Volume *volume)
|
||||
{
|
||||
// ToDo: implement me - fill this request with waiting buffers
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
read_request::PutBuffer(const void **_buffer, size_t *_bufferSize)
|
||||
{
|
||||
TRACE(("pipefs: read_request::PutUserBuffer(buffer = %p, size = %lu)\n", *_buffer, *_bufferSize));
|
||||
|
||||
size_t bytes = *_bufferSize;
|
||||
if (bytes > SpaceLeft())
|
||||
bytes = SpaceLeft();
|
||||
|
||||
uint8 *source = (uint8 *)*_buffer;
|
||||
|
||||
if (user_memcpy((uint8 *)buffer + bytes_read, source, bytes) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
bytes_read += bytes;
|
||||
*_buffer = (void *)(source + bytes);
|
||||
*_bufferSize -= bytes;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
ReadRequests::ReadRequests()
|
||||
:
|
||||
fFirst(NULL),
|
||||
fCurrent(NULL),
|
||||
fLast(NULL)
|
||||
{
|
||||
benaphore_init(&fLock, "pipefs read requests");
|
||||
mutex_init(&fLock, "pipefs read requests");
|
||||
}
|
||||
|
||||
|
||||
ReadRequests::~ReadRequests()
|
||||
{
|
||||
benaphore_destroy(&fLock);
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
void
|
||||
ReadRequests::Lock()
|
||||
{
|
||||
return benaphore_lock(&fLock);
|
||||
mutex_lock(&fLock);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
void
|
||||
ReadRequests::Unlock()
|
||||
{
|
||||
return benaphore_unlock(&fLock);
|
||||
mutex_unlock(&fLock);
|
||||
}
|
||||
|
||||
|
||||
@@ -494,14 +545,34 @@ ReadRequests::Add(read_request &request)
|
||||
if (fFirst == NULL)
|
||||
fFirst = &request;
|
||||
|
||||
fLast = &request;
|
||||
// ToDo: could directly skip full requests
|
||||
if (fCurrent == NULL)
|
||||
fCurrent = &request;
|
||||
|
||||
request.prev = fLast;
|
||||
request.next = NULL;
|
||||
fLast = &request;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReadRequests::Remove(read_request &request)
|
||||
{
|
||||
if (request.next != NULL)
|
||||
request.next->prev = request.prev;
|
||||
if (request.prev != NULL)
|
||||
request.prev->next = request.next;
|
||||
|
||||
// update pointers
|
||||
|
||||
if (fCurrent == &request)
|
||||
fCurrent = fCurrent->next;
|
||||
|
||||
if (fLast == &request)
|
||||
fLast = request.prev;
|
||||
|
||||
if (fFirst == &request)
|
||||
fFirst = request.next;
|
||||
}
|
||||
|
||||
|
||||
@@ -626,11 +697,13 @@ pipefs_get_vnode(fs_volume _volume, vnode_id id, fs_vnode *_inode, bool reenter)
|
||||
static status_t
|
||||
pipefs_put_vnode(fs_volume _volume, fs_vnode _node, bool reenter)
|
||||
{
|
||||
#if PIPEFS_TRACE
|
||||
Inode *inode = (Inode *)_node;
|
||||
|
||||
TRACE(("pipefs_putvnode: entry on vnode 0x%Lx, r %d\n", inode->ID(), reenter));
|
||||
#endif
|
||||
|
||||
// ToDo: delete pipe - it isn't needed anymore!
|
||||
|
||||
//delete inode;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -661,14 +734,18 @@ pipefs_remove_vnode(fs_volume _volume, fs_vnode _node, bool reenter)
|
||||
|
||||
|
||||
static status_t
|
||||
pipefs_create(fs_volume _volume, fs_vnode _dir, const char *name, int omode, int perms,
|
||||
pipefs_create(fs_volume _volume, fs_vnode _dir, const char *name, int openMode, int perms,
|
||||
fs_cookie *_cookie, vnode_id *_newVnodeID)
|
||||
{
|
||||
Volume *volume = (Volume *)_volume;
|
||||
|
||||
TRACE(("pipefs_create_dir: dir = %p, name = '%s', perms = %d, &id = %p\n",
|
||||
TRACE(("pipefs_create(): dir = %p, name = '%s', perms = %d, &id = %p\n",
|
||||
_dir, name, perms, _newVnodeID));
|
||||
|
||||
file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie));
|
||||
if (cookie == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
volume->Lock();
|
||||
|
||||
Inode *directory = (Inode *)_dir;
|
||||
@@ -688,22 +765,34 @@ pipefs_create(fs_volume _volume, fs_vnode _dir, const char *name, int omode, int
|
||||
|
||||
volume->Unlock();
|
||||
|
||||
cookie->open_mode = openMode;
|
||||
|
||||
*_cookie = (void *)cookie;
|
||||
*_newVnodeID = inode->ID();
|
||||
notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, inode->ID(), name);
|
||||
|
||||
return B_OK;
|
||||
|
||||
err:
|
||||
volume->Unlock();
|
||||
free(cookie);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
pipefs_open(fs_volume _volume, fs_vnode _v, int openMode, fs_cookie *_cookie)
|
||||
pipefs_open(fs_volume _volume, fs_vnode _node, int openMode, fs_cookie *_cookie)
|
||||
{
|
||||
// allow to open the file, but it can't be done anything with it
|
||||
|
||||
*_cookie = NULL;
|
||||
// initialize the cookie, because pipefs_free_cookie() relies on it
|
||||
file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie));
|
||||
if (cookie == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
cookie->open_mode = openMode;
|
||||
|
||||
*_cookie = (void *)cookie;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@@ -719,14 +808,13 @@ pipefs_close(fs_volume _volume, fs_vnode _vnode, fs_cookie _cookie)
|
||||
|
||||
|
||||
static status_t
|
||||
pipefs_free_cookie(fs_volume _volume, fs_vnode _vnode, fs_cookie _cookie)
|
||||
pipefs_free_cookie(fs_volume _volume, fs_vnode _node, fs_cookie _cookie)
|
||||
{
|
||||
// pipefs_cookie *cookie = _cookie;
|
||||
file_cookie *cookie = (file_cookie *)_cookie;
|
||||
|
||||
TRACE(("pipefs_freecookie: entry vnode %p, cookie %p\n", _vnode, _cookie));
|
||||
TRACE(("pipefs_freecookie: entry vnode %p, cookie %p\n", _node, _cookie));
|
||||
|
||||
// if (cookie)
|
||||
// free(cookie);
|
||||
free(cookie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -744,54 +832,116 @@ pipefs_read(fs_volume _volume, fs_vnode _node, fs_cookie _cookie, off_t pos,
|
||||
void *buffer, size_t *_length)
|
||||
{
|
||||
file_cookie *cookie = (file_cookie *)_cookie;
|
||||
Volume *fs = (Volume *)_volume;
|
||||
Volume *volume = (Volume *)_volume;
|
||||
|
||||
(void)pos;
|
||||
TRACE(("pipefs_read: vnode %p, cookie %p, pos 0x%Lx , len 0x%lx\n", _node, cookie, pos, *_length));
|
||||
|
||||
if ((cookie->open_mode & O_RWMASK) != O_RDONLY)
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
// issue read request
|
||||
|
||||
read_request request;
|
||||
request.buffer = buffer;
|
||||
request.buffer_size = *_length;
|
||||
request.bytes_read = 0;
|
||||
|
||||
ReadRequests &requests = fs->GetReadRequests();
|
||||
|
||||
if (requests.Lock() != B_OK)
|
||||
return B_ERROR;
|
||||
ReadRequests &requests = volume->GetReadRequests();
|
||||
|
||||
requests.Lock();
|
||||
requests.Add(request);
|
||||
requests.Unlock();
|
||||
|
||||
Inode *inode = (Inode *)_node;
|
||||
status_t status = benaphore_lock_etc(inode->ReadLock(),
|
||||
cookie->open_mode & O_NONBLOCK ? B_TIMEOUT : 0, 0);
|
||||
|
||||
if (requests.Lock() != B_OK)
|
||||
panic("pipefs: could not get lock for read requests");
|
||||
// ToDo: here is the race condition that another reader issues
|
||||
// its read request after this one, but locks earlier; this
|
||||
// will currently lead to a dead-lock or failure - that could
|
||||
// be solved by attaching a thread to the request
|
||||
|
||||
// if (status == B_OK) {
|
||||
// requests.FillRequest(request);
|
||||
// wait for it to be filled
|
||||
|
||||
Inode *inode = (Inode *)_node;
|
||||
status_t status = acquire_sem_etc(inode->ReadLock(), 1,
|
||||
(cookie->open_mode & O_NONBLOCK ? B_TIMEOUT : 0 ) | B_CAN_INTERRUPT, 0);
|
||||
|
||||
requests.Lock();
|
||||
|
||||
if (status == B_OK)
|
||||
request.Fill(volume);
|
||||
|
||||
requests.Remove(request);
|
||||
requests.Unlock();
|
||||
|
||||
if (status == B_TIMED_OUT && request.bytes_read > 0)
|
||||
if (status == B_TIMED_OUT || B_INTERRUPTED && request.bytes_read > 0)
|
||||
status = B_OK;
|
||||
|
||||
if (status == B_OK) {
|
||||
if (status == B_OK)
|
||||
*_length = request.bytes_read;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
pipefs_write(fs_volume fs, fs_vnode _node, fs_cookie cookie, off_t pos,
|
||||
pipefs_write(fs_volume _volume, fs_vnode _node, fs_cookie _cookie, off_t pos,
|
||||
const void *buffer, size_t *_length)
|
||||
{
|
||||
file_cookie *cookie = (file_cookie *)_cookie;
|
||||
Volume *volume = (Volume *)_volume;
|
||||
Inode *inode = (Inode *)_node;
|
||||
|
||||
TRACE(("pipefs_write: vnode %p, cookie %p, pos 0x%Lx , len 0x%lx\n", _node, cookie, pos, *_length));
|
||||
|
||||
return EINVAL;
|
||||
if ((cookie->open_mode & O_RWMASK) != O_WRONLY)
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
mutex_lock(inode->WriteMutex());
|
||||
|
||||
ReadRequests &requests = volume->GetReadRequests();
|
||||
requests.Lock();
|
||||
|
||||
size_t bytesLeft = *_length;
|
||||
read_request *request = NULL;
|
||||
|
||||
do {
|
||||
request = requests.GetCurrent();
|
||||
if (request != NULL) {
|
||||
// fill this request
|
||||
|
||||
request->Fill(volume);
|
||||
if (request->SpaceLeft() > 0) {
|
||||
// place our data into that buffer
|
||||
request->PutBuffer(&buffer, &bytesLeft);
|
||||
if (bytesLeft == 0) {
|
||||
release_sem(inode->ReadLock());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
requests.SkipCurrent();
|
||||
}
|
||||
} while (request != NULL);
|
||||
|
||||
status_t status;
|
||||
|
||||
if (request == NULL) {
|
||||
// there is no read request pending, so we have to put
|
||||
// our data in a temporary buffer
|
||||
|
||||
// ToDo: do that using cbufs!
|
||||
*_length -= bytesLeft;
|
||||
dprintf("pipefs: lazy write saw no read_request...\n");
|
||||
release_sem(inode->ReadLock());
|
||||
status = B_OK;
|
||||
} else {
|
||||
// could write everything without the need to copy!
|
||||
status = B_OK;
|
||||
}
|
||||
|
||||
requests.Unlock();
|
||||
mutex_unlock(inode->WriteMutex());
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -839,10 +989,10 @@ pipefs_open_dir(fs_volume _volume, fs_vnode _node, fs_cookie *_cookie)
|
||||
cookie->current = volume->FirstEntry();
|
||||
volume->InsertCookie(cookie);
|
||||
|
||||
*_cookie = cookie;
|
||||
|
||||
volume->Unlock();
|
||||
|
||||
*_cookie = (void *)cookie;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user