Permalink
Browse files

hotplug: implement concurrent readers through cloning

change /dev/hotplug to 444 since readers don't affect others
  • Loading branch information...
jcs committed Aug 30, 2018
1 parent 0befab1 commit 1d49b6870e7d96ebdb72bc79b676479f47cfaaee
Showing with 122 additions and 46 deletions.
  1. +1 −1 etc/MAKEDEV.common
  2. +120 −44 sys/dev/hotplug.c
  3. +1 −1 sys/sys/conf.h
@@ -512,7 +512,7 @@ __devitem(pdc, pdc, PDC device)dnl
__devitem(gpr, gpr*, GPR400 smartcard reader)dnl
_mcdev(gpr, gpr*, gpr, {-major_gpr_c-})dnl
__devitem(hotplug, hotplug, devices hot plugging)dnl
_mkdev(hotplug, hotplug, {-M hotplug c major_hotplug_c $U 400-})dnl
_mkdev(hotplug, hotplug, {-M hotplug c major_hotplug_c $U 444-})dnl
__devitem(ipmi, ipmi*, IPMI BMC access)dnl
_mkdev(ipmi, ipmi*, {-M ipmi$U c major_ipmi_c $U 600-})dnl
__devitem(gpio, gpio*, General Purpose Input/Output)dnl
@@ -25,15 +25,25 @@
#include <sys/fcntl.h>
#include <sys/hotplug.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/poll.h>
#include <sys/vnode.h>
#define HOTPLUG_MAXEVENTS 64
#define HOTPLUG_EMPTY -1
struct hotplug_d {
int hd_unit;
int evqueue_head;
struct selinfo hotplug_sel;
LIST_ENTRY(hotplug_d) hd_list;
};
LIST_HEAD(, hotplug_d) hotplug_d_list;
struct hotplug_d *hotplug_lookup(int);
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;
void filt_hotplugrdetach(struct knote *);
int filt_hotplugread(struct knote *, long);
@@ -42,20 +52,23 @@ struct filterops hotplugread_filtops =
{ 1, NULL, filt_hotplugrdetach, filt_hotplugread};
#define EVQUEUE_NEXT(p) (p == HOTPLUG_MAXEVENTS - 1 ? 0 : p + 1)
#define EVQUEUE_PREV(p) (p == 0 ? HOTPLUG_MAXEVENTS : p - 1)
int hotplug_put_event(struct hotplug_event *);
int hotplug_get_event(struct hotplug_event *);
void hotplug_put_event(struct hotplug_event *);
int hotplug_get_event(struct hotplug_d *hd, struct hotplug_event *);
void hotplugattach(int);
void
hotplugattach(int count)
{
opened = 0;
int i;
evqueue_head = 0;
evqueue_tail = 0;
evqueue_count = 0;
for (i = 0; i < HOTPLUG_MAXEVENTS; i++)
evqueue[i].he_type = HOTPLUG_EMPTY;
LIST_INIT(&hotplug_d_list);
}
void
@@ -80,76 +93,116 @@ hotplug_device_detach(enum devclass class, char *name)
hotplug_put_event(&he);
}
int
void
hotplug_put_event(struct hotplug_event *he)
{
if (evqueue_count == HOTPLUG_MAXEVENTS && opened) {
printf("hotplug: event lost, queue full\n");
return (1);
}
struct hotplug_d *hd;
evqueue[evqueue_head] = *he;
evqueue_head = EVQUEUE_NEXT(evqueue_head);
if (evqueue_count == HOTPLUG_MAXEVENTS)
evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
else
evqueue_count++;
evqueue[evqueue_head].he_type = HOTPLUG_EMPTY;
/*
* If any readers are still at the new evqueue_head, they are about to
* get lapped. Advance them one ahead.
*/
LIST_FOREACH(hd, &hotplug_d_list, hd_list) {
if (hd->evqueue_head == evqueue_head)
hd->evqueue_head = EVQUEUE_NEXT(evqueue_head);
selwakeup(&hd->hotplug_sel);
}
wakeup(&evqueue);
selwakeup(&hotplug_sel);
return (0);
}
int
hotplug_get_event(struct hotplug_event *he)
hotplug_get_event(struct hotplug_d *hd, 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--;
if (evqueue[hd->evqueue_head].he_type != HOTPLUG_EMPTY) {
*he = evqueue[hd->evqueue_head];
hd->evqueue_head = EVQUEUE_NEXT(hd->evqueue_head);
splx(s);
return (0);
}
splx(s);
return (0);
return (1);
}
int
hotplugopen(dev_t dev, int flag, int mode, struct proc *p)
{
if (minor(dev) != 0)
return (ENXIO);
if ((flag & FWRITE))
struct hotplug_d *hd;
int unit = minor(dev);
int i;
if (flag & FWRITE)
return (EPERM);
if (opened)
if ((hd = hotplug_lookup(unit)) != NULL)
return (EBUSY);
opened = 1;
hd = malloc(sizeof(*hd), M_DEVBUF, M_WAITOK|M_ZERO);
hd->hd_unit = unit;
/*
* Set head as far back as possible so each reader gets all historical
* events.
*/
for (i = EVQUEUE_PREV(evqueue_head);
evqueue[i].he_type != HOTPLUG_EMPTY && i != evqueue_head;
i = EVQUEUE_PREV(i))
;
hd->evqueue_head = EVQUEUE_NEXT(i);
LIST_INSERT_HEAD(&hotplug_d_list, hd, hd_list);
return (0);
}
struct hotplug_d *
hotplug_lookup(int unit)
{
struct hotplug_d *hd;
LIST_FOREACH(hd, &hotplug_d_list, hd_list)
if (hd->hd_unit == unit)
return (hd);
return (NULL);
}
int
hotplugclose(dev_t dev, int flag, int mode, struct proc *p)
{
struct hotplug_event he;
struct hotplug_d *hd;
hd = hotplug_lookup(minor(dev));
if (hd == NULL)
return (EINVAL);
LIST_REMOVE(hd, hd_list);
free(hd, M_DEVBUF, sizeof(*hd));
while (hotplug_get_event(&he) == 0)
continue;
opened = 0;
return (0);
}
int
hotplugread(dev_t dev, struct uio *uio, int flags)
{
struct hotplug_d *hd;
struct hotplug_event he;
int error;
hd = hotplug_lookup(minor(dev));
if (hd == NULL)
return (ENXIO);
if (uio->uio_resid != sizeof(he))
return (EINVAL);
again:
if (hotplug_get_event(&he) == 0)
if (hotplug_get_event(hd, &he) == 0)
return (uiomove(&he, sizeof(he), uio));
if (flags & IO_NDELAY)
return (EAGAIN);
@@ -163,6 +216,12 @@ hotplugread(dev_t dev, struct uio *uio, int flags)
int
hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct hotplug_d *hd;
hd = hotplug_lookup(minor(dev));
if (hd == NULL)
return (ENXIO);
switch (cmd) {
case FIOASYNC:
/* ignore */
@@ -179,13 +238,19 @@ hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
int
hotplugpoll(dev_t dev, int events, struct proc *p)
{
struct hotplug_d *hd;
int revents = 0;
hd = hotplug_lookup(minor(dev));
if (hd == NULL)
return (POLLERR);
if (events & (POLLIN | POLLRDNORM)) {
if (evqueue_count > 0)
if (evqueue[EVQUEUE_NEXT(hd->evqueue_head)].he_type !=
HOTPLUG_EMPTY)
revents |= events & (POLLIN | POLLRDNORM);
else
selrecord(p, &hotplug_sel);
selrecord(p, &hd->hotplug_sel);
}
return (revents);
@@ -194,18 +259,25 @@ hotplugpoll(dev_t dev, int events, struct proc *p)
int
hotplugkqfilter(dev_t dev, struct knote *kn)
{
struct hotplug_d *hd;
struct klist *klist;
int s;
hd = hotplug_lookup(minor(dev));
if (hd == NULL)
return (EINVAL);
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &hotplug_sel.si_note;
klist = &hd->hotplug_sel.si_note;
kn->kn_fop = &hotplugread_filtops;
break;
default:
return (EINVAL);
}
kn->kn_hook = hd;
s = splbio();
SLIST_INSERT_HEAD(klist, kn, kn_selnext);
splx(s);
@@ -215,17 +287,21 @@ hotplugkqfilter(dev_t dev, struct knote *kn)
void
filt_hotplugrdetach(struct knote *kn)
{
struct hotplug_d *hd = kn->kn_hook;
int s;
s = splbio();
SLIST_REMOVE(&hotplug_sel.si_note, kn, knote, kn_selnext);
SLIST_REMOVE(&hd->hotplug_sel.si_note, kn, knote, kn_selnext);
splx(s);
}
int
filt_hotplugread(struct knote *kn, long hint)
{
kn->kn_data = evqueue_count;
struct hotplug_d *hd = kn->kn_hook;
if (evqueue[hd->evqueue_head].he_type == HOTPLUG_EMPTY)
return (0);
return (evqueue_count > 0);
return (1);
}
@@ -423,7 +423,7 @@ extern struct cdevsw cdevsw[];
dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), \
(dev_type_write((*))) enodev, dev_init(c,n,ioctl), \
(dev_type_stop((*))) enodev, 0, dev_init(c,n,poll), \
(dev_type_mmap((*))) enodev, 0, 0, dev_init(c,n,kqfilter) }
(dev_type_mmap((*))) enodev, 0, D_CLONE, dev_init(c,n,kqfilter) }
/* open, close, ioctl */
#define cdev_gpio_init(c,n) { \

0 comments on commit 1d49b68

Please sign in to comment.