-/* $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)
evqueue_head = 0;
evqueue_tail = 0;
evqueue_count = 0;
+
+ klist_init_mutex(&hotplug_klist, &hotplug_mtx);
}
void
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);
}
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);
}
{
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);
}
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
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
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);
+}