1 /* 2 * Copyright 2007-2013, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2003-2010, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "fifo.h" 9 10 #include <limits.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <sys/ioctl.h> 15 #include <sys/stat.h> 16 17 #include <new> 18 19 #include <KernelExport.h> 20 #include <NodeMonitor.h> 21 #include <Select.h> 22 23 #include <condition_variable.h> 24 #include <debug_hex_dump.h> 25 #include <lock.h> 26 #include <select_sync_pool.h> 27 #include <syscall_restart.h> 28 #include <team.h> 29 #include <thread.h> 30 #include <util/DoublyLinkedList.h> 31 #include <util/AutoLock.h> 32 #include <util/ring_buffer.h> 33 #include <vfs.h> 34 #include <vfs_defs.h> 35 #include <vm/vm.h> 36 37 38 //#define TRACE_FIFO 39 #ifdef TRACE_FIFO 40 # define TRACE(x...) dprintf(x) 41 #else 42 # define TRACE(x...) 43 #endif 44 45 46 namespace fifo { 47 48 49 struct file_cookie; 50 class Inode; 51 52 53 class RingBuffer { 54 public: 55 RingBuffer(); 56 ~RingBuffer(); 57 58 status_t CreateBuffer(); 59 void DeleteBuffer(); 60 61 ssize_t Write(const void* buffer, size_t length, 62 bool isUser); 63 ssize_t Read(void* buffer, size_t length, bool isUser); 64 ssize_t Peek(size_t offset, void* buffer, 65 size_t length) const; 66 67 size_t Readable() const; 68 size_t Writable() const; 69 70 private: 71 struct ring_buffer* fBuffer; 72 }; 73 74 75 class ReadRequest : public DoublyLinkedListLinkImpl<ReadRequest> { 76 public: 77 ReadRequest(file_cookie* cookie) 78 : 79 fThread(thread_get_current_thread()), 80 fCookie(cookie), 81 fNotified(true) 82 { 83 B_INITIALIZE_SPINLOCK(&fLock); 84 } 85 86 void SetNotified(bool notified) 87 { 88 InterruptsSpinLocker _(fLock); 89 fNotified = notified; 90 } 91 92 void Notify(status_t status = B_OK) 93 { 94 InterruptsSpinLocker _(fLock); 95 TRACE("ReadRequest %p::Notify(), fNotified %d\n", this, fNotified); 96 97 if (!fNotified) { 98 thread_unblock(fThread, status); 99 fNotified = true; 100 } 101 } 102 103 Thread* GetThread() const 104 { 105 return fThread; 106 } 107 108 file_cookie* Cookie() const 109 { 110 return fCookie; 111 } 112 113 private: 114 spinlock fLock; 115 Thread* fThread; 116 file_cookie* fCookie; 117 volatile bool fNotified; 118 }; 119 120 121 class WriteRequest : public DoublyLinkedListLinkImpl<WriteRequest> { 122 public: 123 WriteRequest(Thread* thread, size_t minimalWriteCount) 124 : 125 fThread(thread), 126 fMinimalWriteCount(minimalWriteCount) 127 { 128 } 129 130 Thread* GetThread() const 131 { 132 return fThread; 133 } 134 135 size_t MinimalWriteCount() const 136 { 137 return fMinimalWriteCount; 138 } 139 140 private: 141 Thread* fThread; 142 size_t fMinimalWriteCount; 143 }; 144 145 146 typedef DoublyLinkedList<ReadRequest> ReadRequestList; 147 typedef DoublyLinkedList<WriteRequest> WriteRequestList; 148 149 150 class Inode { 151 public: 152 Inode(); 153 ~Inode(); 154 155 status_t InitCheck(); 156 157 bool IsActive() const { return fActive; } 158 timespec CreationTime() const { return fCreationTime; } 159 void SetCreationTime(timespec creationTime) 160 { fCreationTime = creationTime; } 161 timespec ModificationTime() const 162 { return fModificationTime; } 163 void SetModificationTime(timespec modificationTime) 164 { fModificationTime = modificationTime; } 165 166 mutex* RequestLock() { return &fRequestLock; } 167 168 status_t WriteDataToBuffer(const void* data, 169 size_t* _length, bool nonBlocking, 170 bool isUser); 171 status_t ReadDataFromBuffer(void* data, size_t* _length, 172 bool nonBlocking, bool isUser, 173 ReadRequest& request); 174 size_t BytesAvailable() const 175 { return fBuffer.Readable(); } 176 size_t BytesWritable() const 177 { return fBuffer.Writable(); } 178 179 void AddReadRequest(ReadRequest& request); 180 void RemoveReadRequest(ReadRequest& request); 181 status_t WaitForReadRequest(ReadRequest& request); 182 183 void NotifyBytesRead(size_t bytes); 184 void NotifyReadDone(); 185 void NotifyBytesWritten(size_t bytes); 186 void NotifyEndClosed(bool writer); 187 188 void Open(int openMode); 189 void Close(file_cookie* cookie); 190 int32 ReaderCount() const { return fReaderCount; } 191 int32 WriterCount() const { return fWriterCount; } 192 193 status_t Select(uint8 event, selectsync* sync, 194 int openMode); 195 status_t Deselect(uint8 event, selectsync* sync, 196 int openMode); 197 198 void Dump(bool dumpData) const; 199 static int Dump(int argc, char** argv); 200 201 private: 202 timespec fCreationTime; 203 timespec fModificationTime; 204 205 RingBuffer fBuffer; 206 207 ReadRequestList fReadRequests; 208 WriteRequestList fWriteRequests; 209 210 mutex fRequestLock; 211 212 ConditionVariable fWriteCondition; 213 214 int32 fReaderCount; 215 int32 fWriterCount; 216 bool fActive; 217 218 select_sync_pool* fReadSelectSyncPool; 219 select_sync_pool* fWriteSelectSyncPool; 220 }; 221 222 223 class FIFOInode : public Inode { 224 public: 225 FIFOInode(fs_vnode* vnode) 226 : 227 Inode(), 228 fSuperVnode(*vnode) 229 { 230 } 231 232 fs_vnode* SuperVnode() { return &fSuperVnode; } 233 234 private: 235 fs_vnode fSuperVnode; 236 }; 237 238 239 struct file_cookie { 240 int open_mode; 241 // guarded by Inode::fRequestLock 242 243 void SetNonBlocking(bool nonBlocking) 244 { 245 if (nonBlocking) 246 open_mode |= O_NONBLOCK; 247 else 248 open_mode &= ~(int)O_NONBLOCK; 249 } 250 }; 251 252 253 // #pragma mark - 254 255 256 RingBuffer::RingBuffer() 257 : 258 fBuffer(NULL) 259 { 260 } 261 262 263 RingBuffer::~RingBuffer() 264 { 265 DeleteBuffer(); 266 } 267 268 269 status_t 270 RingBuffer::CreateBuffer() 271 { 272 if (fBuffer != NULL) 273 return B_OK; 274 275 fBuffer = create_ring_buffer(VFS_FIFO_BUFFER_CAPACITY); 276 return fBuffer != NULL ? B_OK : B_NO_MEMORY; 277 } 278 279 280 void 281 RingBuffer::DeleteBuffer() 282 { 283 if (fBuffer != NULL) { 284 delete_ring_buffer(fBuffer); 285 fBuffer = NULL; 286 } 287 } 288 289 290 inline ssize_t 291 RingBuffer::Write(const void* buffer, size_t length, bool isUser) 292 { 293 if (fBuffer == NULL) 294 return B_NO_MEMORY; 295 if (isUser && !IS_USER_ADDRESS(buffer)) 296 return B_BAD_ADDRESS; 297 298 return isUser 299 ? ring_buffer_user_write(fBuffer, (const uint8*)buffer, length) 300 : ring_buffer_write(fBuffer, (const uint8*)buffer, length); 301 } 302 303 304 inline ssize_t 305 RingBuffer::Read(void* buffer, size_t length, bool isUser) 306 { 307 if (fBuffer == NULL) 308 return B_NO_MEMORY; 309 if (isUser && !IS_USER_ADDRESS(buffer)) 310 return B_BAD_ADDRESS; 311 312 return isUser 313 ? ring_buffer_user_read(fBuffer, (uint8*)buffer, length) 314 : ring_buffer_read(fBuffer, (uint8*)buffer, length); 315 } 316 317 318 inline ssize_t 319 RingBuffer::Peek(size_t offset, void* buffer, size_t length) const 320 { 321 if (fBuffer == NULL) 322 return B_NO_MEMORY; 323 324 return ring_buffer_peek(fBuffer, offset, (uint8*)buffer, length); 325 } 326 327 328 inline size_t 329 RingBuffer::Readable() const 330 { 331 return fBuffer != NULL ? ring_buffer_readable(fBuffer) : 0; 332 } 333 334 335 inline size_t 336 RingBuffer::Writable() const 337 { 338 return fBuffer != NULL ? ring_buffer_writable(fBuffer) : 0; 339 } 340 341 342 // #pragma mark - 343 344 345 Inode::Inode() 346 : 347 fReadRequests(), 348 fWriteRequests(), 349 fReaderCount(0), 350 fWriterCount(0), 351 fActive(false), 352 fReadSelectSyncPool(NULL), 353 fWriteSelectSyncPool(NULL) 354 { 355 fWriteCondition.Publish(this, "pipe"); 356 mutex_init(&fRequestLock, "pipe request"); 357 358 bigtime_t time = real_time_clock(); 359 fModificationTime.tv_sec = time / 1000000; 360 fModificationTime.tv_nsec = (time % 1000000) * 1000; 361 fCreationTime = fModificationTime; 362 } 363 364 365 Inode::~Inode() 366 { 367 fWriteCondition.Unpublish(); 368 mutex_destroy(&fRequestLock); 369 } 370 371 372 status_t 373 Inode::InitCheck() 374 { 375 return B_OK; 376 } 377 378 379 /*! Writes the specified data bytes to the inode's ring buffer. The 380 request lock must be held when calling this method. 381 Notifies readers if necessary, so that blocking readers will get started. 382 Returns B_OK for success, B_BAD_ADDRESS if copying from the buffer failed, 383 and various semaphore errors (like B_WOULD_BLOCK in non-blocking mode). If 384 the returned length is > 0, the returned error code can be ignored. 385 */ 386 status_t 387 Inode::WriteDataToBuffer(const void* _data, size_t* _length, bool nonBlocking, 388 bool isUser) 389 { 390 const uint8* data = (const uint8*)_data; 391 size_t dataSize = *_length; 392 size_t& written = *_length; 393 written = 0; 394 395 TRACE("Inode %p::WriteDataToBuffer(data = %p, bytes = %zu)\n", this, data, 396 dataSize); 397 398 // A request up to VFS_FIFO_ATOMIC_WRITE_SIZE bytes shall not be 399 // interleaved with other writer's data. 400 size_t minToWrite = 1; 401 if (dataSize <= VFS_FIFO_ATOMIC_WRITE_SIZE) 402 minToWrite = dataSize; 403 404 while (dataSize > 0) { 405 // Wait until enough space in the buffer is available. 406 while (!fActive 407 || (fBuffer.Writable() < minToWrite && fReaderCount > 0)) { 408 if (nonBlocking) 409 return B_WOULD_BLOCK; 410 411 ConditionVariableEntry entry; 412 entry.Add(this); 413 414 WriteRequest request(thread_get_current_thread(), minToWrite); 415 fWriteRequests.Add(&request); 416 417 mutex_unlock(&fRequestLock); 418 status_t status = entry.Wait(B_CAN_INTERRUPT); 419 mutex_lock(&fRequestLock); 420 421 fWriteRequests.Remove(&request); 422 423 if (status != B_OK) 424 return status; 425 } 426 427 // write only as long as there are readers left 428 if (fActive && fReaderCount == 0) { 429 if (written == 0) 430 send_signal(find_thread(NULL), SIGPIPE); 431 return EPIPE; 432 } 433 434 // write as much as we can 435 436 size_t toWrite = (fActive ? fBuffer.Writable() : 0); 437 if (toWrite > dataSize) 438 toWrite = dataSize; 439 440 if (toWrite > 0) { 441 ssize_t bytesWritten = fBuffer.Write(data, toWrite, isUser); 442 if (bytesWritten < 0) 443 return bytesWritten; 444 } 445 446 data += toWrite; 447 dataSize -= toWrite; 448 written += toWrite; 449 450 NotifyBytesWritten(toWrite); 451 } 452 453 return B_OK; 454 } 455 456 457 status_t 458 Inode::ReadDataFromBuffer(void* data, size_t* _length, bool nonBlocking, 459 bool isUser, ReadRequest& request) 460 { 461 size_t dataSize = *_length; 462 *_length = 0; 463 464 // wait until our request is first in queue 465 status_t error; 466 if (fReadRequests.Head() != &request) { 467 if (nonBlocking) 468 return B_WOULD_BLOCK; 469 470 TRACE("Inode %p::%s(): wait for request %p to become the first " 471 "request.\n", this, __FUNCTION__, &request); 472 473 error = WaitForReadRequest(request); 474 if (error != B_OK) 475 return error; 476 } 477 478 // wait until data are available 479 while (fBuffer.Readable() == 0) { 480 if (nonBlocking) 481 return B_WOULD_BLOCK; 482 483 if (fActive && fWriterCount == 0) 484 return B_OK; 485 486 TRACE("Inode %p::%s(): wait for data, request %p\n", this, __FUNCTION__, 487 &request); 488 489 error = WaitForReadRequest(request); 490 if (error != B_OK) 491 return error; 492 } 493 494 // read as much as we can 495 size_t toRead = fBuffer.Readable(); 496 if (toRead > dataSize) 497 toRead = dataSize; 498 499 ssize_t bytesRead = fBuffer.Read(data, toRead, isUser); 500 if (bytesRead < 0) 501 return bytesRead; 502 503 NotifyBytesRead(toRead); 504 505 *_length = toRead; 506 507 return B_OK; 508 } 509 510 511 void 512 Inode::AddReadRequest(ReadRequest& request) 513 { 514 fReadRequests.Add(&request); 515 } 516 517 518 void 519 Inode::RemoveReadRequest(ReadRequest& request) 520 { 521 fReadRequests.Remove(&request); 522 } 523 524 525 status_t 526 Inode::WaitForReadRequest(ReadRequest& request) 527 { 528 // add the entry to wait on 529 thread_prepare_to_block(thread_get_current_thread(), B_CAN_INTERRUPT, 530 THREAD_BLOCK_TYPE_OTHER, "fifo read request"); 531 532 request.SetNotified(false); 533 534 // wait 535 mutex_unlock(&fRequestLock); 536 status_t status = thread_block(); 537 538 // Before going to lock again, we need to make sure no one tries to 539 // unblock us. Otherwise that would screw with mutex_lock(). 540 request.SetNotified(true); 541 542 mutex_lock(&fRequestLock); 543 544 return status; 545 } 546 547 548 void 549 Inode::NotifyBytesRead(size_t bytes) 550 { 551 // notify writer, if something can be written now 552 size_t writable = fBuffer.Writable(); 553 if (bytes > 0) { 554 // notify select()ors only, if nothing was writable before 555 if (writable == bytes) { 556 if (fWriteSelectSyncPool) 557 notify_select_event_pool(fWriteSelectSyncPool, B_SELECT_WRITE); 558 } 559 560 // If any of the waiting writers has a minimal write count that has 561 // now become satisfied, we notify all of them (condition variables 562 // don't support doing that selectively). 563 WriteRequest* request; 564 WriteRequestList::Iterator iterator = fWriteRequests.GetIterator(); 565 while ((request = iterator.Next()) != NULL) { 566 size_t minWriteCount = request->MinimalWriteCount(); 567 if (minWriteCount > 0 && minWriteCount <= writable 568 && minWriteCount > writable - bytes) { 569 fWriteCondition.NotifyAll(); 570 break; 571 } 572 } 573 } 574 } 575 576 577 void 578 Inode::NotifyReadDone() 579 { 580 // notify next reader, if there's still something to be read 581 if (fBuffer.Readable() > 0) { 582 if (ReadRequest* request = fReadRequests.First()) 583 request->Notify(); 584 } 585 } 586 587 588 void 589 Inode::NotifyBytesWritten(size_t bytes) 590 { 591 // notify reader, if something can be read now 592 if (bytes > 0 && fBuffer.Readable() == bytes) { 593 if (fReadSelectSyncPool) 594 notify_select_event_pool(fReadSelectSyncPool, B_SELECT_READ); 595 596 if (ReadRequest* request = fReadRequests.First()) 597 request->Notify(); 598 } 599 } 600 601 602 void 603 Inode::NotifyEndClosed(bool writer) 604 { 605 TRACE("Inode %p::%s(%s)\n", this, __FUNCTION__, 606 writer ? "writer" : "reader"); 607 608 if (writer) { 609 // Our last writer has been closed; if the pipe 610 // contains no data, unlock all waiting readers 611 TRACE(" buffer readable: %zu\n", fBuffer.Readable()); 612 if (fBuffer.Readable() == 0) { 613 ReadRequestList::Iterator iterator = fReadRequests.GetIterator(); 614 while (ReadRequest* request = iterator.Next()) 615 request->Notify(); 616 617 if (fReadSelectSyncPool) 618 notify_select_event_pool(fReadSelectSyncPool, B_SELECT_READ); 619 } 620 } else { 621 // Last reader is gone. Wake up all writers. 622 fWriteCondition.NotifyAll(); 623 624 if (fWriteSelectSyncPool) { 625 notify_select_event_pool(fWriteSelectSyncPool, B_SELECT_WRITE); 626 notify_select_event_pool(fWriteSelectSyncPool, B_SELECT_ERROR); 627 } 628 } 629 } 630 631 632 void 633 Inode::Open(int openMode) 634 { 635 MutexLocker locker(RequestLock()); 636 637 if ((openMode & O_ACCMODE) == O_WRONLY || (openMode & O_ACCMODE) == O_RDWR) 638 fWriterCount++; 639 640 if ((openMode & O_ACCMODE) == O_RDONLY || (openMode & O_ACCMODE) == O_RDWR) 641 fReaderCount++; 642 643 if (fReaderCount > 0 && fWriterCount > 0) { 644 TRACE("Inode %p::Open(): fifo becomes active\n", this); 645 fBuffer.CreateBuffer(); 646 fActive = true; 647 648 // notify all waiting writers that they can start 649 if (fWriteSelectSyncPool) 650 notify_select_event_pool(fWriteSelectSyncPool, B_SELECT_WRITE); 651 fWriteCondition.NotifyAll(); 652 } 653 } 654 655 656 void 657 Inode::Close(file_cookie* cookie) 658 { 659 660 MutexLocker locker(RequestLock()); 661 662 int openMode = cookie->open_mode; 663 TRACE("Inode %p::Close(openMode = %" B_PRId32 ")\n", this, openMode); 664 665 // Notify all currently reading file descriptors 666 ReadRequestList::Iterator iterator = fReadRequests.GetIterator(); 667 while (ReadRequest* request = iterator.Next()) { 668 if (request->Cookie() == cookie) 669 request->Notify(B_FILE_ERROR); 670 } 671 672 if ((openMode & O_ACCMODE) == O_WRONLY || (openMode & O_ACCMODE) == O_RDWR) { 673 if (--fWriterCount == 0) 674 NotifyEndClosed(true); 675 } 676 677 if ((openMode & O_ACCMODE) == O_RDONLY || (openMode & O_ACCMODE) == O_RDWR) { 678 if (--fReaderCount == 0) 679 NotifyEndClosed(false); 680 } 681 682 if (fWriterCount == 0) { 683 // Notify any still reading writers to stop 684 // TODO: This only works reliable if there is only one writer - we could 685 // do the same thing done for the read requests. 686 fWriteCondition.NotifyAll(B_FILE_ERROR); 687 } 688 689 if (fReaderCount == 0 && fWriterCount == 0) { 690 fActive = false; 691 fBuffer.DeleteBuffer(); 692 } 693 } 694 695 696 status_t 697 Inode::Select(uint8 event, selectsync* sync, int openMode) 698 { 699 bool writer = true; 700 select_sync_pool** pool; 701 if (event == B_SELECT_READ || (openMode & O_RWMASK) == O_RDONLY) { 702 pool = &fReadSelectSyncPool; 703 writer = false; 704 } else if ((openMode & O_RWMASK) == O_WRONLY) { 705 pool = &fWriteSelectSyncPool; 706 } else 707 return B_NOT_ALLOWED; 708 709 if (add_select_sync_pool_entry(pool, sync, event) != B_OK) 710 return B_ERROR; 711 712 // signal right away, if the condition holds already 713 if (writer) { 714 if ((event == B_SELECT_WRITE 715 && (fBuffer.Writable() > 0 || fReaderCount == 0)) 716 || (event == B_SELECT_ERROR && fReaderCount == 0)) { 717 return notify_select_event(sync, event); 718 } 719 } else { 720 if (event == B_SELECT_READ 721 && (fBuffer.Readable() > 0 || fWriterCount == 0)) { 722 return notify_select_event(sync, event); 723 } 724 } 725 726 return B_OK; 727 } 728 729 730 status_t 731 Inode::Deselect(uint8 event, selectsync* sync, int openMode) 732 { 733 select_sync_pool** pool; 734 if (event == B_SELECT_READ || (openMode & O_RWMASK) == O_RDONLY) { 735 pool = &fReadSelectSyncPool; 736 } else if ((openMode & O_RWMASK) == O_WRONLY) { 737 pool = &fWriteSelectSyncPool; 738 } else 739 return B_NOT_ALLOWED; 740 741 remove_select_sync_pool_entry(pool, sync, event); 742 return B_OK; 743 } 744 745 746 void 747 Inode::Dump(bool dumpData) const 748 { 749 kprintf("FIFO %p\n", this); 750 kprintf(" active: %s\n", fActive ? "true" : "false"); 751 kprintf(" readers: %" B_PRId32 "\n", fReaderCount); 752 kprintf(" writers: %" B_PRId32 "\n", fWriterCount); 753 754 if (!fReadRequests.IsEmpty()) { 755 kprintf(" pending readers:\n"); 756 for (ReadRequestList::ConstIterator it = fReadRequests.GetIterator(); 757 ReadRequest* request = it.Next();) { 758 kprintf(" %p: thread %" B_PRId32 ", cookie: %p\n", request, 759 request->GetThread()->id, request->Cookie()); 760 } 761 } 762 763 if (!fWriteRequests.IsEmpty()) { 764 kprintf(" pending writers:\n"); 765 for (WriteRequestList::ConstIterator it = fWriteRequests.GetIterator(); 766 WriteRequest* request = it.Next();) { 767 kprintf(" %p: thread %" B_PRId32 ", min count: %zu\n", request, 768 request->GetThread()->id, request->MinimalWriteCount()); 769 } 770 } 771 772 kprintf(" %zu bytes buffered\n", fBuffer.Readable()); 773 774 if (dumpData && fBuffer.Readable() > 0) { 775 struct DataProvider : BKernel::HexDumpDataProvider { 776 DataProvider(const RingBuffer& buffer) 777 : 778 fBuffer(buffer), 779 fOffset(0) 780 { 781 } 782 783 virtual bool HasMoreData() const 784 { 785 return fOffset < fBuffer.Readable(); 786 } 787 788 virtual uint8 NextByte() 789 { 790 uint8 byte = '\0'; 791 if (fOffset < fBuffer.Readable()) { 792 fBuffer.Peek(fOffset, &byte, 1); 793 fOffset++; 794 } 795 return byte; 796 } 797 798 virtual bool GetAddressString(char* buffer, size_t bufferSize) const 799 { 800 snprintf(buffer, bufferSize, " %4zx", fOffset); 801 return true; 802 } 803 804 private: 805 const RingBuffer& fBuffer; 806 size_t fOffset; 807 }; 808 809 DataProvider dataProvider(fBuffer); 810 BKernel::print_hex_dump(dataProvider, fBuffer.Readable()); 811 } 812 } 813 814 815 /*static*/ int 816 Inode::Dump(int argc, char** argv) 817 { 818 bool dumpData = false; 819 int argi = 1; 820 if (argi < argc && strcmp(argv[argi], "-d") == 0) { 821 dumpData = true; 822 argi++; 823 } 824 825 if (argi >= argc || argi + 2 < argc) { 826 print_debugger_command_usage(argv[0]); 827 return 0; 828 } 829 830 Inode* node = (Inode*)parse_expression(argv[argi]); 831 if (IS_USER_ADDRESS(node)) { 832 kprintf("invalid FIFO address\n"); 833 return 0; 834 } 835 836 node->Dump(dumpData); 837 return 0; 838 } 839 840 841 // #pragma mark - vnode API 842 843 844 static status_t 845 fifo_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) 846 { 847 FIFOInode* fifo = (FIFOInode*)vnode->private_node; 848 fs_vnode* superVnode = fifo->SuperVnode(); 849 850 status_t error = B_OK; 851 if (superVnode->ops->put_vnode != NULL) 852 error = superVnode->ops->put_vnode(volume, superVnode, reenter); 853 854 delete fifo; 855 856 return error; 857 } 858 859 860 static status_t 861 fifo_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) 862 { 863 FIFOInode* fifo = (FIFOInode*)vnode->private_node; 864 fs_vnode* superVnode = fifo->SuperVnode(); 865 866 status_t error = B_OK; 867 if (superVnode->ops->remove_vnode != NULL) 868 error = superVnode->ops->remove_vnode(volume, superVnode, reenter); 869 870 delete fifo; 871 872 return error; 873 } 874 875 876 static status_t 877 fifo_open(fs_volume* _volume, fs_vnode* _node, int openMode, 878 void** _cookie) 879 { 880 Inode* inode = (Inode*)_node->private_node; 881 882 TRACE("fifo_open(): node = %p, openMode = %d\n", inode, openMode); 883 884 file_cookie* cookie = (file_cookie*)malloc(sizeof(file_cookie)); 885 if (cookie == NULL) 886 return B_NO_MEMORY; 887 888 TRACE(" open cookie = %p\n", cookie); 889 cookie->open_mode = openMode; 890 inode->Open(openMode); 891 892 *_cookie = (void*)cookie; 893 894 return B_OK; 895 } 896 897 898 static status_t 899 fifo_close(fs_volume* volume, fs_vnode* vnode, void* _cookie) 900 { 901 file_cookie* cookie = (file_cookie*)_cookie; 902 FIFOInode* fifo = (FIFOInode*)vnode->private_node; 903 904 fifo->Close(cookie); 905 906 return B_OK; 907 } 908 909 910 static status_t 911 fifo_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie) 912 { 913 file_cookie* cookie = (file_cookie*)_cookie; 914 915 TRACE("fifo_freecookie: entry vnode %p, cookie %p\n", _node, _cookie); 916 917 free(cookie); 918 919 return B_OK; 920 } 921 922 923 static status_t 924 fifo_fsync(fs_volume* _volume, fs_vnode* _node) 925 { 926 return B_BAD_VALUE; 927 } 928 929 930 static status_t 931 fifo_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, 932 off_t /*pos*/, void* buffer, size_t* _length) 933 { 934 file_cookie* cookie = (file_cookie*)_cookie; 935 Inode* inode = (Inode*)_node->private_node; 936 937 TRACE("fifo_read(vnode = %p, cookie = %p, length = %lu, mode = %d)\n", 938 inode, cookie, *_length, cookie->open_mode); 939 940 MutexLocker locker(inode->RequestLock()); 941 942 if (inode->IsActive() && inode->WriterCount() == 0) { 943 // as long there is no writer, and the pipe is empty, 944 // we always just return 0 to indicate end of file 945 if (inode->BytesAvailable() == 0) { 946 *_length = 0; 947 return B_OK; 948 } 949 } 950 951 // issue read request 952 953 ReadRequest request(cookie); 954 inode->AddReadRequest(request); 955 956 TRACE(" issue read request %p\n", &request); 957 958 size_t length = *_length; 959 status_t status = inode->ReadDataFromBuffer(buffer, &length, 960 (cookie->open_mode & O_NONBLOCK) != 0, is_called_via_syscall(), 961 request); 962 963 inode->RemoveReadRequest(request); 964 inode->NotifyReadDone(); 965 966 TRACE(" done reading request %p, length %zu\n", &request, length); 967 968 if (length > 0) 969 status = B_OK; 970 971 *_length = length; 972 return status; 973 } 974 975 976 static status_t 977 fifo_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, 978 off_t /*pos*/, const void* buffer, size_t* _length) 979 { 980 file_cookie* cookie = (file_cookie*)_cookie; 981 Inode* inode = (Inode*)_node->private_node; 982 983 TRACE("fifo_write(vnode = %p, cookie = %p, length = %lu)\n", 984 _node, cookie, *_length); 985 986 MutexLocker locker(inode->RequestLock()); 987 988 size_t length = *_length; 989 if (length == 0) 990 return B_OK; 991 992 // copy data into ring buffer 993 status_t status = inode->WriteDataToBuffer(buffer, &length, 994 (cookie->open_mode & O_NONBLOCK) != 0, is_called_via_syscall()); 995 996 if (length > 0) 997 status = B_OK; 998 999 *_length = length; 1000 return status; 1001 } 1002 1003 1004 static status_t 1005 fifo_read_stat(fs_volume* volume, fs_vnode* vnode, struct ::stat* st) 1006 { 1007 FIFOInode* fifo = (FIFOInode*)vnode->private_node; 1008 fs_vnode* superVnode = fifo->SuperVnode(); 1009 1010 if (superVnode->ops->read_stat == NULL) 1011 return B_BAD_VALUE; 1012 1013 status_t error = superVnode->ops->read_stat(volume, superVnode, st); 1014 if (error != B_OK) 1015 return error; 1016 1017 1018 MutexLocker locker(fifo->RequestLock()); 1019 1020 st->st_size = fifo->BytesAvailable(); 1021 1022 st->st_blksize = 4096; 1023 1024 // TODO: Just pass the changes to our modification time on to the super node. 1025 st->st_atim.tv_sec = time(NULL); 1026 st->st_atim.tv_nsec = 0; 1027 st->st_mtim = st->st_ctim = fifo->ModificationTime(); 1028 1029 return B_OK; 1030 } 1031 1032 1033 static status_t 1034 fifo_write_stat(fs_volume* volume, fs_vnode* vnode, const struct ::stat* st, 1035 uint32 statMask) 1036 { 1037 // we cannot change the size of anything 1038 if ((statMask & B_STAT_SIZE) != 0) 1039 return B_BAD_VALUE; 1040 1041 FIFOInode* fifo = (FIFOInode*)vnode->private_node; 1042 fs_vnode* superVnode = fifo->SuperVnode(); 1043 1044 if (superVnode->ops->write_stat == NULL) 1045 return B_BAD_VALUE; 1046 1047 status_t error = superVnode->ops->write_stat(volume, superVnode, st, 1048 statMask); 1049 if (error != B_OK) 1050 return error; 1051 1052 return B_OK; 1053 } 1054 1055 1056 static status_t 1057 fifo_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 op, 1058 void* buffer, size_t length) 1059 { 1060 file_cookie* cookie = (file_cookie*)_cookie; 1061 Inode* inode = (Inode*)_node->private_node; 1062 1063 TRACE("fifo_ioctl: vnode %p, cookie %p, op %" B_PRId32 ", buf %p, len %ld\n", 1064 _node, _cookie, op, buffer, length); 1065 1066 switch (op) { 1067 case FIONBIO: 1068 { 1069 if (buffer == NULL) 1070 return B_BAD_VALUE; 1071 1072 int value; 1073 if (is_called_via_syscall()) { 1074 if (!IS_USER_ADDRESS(buffer) 1075 || user_memcpy(&value, buffer, sizeof(int)) != B_OK) { 1076 return B_BAD_ADDRESS; 1077 } 1078 } else 1079 value = *(int*)buffer; 1080 1081 MutexLocker locker(inode->RequestLock()); 1082 cookie->SetNonBlocking(value != 0); 1083 return B_OK; 1084 } 1085 1086 case FIONREAD: 1087 { 1088 if (buffer == NULL) 1089 return B_BAD_VALUE; 1090 1091 MutexLocker locker(inode->RequestLock()); 1092 int available = (int)inode->BytesAvailable(); 1093 locker.Unlock(); 1094 1095 if (is_called_via_syscall()) { 1096 if (!IS_USER_ADDRESS(buffer) 1097 || user_memcpy(buffer, &available, sizeof(available)) 1098 != B_OK) { 1099 return B_BAD_ADDRESS; 1100 } 1101 } else 1102 *(int*)buffer = available; 1103 1104 return B_OK; 1105 } 1106 1107 case B_SET_BLOCKING_IO: 1108 case B_SET_NONBLOCKING_IO: 1109 { 1110 MutexLocker locker(inode->RequestLock()); 1111 cookie->SetNonBlocking(op == B_SET_NONBLOCKING_IO); 1112 return B_OK; 1113 } 1114 } 1115 1116 return EINVAL; 1117 } 1118 1119 1120 static status_t 1121 fifo_set_flags(fs_volume* _volume, fs_vnode* _node, void* _cookie, 1122 int flags) 1123 { 1124 Inode* inode = (Inode*)_node->private_node; 1125 file_cookie* cookie = (file_cookie*)_cookie; 1126 1127 TRACE("fifo_set_flags(vnode = %p, flags = %x)\n", _node, flags); 1128 1129 MutexLocker locker(inode->RequestLock()); 1130 cookie->open_mode = (cookie->open_mode & ~(O_APPEND | O_NONBLOCK)) | flags; 1131 return B_OK; 1132 } 1133 1134 1135 static status_t 1136 fifo_select(fs_volume* _volume, fs_vnode* _node, void* _cookie, 1137 uint8 event, selectsync* sync) 1138 { 1139 file_cookie* cookie = (file_cookie*)_cookie; 1140 1141 TRACE("fifo_select(vnode = %p)\n", _node); 1142 Inode* inode = (Inode*)_node->private_node; 1143 if (!inode) 1144 return B_ERROR; 1145 1146 MutexLocker locker(inode->RequestLock()); 1147 return inode->Select(event, sync, cookie->open_mode); 1148 } 1149 1150 1151 static status_t 1152 fifo_deselect(fs_volume* _volume, fs_vnode* _node, void* _cookie, 1153 uint8 event, selectsync* sync) 1154 { 1155 file_cookie* cookie = (file_cookie*)_cookie; 1156 1157 TRACE("fifo_deselect(vnode = %p)\n", _node); 1158 Inode* inode = (Inode*)_node->private_node; 1159 if (inode == NULL) 1160 return B_ERROR; 1161 1162 MutexLocker locker(inode->RequestLock()); 1163 return inode->Deselect(event, sync, cookie->open_mode); 1164 } 1165 1166 1167 static bool 1168 fifo_can_page(fs_volume* _volume, fs_vnode* _node, void* cookie) 1169 { 1170 return false; 1171 } 1172 1173 1174 static status_t 1175 fifo_read_pages(fs_volume* _volume, fs_vnode* _node, void* cookie, off_t pos, 1176 const iovec* vecs, size_t count, size_t* _numBytes) 1177 { 1178 return B_NOT_ALLOWED; 1179 } 1180 1181 1182 static status_t 1183 fifo_write_pages(fs_volume* _volume, fs_vnode* _node, void* cookie, 1184 off_t pos, const iovec* vecs, size_t count, size_t* _numBytes) 1185 { 1186 return B_NOT_ALLOWED; 1187 } 1188 1189 1190 static status_t 1191 fifo_get_super_vnode(fs_volume* volume, fs_vnode* vnode, fs_volume* superVolume, 1192 fs_vnode* _superVnode) 1193 { 1194 FIFOInode* fifo = (FIFOInode*)vnode->private_node; 1195 fs_vnode* superVnode = fifo->SuperVnode(); 1196 1197 if (superVnode->ops->get_super_vnode != NULL) { 1198 return superVnode->ops->get_super_vnode(volume, superVnode, superVolume, 1199 _superVnode); 1200 } 1201 1202 *_superVnode = *superVnode; 1203 1204 return B_OK; 1205 } 1206 1207 1208 static fs_vnode_ops sFIFOVnodeOps = { 1209 NULL, // lookup 1210 NULL, // get_vnode_name 1211 // TODO: This is suboptimal! We'd need to forward the 1212 // super node's hook, if it has got one. 1213 1214 &fifo_put_vnode, 1215 &fifo_remove_vnode, 1216 1217 &fifo_can_page, 1218 &fifo_read_pages, 1219 &fifo_write_pages, 1220 1221 NULL, // io() 1222 NULL, // cancel_io() 1223 1224 NULL, // get_file_map 1225 1226 /* common */ 1227 &fifo_ioctl, 1228 &fifo_set_flags, 1229 &fifo_select, 1230 &fifo_deselect, 1231 &fifo_fsync, 1232 1233 NULL, // fs_read_link 1234 NULL, // fs_symlink 1235 NULL, // fs_link 1236 NULL, // unlink 1237 NULL, // rename 1238 1239 NULL, // fs_access() 1240 &fifo_read_stat, 1241 &fifo_write_stat, 1242 NULL, 1243 1244 /* file */ 1245 NULL, // create() 1246 &fifo_open, 1247 &fifo_close, 1248 &fifo_free_cookie, 1249 &fifo_read, 1250 &fifo_write, 1251 1252 /* directory */ 1253 NULL, // create_dir 1254 NULL, // remove_dir 1255 NULL, // open_dir 1256 NULL, // close_dir 1257 NULL, // free_dir_cookie 1258 NULL, // read_dir 1259 NULL, // rewind_dir 1260 1261 /* attribute directory operations */ 1262 NULL, // open_attr_dir 1263 NULL, // close_attr_dir 1264 NULL, // free_attr_dir_cookie 1265 NULL, // read_attr_dir 1266 NULL, // rewind_attr_dir 1267 1268 /* attribute operations */ 1269 NULL, // create_attr 1270 NULL, // open_attr 1271 NULL, // close_attr 1272 NULL, // free_attr_cookie 1273 NULL, // read_attr 1274 NULL, // write_attr 1275 1276 NULL, // read_attr_stat 1277 NULL, // write_attr_stat 1278 NULL, // rename_attr 1279 NULL, // remove_attr 1280 1281 /* support for node and FS layers */ 1282 NULL, // create_special_node 1283 &fifo_get_super_vnode, 1284 }; 1285 1286 1287 } // namespace fifo 1288 1289 1290 using namespace fifo; 1291 1292 1293 // #pragma mark - 1294 1295 1296 status_t 1297 create_fifo_vnode(fs_volume* superVolume, fs_vnode* vnode) 1298 { 1299 FIFOInode* fifo = new(std::nothrow) FIFOInode(vnode); 1300 if (fifo == NULL) 1301 return B_NO_MEMORY; 1302 1303 status_t status = fifo->InitCheck(); 1304 if (status != B_OK) { 1305 delete fifo; 1306 return status; 1307 } 1308 1309 vnode->private_node = fifo; 1310 vnode->ops = &sFIFOVnodeOps; 1311 1312 return B_OK; 1313 } 1314 1315 1316 void 1317 fifo_init() 1318 { 1319 add_debugger_command_etc("fifo", &Inode::Dump, 1320 "Print info about the specified FIFO node", 1321 "[ \"-d\" ] <address>\n" 1322 "Prints information about the FIFO node specified by address\n" 1323 "<address>. If \"-d\" is given, the data in the FIFO's ring buffer\n" 1324 "hexdumped as well.\n", 1325 0); 1326 } 1327