mtd: powernv_flash: Use opal_async_wait_response_interruptible()
authorCyril Bur <cyrilbur@gmail.com>
Fri, 3 Nov 2017 02:41:46 +0000 (13:41 +1100)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 6 Nov 2017 09:39:31 +0000 (20:39 +1100)
The OPAL calls performed in this driver shouldn't be using
opal_async_wait_response() as this performs a wait_event() which, on
long running OPAL calls could result in hung task warnings. wait_event()
prevents timely signal delivery which is also undesirable.

This patch also attempts to quieten down the use of dev_err() when
errors haven't actually occurred and also to return better information up
the stack rather than always -EIO.

Signed-off-by: Cyril Bur <cyrilbur@gmail.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
drivers/mtd/devices/powernv_flash.c

index 3343d4f..26f9fea 100644 (file)
@@ -89,33 +89,46 @@ static int powernv_flash_async_op(struct mtd_info *mtd, enum flash_op op,
                return -EIO;
        }
 
-       if (rc == OPAL_SUCCESS)
-               goto out_success;
+       if (rc == OPAL_ASYNC_COMPLETION) {
+               rc = opal_async_wait_response_interruptible(token, &msg);
+               if (rc) {
+                       /*
+                        * If we return the mtd core will free the
+                        * buffer we've just passed to OPAL but OPAL
+                        * will continue to read or write from that
+                        * memory.
+                        * It may be tempting to ultimately return 0
+                        * if we're doing a read or a write since we
+                        * are going to end up waiting until OPAL is
+                        * done. However, because the MTD core sends
+                        * us the userspace request in chunks, we need
+                        * it to know we've been interrupted.
+                        */
+                       rc = -EINTR;
+                       if (opal_async_wait_response(token, &msg))
+                               dev_err(dev, "opal_async_wait_response() failed\n");
+                       goto out;
+               }
+               rc = opal_get_async_rc(msg);
+       }
 
-       if (rc != OPAL_ASYNC_COMPLETION) {
+       /*
+        * OPAL does mutual exclusion on the flash, it will return
+        * OPAL_BUSY.
+        * During firmware updates by the service processor OPAL may
+        * be (temporarily) prevented from accessing the flash, in
+        * this case OPAL will also return OPAL_BUSY.
+        * Both cases aren't errors exactly but the flash could have
+        * changed, userspace should be informed.
+        */
+       if (rc != OPAL_SUCCESS && rc != OPAL_BUSY)
                dev_err(dev, "opal_flash_async_op(op=%d) failed (rc %d)\n",
                                op, rc);
-               rc = -EIO;
-               goto out;
-       }
 
-       rc = opal_async_wait_response(token, &msg);
-       if (rc) {
-               dev_err(dev, "opal async wait failed (rc %d)\n", rc);
-               rc = -EIO;
-               goto out;
-       }
-
-       rc = opal_get_async_rc(msg);
-out_success:
-       if (rc == OPAL_SUCCESS) {
-               rc = 0;
-               if (retlen)
-                       *retlen = len;
-       } else {
-               rc = -EIO;
-       }
+       if (rc == OPAL_SUCCESS && retlen)
+               *retlen = len;
 
+       rc = opal_error_code(rc);
 out:
        opal_async_release_token(token);
        return rc;