Author: kib
Date: Sun Oct  6 05:50:55 2013
New Revision: 256074
URL: http://svnweb.freebsd.org/changeset/base/256074

Log:
  MFC r255798:
  Pre-acquire the filedesc sx when a possibility exists that the later
  code could need to remove a kqueue from the filedesc list.

Modified:
  stable/9/sys/kern/kern_event.c
Directory Properties:
  stable/9/sys/   (props changed)

Modified: stable/9/sys/kern/kern_event.c
==============================================================================
--- stable/9/sys/kern/kern_event.c      Sat Oct  5 23:11:01 2013        
(r256073)
+++ stable/9/sys/kern/kern_event.c      Sun Oct  6 05:50:55 2013        
(r256074)
@@ -965,12 +965,13 @@ kqueue_register(struct kqueue *kq, struc
        struct file *fp;
        struct knote *kn, *tkn;
        int error, filt, event;
-       int haskqglobal;
+       int haskqglobal, filedesc_unlock;
 
        fp = NULL;
        kn = NULL;
        error = 0;
        haskqglobal = 0;
+       filedesc_unlock = 0;
 
        filt = kev->filter;
        fops = kqueue_fo_find(filt);
@@ -1010,6 +1011,13 @@ findkn:
                                goto done;
                        }
 
+                       /*
+                        * Pre-lock the filedesc before the global
+                        * lock mutex, see the comment in
+                        * kqueue_close().
+                        */
+                       FILEDESC_XLOCK(td->td_proc->p_fd);
+                       filedesc_unlock = 1;
                        KQ_GLOBAL_LOCK(&kq_global, haskqglobal);
                }
 
@@ -1039,6 +1047,10 @@ findkn:
        /* knote is in the process of changing, wait for it to stablize. */
        if (kn != NULL && (kn->kn_status & KN_INFLUX) == KN_INFLUX) {
                KQ_GLOBAL_UNLOCK(&kq_global, haskqglobal);
+               if (filedesc_unlock) {
+                       FILEDESC_XUNLOCK(td->td_proc->p_fd);
+                       filedesc_unlock = 0;
+               }
                kq->kq_state |= KQ_FLUXWAIT;
                msleep(kq, &kq->kq_lock, PSOCK | PDROP, "kqflxwt", 0);
                if (fp != NULL) {
@@ -1155,6 +1167,8 @@ done_ev_add:
 
 done:
        KQ_GLOBAL_UNLOCK(&kq_global, haskqglobal);
+       if (filedesc_unlock)
+               FILEDESC_XUNLOCK(td->td_proc->p_fd);
        if (fp != NULL)
                fdrop(fp, td);
        if (tkn != NULL)
@@ -1642,10 +1656,12 @@ kqueue_close(struct file *fp, struct thr
        struct knote *kn;
        int i;
        int error;
+       int filedesc_unlock;
 
        if ((error = kqueue_acquire(fp, &kq)))
                return error;
 
+       filedesc_unlock = 0;
        KQ_LOCK(kq);
 
        KASSERT((kq->kq_state & KQ_CLOSING) != KQ_CLOSING,
@@ -1707,9 +1723,20 @@ kqueue_close(struct file *fp, struct thr
 
        KQ_UNLOCK(kq);
 
-       FILEDESC_XLOCK(fdp);
+       /*
+        * We could be called due to the knote_drop() doing fdrop(),
+        * called from kqueue_register().  In this case the global
+        * lock is owned, and filedesc sx is locked before, to not
+        * take the sleepable lock after non-sleepable.
+        */
+       if (!sx_xlocked(FILEDESC_LOCK(fdp))) {
+               FILEDESC_XLOCK(fdp);
+               filedesc_unlock = 1;
+       } else
+               filedesc_unlock = 0;
        TAILQ_REMOVE(&fdp->fd_kqlist, kq, kq_list);
-       FILEDESC_XUNLOCK(fdp);
+       if (filedesc_unlock)
+               FILEDESC_XUNLOCK(fdp);
 
        seldrain(&kq->kq_sel);
        knlist_destroy(&kq->kq_sel.si_note);
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-stable-9
To unsubscribe, send any mail to "[email protected]"

Reply via email to