SUBSYSTEM=="aoe", KERNEL=="err",       NAME="etherd/%k", GROUP="disk", MODE="0440"
 SUBSYSTEM=="aoe", KERNEL=="interfaces",        NAME="etherd/%k", GROUP="disk", MODE="0220"
 SUBSYSTEM=="aoe", KERNEL=="revalidate",        NAME="etherd/%k", GROUP="disk", MODE="0220"
+SUBSYSTEM=="aoe", KERNEL=="flush",     NAME="etherd/%k", GROUP="disk", MODE="0220"
 
 # aoe block devices     
 KERNEL=="etherd*",       NAME="%k", GROUP="disk"
 
        MINOR_DISCOVER,
        MINOR_INTERFACES,
        MINOR_REVALIDATE,
+       MINOR_FLUSH,
        MSGSZ = 2048,
        NMSG = 100,             /* message backlog to retain */
 };
        { MINOR_DISCOVER, "discover" },
        { MINOR_INTERFACES, "interfaces" },
        { MINOR_REVALIDATE, "revalidate" },
+       { MINOR_FLUSH, "flush" },
 };
 
 static int
                break;
        case MINOR_REVALIDATE:
                ret = revalidate(buf, cnt);
+               break;
+       case MINOR_FLUSH:
+               ret = aoedev_flush(buf, cnt);
        }
        if (ret == 0)
                ret = cnt;
 
 #include <linux/netdevice.h>
 #include "aoe.h"
 
+static void dummy_timer(ulong);
+static void aoedev_freedev(struct aoedev *);
+static void freetgt(struct aoetgt *t);
+
 static struct aoedev *devlist;
 static spinlock_t devlist_lock;
 
        d->flags &= ~DEVFL_UP;
 }
 
+static void
+aoedev_freedev(struct aoedev *d)
+{
+       struct aoetgt **t, **e;
+
+       if (d->gd) {
+               aoedisk_rm_sysfs(d);
+               del_gendisk(d->gd);
+               put_disk(d->gd);
+       }
+       t = d->targets;
+       e = t + NTARGETS;
+       for (; t < e && *t; t++)
+               freetgt(*t);
+       if (d->bufpool)
+               mempool_destroy(d->bufpool);
+       kfree(d);
+}
+
+int
+aoedev_flush(const char __user *str, size_t cnt)
+{
+       ulong flags;
+       struct aoedev *d, **dd;
+       struct aoedev *rmd = NULL;
+       char buf[16];
+       int all = 0;
+
+       if (cnt >= 3) {
+               if (cnt > sizeof buf)
+                       cnt = sizeof buf;
+               if (copy_from_user(buf, str, cnt))
+                       return -EFAULT;
+               all = !strncmp(buf, "all", 3);
+       }
+
+       flush_scheduled_work();
+       spin_lock_irqsave(&devlist_lock, flags);
+       dd = &devlist;
+       while ((d = *dd)) {
+               spin_lock(&d->lock);
+               if ((!all && (d->flags & DEVFL_UP))
+               || (d->flags & (DEVFL_GDALLOC|DEVFL_NEWSIZE))
+               || d->nopen) {
+                       spin_unlock(&d->lock);
+                       dd = &d->next;
+                       continue;
+               }
+               *dd = d->next;
+               aoedev_downdev(d);
+               d->flags |= DEVFL_TKILL;
+               spin_unlock(&d->lock);
+               d->next = rmd;
+               rmd = d;
+       }
+       spin_unlock_irqrestore(&devlist_lock, flags);
+       while ((d = rmd)) {
+               rmd = d->next;
+               del_timer_sync(&d->timer);
+               aoedev_freedev(d);      /* must be able to sleep */
+       }
+       return 0;
+}
+
 /* find it or malloc it */
 struct aoedev *
 aoedev_by_sysminor_m(ulong sysminor)
        kfree(t);
 }
 
-static void
-aoedev_freedev(struct aoedev *d)
-{
-       struct aoetgt **t, **e;
-
-       if (d->gd) {
-               aoedisk_rm_sysfs(d);
-               del_gendisk(d->gd);
-               put_disk(d->gd);
-       }
-       t = d->targets;
-       e = t + NTARGETS;
-       for (; t < e && *t; t++)
-               freetgt(*t);
-       if (d->bufpool)
-               mempool_destroy(d->bufpool);
-       kfree(d);
-}
-
 void
 aoedev_exit(void)
 {