vrevoke/single-user - fix more revoke issues.
authorMatthew Dillon <dillon@apollo.backplane.com>
Sat, 11 Jul 2009 22:56:25 +0000 (15:56 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sat, 11 Jul 2009 22:56:25 +0000 (15:56 -0700)
* vrevoke() no longer clears vp->v_rdev.  Doing so interferes with specfs's
  ability to call dev_dclose().  Fixing this will allow the regular close
  of the vnode to close the underlying device.

  Since the revoke code detaches the vnode from any user-visible file
  descriptors the vnode will end up being closed the moment the last
  referenced to the detached fp goes away.

* kill 1 no longer leaves the syscons in a state where both /dev/ttyv0 and
  /dev/console are marked open (due to the above bug).  This state would
  cause the keyboard to stop working sometimes when dropping into single
  user.

* Add dev_drevoke() and friends.  This allows a device to do something
  when an attempt is made to revoke() the related vnode.

  TTY and PTY devices now attempt to break out of any ttysleep() operation
  (such as when processes are blocked in read()).  This is advisory and
  if it fails to break a process out of a blocked condition that process
  will still detach after the blocked condition is resolved normally.

20 files changed:
sys/dev/misc/dcons/dcons_os.c
sys/dev/misc/nmdm/nmdm.c
sys/dev/misc/syscons/syscons.c
sys/dev/misc/syscons/sysmouse.c
sys/dev/serial/cy/cy.c
sys/dev/serial/dgb/dgm.c
sys/dev/serial/digi/digi.c
sys/dev/serial/rc/rc.c
sys/dev/serial/rp/rp.c
sys/dev/serial/si/si.c
sys/dev/serial/sio/sio.c
sys/dev/serial/stl/stallion.c
sys/dev/serial/stli/istallion.c
sys/dev/usbmisc/ucom/ucom.c
sys/kern/kern_device.c
sys/kern/tty.c
sys/kern/tty_pty.c
sys/kern/vfs_subr.c
sys/sys/device.h
sys/sys/tty.h

index a8edd34..4232d3d 100644 (file)
@@ -101,6 +101,7 @@ static struct dev_ops dcons_ops = {
        .d_write =      ttywrite,
        .d_ioctl =      dcons_ioctl,
        .d_poll =       ttypoll,
+       .d_revoke =     ttyrevoke
 };
 
 #ifndef KLD_MODULE
index be1546d..fd151da 100644 (file)
@@ -77,6 +77,7 @@ static struct dev_ops nmdm_ops = {
        .d_write =      nmdmwrite,
        .d_ioctl =      nmdmioctl,
        .d_poll =       ttypoll,
+       .d_revoke =     ttyrevoke
 };
 
 #define BUFSIZ 100             /* Chunk size iomoved to/from user */
index 59ec308..335d10e 100644 (file)
@@ -209,7 +209,8 @@ static struct dev_ops sc_ops = {
        .d_ioctl =      scioctl,
        .d_poll =       ttypoll,
        .d_mmap =       scmmap,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 int
index 86e1e40..64e2f12 100644 (file)
@@ -59,6 +59,7 @@ static struct dev_ops sm_ops = {
        .d_read =       ttyread,
        .d_ioctl =      smioctl,
        .d_poll =       ttypoll,
+       .d_revoke =     ttyrevoke
 };
 
 /* local variables */
index 0c5fe03..403b7de 100644 (file)
@@ -391,7 +391,8 @@ static struct dev_ops sio_ops = {
        .d_write =      siowrite,
        .d_ioctl =      sioioctl,
        .d_poll =       ttypoll,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 static int     comconsole = -1;
index 6e8ba89..76dcedd 100644 (file)
@@ -249,7 +249,8 @@ static struct dev_ops dgm_ops = {
        .d_write =      ttywrite,
        .d_ioctl =      dgmioctl,
        .d_poll =       ttypoll,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 static int
index 1da319d..998ac56 100644 (file)
@@ -154,7 +154,8 @@ static struct dev_ops digi_ops = {
        .d_write =      digiwrite,
        .d_ioctl =      digiioctl,
        .d_poll =       ttypoll,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 static void
index 4e8623a..6465c56 100644 (file)
@@ -99,7 +99,8 @@ static struct dev_ops rc_ops = {
        .d_write =      ttywrite,
        .d_ioctl =      rcioctl,
        .d_poll =       ttypoll,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 /* Per-board structure */
index 238bd91..47fc0b4 100644 (file)
@@ -579,6 +579,7 @@ struct dev_ops rp_ops = {
        .d_write =      rpwrite,
        .d_ioctl =      rpioctl,
        .d_poll =       ttypoll,
+       .d_revoke =     ttyrevoke
 };
 
 static int     rp_num_ports_open = 0;
index 3a19c35..564a56f 100644 (file)
@@ -127,7 +127,8 @@ static struct dev_ops si_ops = {
        .d_write =      siwrite,
        .d_ioctl =      siioctl,
        .d_poll =       ttypoll,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 static int si_Nports;
index 57ed9ea..f394f6a 100644 (file)
@@ -257,7 +257,8 @@ static struct dev_ops sio_ops = {
        .d_write =      siowrite,
        .d_ioctl =      sioioctl,
        .d_poll =       ttypoll,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 int    comconsole = -1;
index 58e212b..7c63792 100644 (file)
@@ -757,7 +757,8 @@ static struct dev_ops stl_ops = {
        .d_write =      ttywrite,
        .d_ioctl =      stlioctl,
        .d_poll =       ttypoll,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 static void stl_drvinit(void *unused)
index 577a1ae..660dd0f 100644 (file)
@@ -640,7 +640,8 @@ static struct dev_ops stli_ops = {
        .d_write =      stliwrite,
        .d_ioctl =      stliioctl,
        .d_poll =       ttypoll,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 #endif
index cbc7ed3..ec48f45 100644 (file)
@@ -128,7 +128,8 @@ static struct dev_ops ucom_ops = {
        .d_write =      ucomwrite,
        .d_ioctl =      ucomioctl,
        .d_poll =       ttypoll,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 static void ucom_cleanup(struct ucom_softc *);
index 38ce9bf..b1004e6 100644 (file)
@@ -71,6 +71,7 @@ DEVOP_DESC_INIT(poll);
 DEVOP_DESC_INIT(mmap);
 DEVOP_DESC_INIT(strategy);
 DEVOP_DESC_INIT(kqfilter);
+DEVOP_DESC_INIT(revoke);
 DEVOP_DESC_INIT(clone);
 
 /*
@@ -92,6 +93,7 @@ struct dev_ops default_dev_ops = {
        .d_dump = nodump,
        .d_psize = nopsize,
        .d_kqfilter = nokqfilter,
+       .d_revoke = norevoke,
        .d_clone = noclone
 };
     
@@ -210,6 +212,16 @@ dev_dclone(cdev_t dev)
        return (dev->si_ops->d_clone(&ap));
 }
 
+int
+dev_drevoke(cdev_t dev)
+{
+       struct dev_revoke_args ap;
+
+       ap.a_head.a_desc = &dev_revoke_desc;
+       ap.a_head.a_dev = dev;
+       return (dev->si_ops->d_revoke(&ap));
+}
+
 /*
  * Core device strategy call, used to issue I/O on a device.  There are
  * two versions, a non-chained version and a chained version.  The chained
@@ -694,6 +706,12 @@ dev_ops_restore(cdev_t dev, struct dev_ops *oops)
  * Unsupported devswitch functions (e.g. for writing to read-only device).
  * XXX may belong elsewhere.
  */
+int
+norevoke(struct dev_revoke_args *ap)
+{
+       /* take no action */
+       return(0);
+}
 
 int
 noclone(struct dev_clone_args *ap)
index 603807f..9e5c201 100644 (file)
@@ -2576,6 +2576,28 @@ ttysleep(struct tty *tp, void *chan, int slpflags, char *wmesg, int timo)
 }
 
 /*
+ * Revoke a tty.
+ *
+ * We bump the gen to force any ttysleep()'s to return with ERESTART
+ * and flush the tty.  The related fp's should already have been
+ * replaced so the tty will close when the last references on the
+ * original fp's go away.
+ */
+int
+ttyrevoke(struct dev_revoke_args *ap)
+{
+       struct tty *tp;
+
+       tp = ap->a_head.a_dev->si_tty;
+       tp->t_gen++;
+       ttyflush(tp, FREAD | FWRITE);
+       wakeup(TSA_CARR_ON(tp));
+       ttwakeup(tp);
+       ttwwakeup(tp);
+       return (0);
+}
+
+/*
  * Allocate a tty struct.  Clists in the struct will be allocated by
  * ttyopen().
  */
index 84c61cb..6e5e086 100644 (file)
@@ -87,7 +87,8 @@ static struct dev_ops pts_ops = {
        .d_write =      ptswrite,
        .d_ioctl =      ptyioctl,
        .d_poll =       ttypoll,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 #define        CDEV_MAJOR_C    6
@@ -99,7 +100,8 @@ static struct dev_ops ptc_ops = {
        .d_write =      ptcwrite,
        .d_ioctl =      ptyioctl,
        .d_poll =       ptcpoll,
-       .d_kqfilter =   ttykqfilter
+       .d_kqfilter =   ttykqfilter,
+       .d_revoke =     ttyrevoke
 };
 
 #define BUFSIZ 100             /* Chunk size iomoved to/from user */
index 18d9821..b110f7a 100644 (file)
@@ -1207,6 +1207,7 @@ int
 vrevoke(struct vnode *vp, struct ucred *cred)
 {
        struct vnode *vq;
+       struct vnode *vqn;
        lwkt_tokref ilock;
        cdev_t dev;
        int error;
@@ -1218,6 +1219,11 @@ vrevoke(struct vnode *vp, struct ucred *cred)
         *
         * The passed vp will probably show up in the list, do not VX lock
         * it twice!
+        *
+        * Releasing the vnode's rdev here can mess up specfs's call to
+        * device close, so don't do it.  The vnode has been disassociated
+        * and the device will be closed after the last ref on the related
+        * fp goes away (if not still open by e.g. the kernel).
         */
        if (vp->v_type != VCHR) {
                error = fdrevoke(vp, DTYPE_VNODE, cred);
@@ -1229,13 +1235,20 @@ vrevoke(struct vnode *vp, struct ucred *cred)
        }
        reference_dev(dev);
        lwkt_gettoken(&ilock, &spechash_token);
-       while ((vq = SLIST_FIRST(&dev->si_hlist)) != NULL) {
-               vref(vq);
+
+       vqn = SLIST_FIRST(&dev->si_hlist);
+       if (vqn)
+               vref(vqn);
+       while ((vq = vqn) != NULL) {
+               vqn = SLIST_NEXT(vqn, v_cdevnext);
+               if (vqn)
+                       vref(vqn);
                fdrevoke(vq, DTYPE_VNODE, cred);
-               v_release_rdev(vq);
+               /*v_release_rdev(vq);*/
                vrele(vq);
        }
        lwkt_reltoken(&ilock);
+       dev_drevoke(dev);
        release_dev(dev);
        return (0);
 }
index c5eb2eb..8b0633b 100644 (file)
@@ -161,11 +161,21 @@ struct dev_kqfilter_args {
        int             a_result;
 };
 
+/*
+ * int d_clone(cdev_t dev);
+ */
 struct dev_clone_args {
        struct dev_generic_args a_head;
 };
 
 /*
+ * int d_revoke(cdev_t dev)
+ */
+struct dev_revoke_args {
+       struct dev_generic_args a_head;
+};
+
+/*
  * Typedefs to help drivers declare the driver routines and such
  */
 typedef int d_default_t (struct dev_generic_args *ap);
@@ -181,6 +191,7 @@ typedef int d_dump_t (struct dev_dump_args *ap);
 typedef int d_psize_t (struct dev_psize_args *ap);
 typedef int d_kqfilter_t (struct dev_kqfilter_args *ap);
 typedef int d_clone_t (struct dev_clone_args *ap);
+typedef int d_revoke_t (struct dev_revoke_args *ap);
 
 /*
  * Character device switch table.
@@ -210,7 +221,8 @@ struct dev_ops {
        d_psize_t       *d_psize;
        d_kqfilter_t    *d_kqfilter;
        d_clone_t       *d_clone;       /* clone from base dev_ops */
-#define dev_ops_last_field     d_clone
+       d_revoke_t      *d_revoke;
+#define dev_ops_last_field     d_revoke
 };
 
 /*
@@ -291,6 +303,7 @@ int dev_dpoll(cdev_t dev, int events);
 int dev_dkqfilter(cdev_t dev, struct knote *kn);
 int dev_dmmap(cdev_t dev, vm_offset_t offset, int nprot);
 int dev_dclone(cdev_t dev);
+int dev_drevoke(cdev_t dev);
 
 int dev_drefs(cdev_t dev);
 const char *dev_dname(cdev_t dev);
@@ -312,6 +325,7 @@ d_dump_t    nodump;
 d_psize_t      nopsize;
 d_kqfilter_t   nokqfilter;
 d_clone_t      noclone;
+d_revoke_t     norevoke;
 
 d_open_t       nullopen;
 d_close_t      nullclose;
index cdbf794..52ef3b0 100644 (file)
@@ -287,6 +287,7 @@ int  ttyread (struct dev_read_args *);
 void    ttyregister (struct tty *tp);
 int     ttysleep (struct tty *tp, void *chan, int slpflags, char *wmesg,
            int timeout);
+int     ttyrevoke (struct dev_revoke_args *);
 int     ttywait (struct tty *tp);
 int     ttywrite (struct dev_write_args *);