Introduce `hotplug_mtx' mutex(9) and make `hotplugread_filtops' MP safe.
authormvs <mvs@openbsd.org>
Fri, 22 Sep 2023 22:12:32 +0000 (22:12 +0000)
committermvs <mvs@openbsd.org>
Fri, 22 Sep 2023 22:12:32 +0000 (22:12 +0000)
Use this mutex(9) to protect `evqueue_head', `evqueue_tail' and
`evqueue_count'.

ok bluhm

sys/dev/hotplug.c

index 1d33606..ae38f0f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: hotplug.c,v 1.23 2023/09/08 20:00:27 mvs Exp $        */
+/*     $OpenBSD: hotplug.c,v 1.24 2023/09/22 22:12:32 mvs Exp $        */
 /*
  * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
  *
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/device.h>
+#include <sys/event.h>
 #include <sys/fcntl.h>
 #include <sys/hotplug.h>
 #include <sys/ioctl.h>
-#include <sys/selinfo.h>
+#include <sys/mutex.h>
 #include <sys/vnode.h>
 
 #define HOTPLUG_MAXEVENTS      64
 
+/*
+ * Locks used to protect struct members and global data
+ *      M      hotplug_mtx
+ */
+
+static struct mutex hotplug_mtx = MUTEX_INITIALIZER(IPL_MPFLOOR);
+
 static int opened;
 static struct hotplug_event evqueue[HOTPLUG_MAXEVENTS];
-static int evqueue_head, evqueue_tail, evqueue_count;
-static struct selinfo hotplug_sel;
+static int evqueue_head, evqueue_tail, evqueue_count;  /* [M] */
+static struct klist hotplug_klist;                     /* [M] */
 
 void filt_hotplugrdetach(struct knote *);
 int  filt_hotplugread(struct knote *, long);
+int  filt_hotplugmodify(struct kevent *, struct knote *);
+int  filt_hotplugprocess(struct knote *, struct kevent *);
 
 const struct filterops hotplugread_filtops = {
-       .f_flags        = FILTEROP_ISFD,
+       .f_flags        = FILTEROP_ISFD | FILTEROP_MPSAFE,
        .f_attach       = NULL,
        .f_detach       = filt_hotplugrdetach,
        .f_event        = filt_hotplugread,
+       .f_modify       = filt_hotplugmodify,
+       .f_process      = filt_hotplugprocess,
 };
 
 #define EVQUEUE_NEXT(p) (p == HOTPLUG_MAXEVENTS - 1 ? 0 : p + 1)
@@ -60,6 +72,8 @@ hotplugattach(int count)
        evqueue_head = 0;
        evqueue_tail = 0;
        evqueue_count = 0;
+
+       klist_init_mutex(&hotplug_klist, &hotplug_mtx);
 }
 
 void
@@ -87,7 +101,9 @@ hotplug_device_detach(enum devclass class, char *name)
 int
 hotplug_put_event(struct hotplug_event *he)
 {
+       mtx_enter(&hotplug_mtx);
        if (evqueue_count == HOTPLUG_MAXEVENTS && opened) {
+               mtx_leave(&hotplug_mtx);
                printf("hotplug: event lost, queue full\n");
                return (1);
        }
@@ -98,24 +114,21 @@ hotplug_put_event(struct hotplug_event *he)
                evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
        else 
                evqueue_count++;
+       knote_locked(&hotplug_klist, 0);
        wakeup(&evqueue);
-       selwakeup(&hotplug_sel);
+       mtx_leave(&hotplug_mtx);
        return (0);
 }
 
 int
 hotplug_get_event(struct hotplug_event *he)
 {
-       int s;
-
        if (evqueue_count == 0)
                return (1);
 
-       s = splbio();
        *he = evqueue[evqueue_tail];
        evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
        evqueue_count--;
-       splx(s);
        return (0);
 }
 
@@ -137,8 +150,11 @@ hotplugclose(dev_t dev, int flag, int mode, struct proc *p)
 {
        struct hotplug_event he;
 
+       mtx_enter(&hotplug_mtx);
        while (hotplug_get_event(&he) == 0)
                continue;
+       mtx_leave(&hotplug_mtx);
+       klist_invalidate(&hotplug_klist);
        opened = 0;
        return (0);
 }
@@ -152,16 +168,23 @@ hotplugread(dev_t dev, struct uio *uio, int flags)
        if (uio->uio_resid != sizeof(he))
                return (EINVAL);
 
-again:
-       if (hotplug_get_event(&he) == 0)
-               return (uiomove(&he, sizeof(he), uio));
-       if (flags & IO_NDELAY)
-               return (EAGAIN);
+       mtx_enter(&hotplug_mtx);
+       while (hotplug_get_event(&he)) {
+               if (flags & IO_NDELAY) {
+                       mtx_leave(&hotplug_mtx);
+                       return (EAGAIN);
+               }
+       
+               error = msleep_nsec(&evqueue, &hotplug_mtx, PRIBIO | PCATCH,
+                   "htplev", INFSLP);
+               if (error) {
+                       mtx_leave(&hotplug_mtx);
+                       return (error);
+               }
+       }
+       mtx_leave(&hotplug_mtx);
 
-       error = tsleep_nsec(&evqueue, PRIBIO | PCATCH, "htplev", INFSLP);
-       if (error)
-               return (error);
-       goto again;
+       return (uiomove(&he, sizeof(he), uio));
 }
 
 int
@@ -183,32 +206,22 @@ hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
 int
 hotplugkqfilter(dev_t dev, struct knote *kn)
 {
-       struct klist *klist;
-       int s;
-
        switch (kn->kn_filter) {
        case EVFILT_READ:
-               klist = &hotplug_sel.si_note;
                kn->kn_fop = &hotplugread_filtops;
                break;
        default:
                return (EINVAL);
        }
 
-       s = splbio();
-       klist_insert_locked(klist, kn);
-       splx(s);
+       klist_insert(&hotplug_klist, kn);
        return (0);
 }
 
 void
 filt_hotplugrdetach(struct knote *kn)
 {
-       int s;
-
-       s = splbio();
-       klist_remove_locked(&hotplug_sel.si_note, kn);
-       splx(s);
+       klist_remove(&hotplug_klist, kn);
 }
 
 int
@@ -218,3 +231,27 @@ filt_hotplugread(struct knote *kn, long hint)
 
        return (evqueue_count > 0);
 }
+
+int
+filt_hotplugmodify(struct kevent *kev, struct knote *kn)
+{
+       int active;
+
+       mtx_enter(&hotplug_mtx);
+       active = knote_modify(kev, kn);
+       mtx_leave(&hotplug_mtx);
+
+       return (active);
+}
+
+int
+filt_hotplugprocess(struct knote *kn, struct kevent *kev)
+{
+       int active;
+
+       mtx_enter(&hotplug_mtx);
+       active = knote_process(kn, kev);
+       mtx_leave(&hotplug_mtx);
+
+       return (active);
+}