From 2469f26dfc618dac7853c0de146df7872e60623f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sat, 28 Jun 2003 04:38:04 +0000 Subject: [PATCH] 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 --- src/kernel/core/fs/pipefs.cpp | 266 ++++++++++++++++++++++++++-------- 1 file changed, 208 insertions(+), 58 deletions(-) diff --git a/src/kernel/core/fs/pipefs.cpp b/src/kernel/core/fs/pipefs.cpp index 3d24d91186..c945df41c1 100644 --- a/src/kernel/core/fs/pipefs.cpp +++ b/src/kernel/core/fs/pipefs.cpp @@ -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(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; }