devfs - Let devfs assume degenerate knotes when a device goes away
authorSamuel J. Greear <sjg@thesjg.com>
Sat, 14 Aug 2010 14:41:35 +0000 (14:41 +0000)
committerSamuel J. Greear <sjg@thesjg.com>
Sat, 11 Sep 2010 03:30:58 +0000 (03:30 +0000)
* Some devices can disappear while knotes are still active on the klist
  embedded in the devices softc or etc., create a mechanism to allow devfs
  to assume these degenerate knotes.

* Convert ums to this new way of things

* This does not actually fix the reported panic (where X is holding a mouse
  open across detach), panic is moved into the new devfs handler.

* Other devices which could be detached and support the kq interface need to
  use this interface as well (umass, ...)

Reported-by: Rumko
sys/dev/usbmisc/ums/ums.c
sys/sys/conf.h
sys/sys/devfs.h
sys/vfs/devfs/devfs_core.c

index ee2f775..ace91b2 100644 (file)
@@ -56,6 +56,7 @@
 #include <sys/vnode.h>
 #include <sys/event.h>
 #include <sys/sysctl.h>
+#include <sys/devfs.h>
 #include <sys/thread2.h>
 
 #include <bus/usb/usb.h>
@@ -88,6 +89,7 @@ SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW,
 
 struct ums_softc {
        device_t sc_dev;                /* base device */
+       cdev_t   sc_cdev;
        usbd_interface_handle sc_iface; /* interface */
        usbd_pipe_handle sc_intrpipe;   /* interrupt pipe */
        int sc_ep_addr;
@@ -341,9 +343,10 @@ ums_attach(device_t self)
        sc->status.button = sc->status.obutton = 0;
        sc->status.dx = sc->status.dy = sc->status.dz = 0;
 
-       make_dev(&ums_ops, device_get_unit(self),
-                UID_ROOT, GID_OPERATOR,
-                0644, "ums%d", device_get_unit(self));
+       sc->sc_cdev = make_dev(&ums_ops, device_get_unit(self),
+                              UID_ROOT, GID_OPERATOR,
+                              0644, "ums%d", device_get_unit(self));
+       reference_dev(sc->sc_cdev);
 
        if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) {
                DPRINTF(("%s: Spurious button up events\n",
@@ -380,9 +383,11 @@ ums_detach(device_t self)
                sc->state &= ~UMS_ASLEEP;
                wakeup(sc);
        }
-       KNOTE(&sc->rkq.ki_note, 0);
 
        dev_ops_remove_minor(&ums_ops, /*-1, */device_get_unit(self));
+       devfs_assume_knotes(sc->sc_cdev, &sc->rkq);
+       release_dev(sc->sc_cdev);
+        sc->sc_cdev = NULL;
 
        return 0;
 }
index 2571221..8726ee3 100644 (file)
@@ -57,6 +57,9 @@
 #ifndef _SYS_SYSREF_H_
 #include <sys/sysref.h>
 #endif
+#ifndef _SYS_EVENT_H_
+#include <sys/event.h>
+#endif
 #include <libprop/proplib.h>
 
 #define SPECNAMELEN    63
@@ -102,6 +105,7 @@ struct cdev {
        time_t          si_lastwrite;   /* time_second */
        struct vm_object *si_object;    /* vm_pager support */
        prop_dictionary_t si_dict;
+       struct kqinfo   si_kqinfo;      /* degenerate delegated knotes */
 };
 
 #define SI_UNUSED01    0x0001
index 0bbefce..7acc794 100644 (file)
@@ -393,6 +393,7 @@ int devfs_destroy_dev_by_ops(struct dev_ops *, int);
 struct devfs_node *devfs_find_device_node_by_name(struct devfs_node *, char *);
 
 cdev_t devfs_new_cdev(struct dev_ops *, int, struct dev_ops *);
+void devfs_assume_knotes(cdev_t dev, struct kqinfo *kqi);
 
 cdev_t devfs_find_device_by_name(const char *, ...) __printflike(1, 2);
 cdev_t devfs_find_device_by_udev(udev_t);
index 1f00265..960294c 100644 (file)
@@ -2219,6 +2219,57 @@ devfs_cdev_unlock(cdev_t dev)
 {
 }
 
+static int
+devfs_detached_filter_eof(struct knote *kn, long hint)
+{
+       kn->kn_flags |= EV_EOF;
+       return (1);
+}
+
+static void
+devfs_detached_filter_detach(struct knote *kn)
+{
+       cdev_t dev = (cdev_t)kn->kn_hook;
+
+       knote_remove(&dev->si_kqinfo.ki_note, kn);
+}
+
+static struct filterops devfs_detached_filterops =
+       { FILTEROP_ISFD, NULL,
+         devfs_detached_filter_detach,
+         devfs_detached_filter_eof };
+
+/*
+ * Delegates knote filter handling responsibility to devfs
+ *
+ * Any device that implements kqfilter event handling and could be detached
+ * or shut down out from under the kevent subsystem must allow devfs to
+ * assume responsibility for any knotes it may hold.
+ */
+void
+devfs_assume_knotes(cdev_t dev, struct kqinfo *kqi)
+{
+       struct knote *kn;
+
+       lwkt_gettoken(&kq_token);
+
+       while (!SLIST_EMPTY(&kqi->ki_note)) {
+               kn = SLIST_FIRST(&kqi->ki_note);
+                knote_remove(&kqi->ki_note, kn);
+               kn->kn_fop = &devfs_detached_filterops;
+               kn->kn_hook = (caddr_t)dev;
+               knote_insert(&dev->si_kqinfo.ki_note, kn);
+        }
+
+       /*
+        * These should probably be activated individually, but doing so
+        * would require refactoring kq's public in-kernel interface.
+        */
+       KNOTE(&dev->si_kqinfo.ki_note, 0);
+
+       lwkt_reltoken(&kq_token);
+}
+
 /*
  * Links a given cdev into the dev list.
  */