Retry removal of busy minors
authorDaniel Verite <daniel@verite.pro>
Sat, 9 Jun 2012 02:16:11 +0000 (04:16 +0200)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Mon, 11 Jun 2012 17:50:20 +0000 (10:50 -0700)
When failing to remove a zvol device link because it's busy, wait
a bit and retry in a loop instead of giving up immediately.  This
technique is similar to the loop in zpool_label_disk_wait(), with
the same goal: waiting for the asynchronous udev processes to finish
their work.

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #692

lib/libzfs/libzfs_dataset.c

index e8c329c..661f9b5 100644 (file)
@@ -3920,10 +3920,29 @@ int
 zvol_remove_link(libzfs_handle_t *hdl, const char *dataset)
 {
        zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+       int timeout = 3000; /* in milliseconds */
+       int error = 0;
+       int i;
 
        (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
 
-       if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) {
+       /*
+        * Due to concurrent updates by udev the device may be reported as
+        * busy.  In this case don't immediately fail.  Instead briefly delay
+        * and retry the ioctl() which is now likely to succeed.  If unable
+        * remove the link after timeout milliseconds return the failure.
+        */
+       for (i = 0; i < timeout; i++) {
+               error = ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
+               if (error && errno == EBUSY) {
+                       usleep(1000);
+                       continue;
+               } else {
+                       break;
+               }
+       }
+
+       if (error) {
                switch (errno) {
                case ENXIO:
                        /*