[BACK]Return to hotplug.c CVS log [TXT][DIR] Up to [local] / src / sys / dev

File: [local] / src / sys / dev / hotplug.c (download)

Revision 1.24, Fri Sep 22 22:12:32 2023 UTC (8 months, 2 weeks ago) by mvs
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, HEAD
Changes since 1.23: +69 -32 lines

Introduce `hotplug_mtx' mutex(9) and make `hotplugread_filtops' MP safe.
Use this mutex(9) to protect `evqueue_head', `evqueue_tail' and
`evqueue_count'.

ok bluhm

/*	$OpenBSD: hotplug.c,v 1.24 2023/09/22 22:12:32 mvs Exp $	*/
/*
 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Device attachment and detachment notifications.
 */

#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/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;	/* [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 | 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)


int hotplug_put_event(struct hotplug_event *);
int hotplug_get_event(struct hotplug_event *);

void hotplugattach(int);

void
hotplugattach(int count)
{
	opened = 0;
	evqueue_head = 0;
	evqueue_tail = 0;
	evqueue_count = 0;

	klist_init_mutex(&hotplug_klist, &hotplug_mtx);
}

void
hotplug_device_attach(enum devclass class, char *name)
{
	struct hotplug_event he;

	he.he_type = HOTPLUG_DEVAT;
	he.he_devclass = class;
	strlcpy(he.he_devname, name, sizeof(he.he_devname));
	hotplug_put_event(&he);
}

void
hotplug_device_detach(enum devclass class, char *name)
{
	struct hotplug_event he;

	he.he_type = HOTPLUG_DEVDT;
	he.he_devclass = class;
	strlcpy(he.he_devname, name, sizeof(he.he_devname));
	hotplug_put_event(&he);
}

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[evqueue_head] = *he;
	evqueue_head = EVQUEUE_NEXT(evqueue_head);
	if (evqueue_count == HOTPLUG_MAXEVENTS)
		evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
	else 
		evqueue_count++;
	knote_locked(&hotplug_klist, 0);
	wakeup(&evqueue);
	mtx_leave(&hotplug_mtx);
	return (0);
}

int
hotplug_get_event(struct hotplug_event *he)
{
	if (evqueue_count == 0)
		return (1);

	*he = evqueue[evqueue_tail];
	evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
	evqueue_count--;
	return (0);
}

int
hotplugopen(dev_t dev, int flag, int mode, struct proc *p)
{
	if (minor(dev) != 0)
		return (ENXIO);
	if ((flag & FWRITE))
		return (EPERM);
	if (opened)
		return (EBUSY);
	opened = 1;
	return (0);
}

int
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);
}

int
hotplugread(dev_t dev, struct uio *uio, int flags)
{
	struct hotplug_event he;
	int error;

	if (uio->uio_resid != sizeof(he))
		return (EINVAL);

	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);

	return (uiomove(&he, sizeof(he), uio));
}

int
hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	switch (cmd) {
	case FIOASYNC:
		/* ignore */
	case FIONBIO:
		/* handled in the upper fs layer */
		break;
	default:
		return (ENOTTY);
	}

	return (0);
}

int
hotplugkqfilter(dev_t dev, struct knote *kn)
{
	switch (kn->kn_filter) {
	case EVFILT_READ:
		kn->kn_fop = &hotplugread_filtops;
		break;
	default:
		return (EINVAL);
	}

	klist_insert(&hotplug_klist, kn);
	return (0);
}

void
filt_hotplugrdetach(struct knote *kn)
{
	klist_remove(&hotplug_klist, kn);
}

int
filt_hotplugread(struct knote *kn, long hint)
{
	kn->kn_data = evqueue_count;

	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);
}