inode: fix the pipe deadlock
authorAlan Cox <alan@linux.intel.com>
Tue, 12 Mar 2019 22:48:06 +0000 (22:48 +0000)
committerAlan Cox <alan@linux.intel.com>
Tue, 12 Mar 2019 22:48:06 +0000 (22:48 +0000)
I think this will do the trick. It could do with more review and testing.

Kernel/inode.c

index 17342e4..bb25d2c 100644 (file)
@@ -14,7 +14,7 @@
    need to integrate this into the I/O loop, but when we do it changes
    how we handle the psleep_flags bit. Pipes wrap before 64k so we can
    shorten the check */
-static uint8_t pipewait(inoptr ino, uint8_t flag)
+static uint8_t wait_pipe_read(inoptr ino, uint8_t flag)
 {
         while((uint16_t)ino->c_node.i_size == 0) {
                 if (ino->c_writers == 0 || psleep_flags(ino, flag)) {
@@ -26,6 +26,23 @@ static uint8_t pipewait(inoptr ino, uint8_t flag)
         return 1;
 }
 
+/* Wait for our pipe to become writable. We must have enough space and
+   a reader */
+static uint8_t wait_pipe_write(inoptr ino, uint8_t flag)
+{
+       while (LOWORD(ino->c_node.i_size) > 8 * BLKSIZE) {
+               if (ino->c_readers == 0) {      /* No readers */
+                       udata.u_done = (usize_t)-1;
+                       udata.u_error = EPIPE;
+                       ssig(udata.u_ptab, SIGPIPE);
+                       return 1;
+               }
+               /* Sleep if pipe full */
+               if (psleep_flags(ino, flag))
+                       return 1;
+       }
+       return 0;
+}
 /* We need this because SDCC otherwise makes a right hash of it */
 static uint16_t uoff(void)
 {
@@ -81,7 +98,7 @@ void readi(regptr inoptr ino, uint8_t flag)
        case MODE_R(F_PIPE):
                ispipe = true;
                /* This bit really needs to be inside the loop for pipe cases */
-               if (!pipewait(ino, flag))
+               if (!wait_pipe_read(ino, flag))
                        break;
                goto loop;
 
@@ -149,8 +166,6 @@ void readi(regptr inoptr ino, uint8_t flag)
        }
 }
 
-
-
 void writei(regptr inoptr ino, uint8_t flag)
 {
        usize_t amount;
@@ -175,21 +190,6 @@ void writei(regptr inoptr ino, uint8_t flag)
 #endif
        case MODE_R(F_PIPE):
                ispipe = true;
-               /* FIXME: this will hang if you ever write > 16 * BLKSIZE
-                  in one go - needs merging into the loop */
-               while (udata.u_count > (16 * BLKSIZE) -
-                                       LOWORD(ino->c_node.i_size)) {
-                       if (ino->c_readers == 0) {      /* No readers */
-                               udata.u_done = (usize_t)-1;
-                               udata.u_error = EPIPE;
-                               ssig(udata.u_ptab, SIGPIPE);
-                               return;
-                       }
-                       /* Sleep if empty pipe */
-                       if (psleep_flags(ino, flag))
-                               return;
-               }
-               /* Sleep if empty pipe */
 
        case MODE_R(F_DIR):
        case MODE_R(F_REG):
@@ -197,6 +197,11 @@ void writei(regptr inoptr ino, uint8_t flag)
                flag = flag & O_SYNC ? 2 : 1;
 
                while (udata.u_count) {
+                       if (ispipe) {
+                               /* Sleep if empty pipe */
+                               if (wait_pipe_write(ino, flag))
+                                       return;
+                       }
                        pblk = mapcalc(ino, &amount, 0);
 
                         if (HIBYTE32(udata.u_offset) & BLKOVERSIZE32) {