xref: /openbsd-src/sys/dev/hotplug.c (revision b9ae17a00bed12afbf856d60e03f648694a9de20)
1*b9ae17a0Sguenther /*	$OpenBSD: hotplug.c,v 1.25 2024/12/30 02:46:00 guenther Exp $	*/
2cfd0487fSgrange /*
3cfd0487fSgrange  * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
4cfd0487fSgrange  *
5cfd0487fSgrange  * Permission to use, copy, modify, and distribute this software for any
6cfd0487fSgrange  * purpose with or without fee is hereby granted, provided that the above
7cfd0487fSgrange  * copyright notice and this permission notice appear in all copies.
8cfd0487fSgrange  *
9cfd0487fSgrange  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10cfd0487fSgrange  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11cfd0487fSgrange  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12cfd0487fSgrange  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13cfd0487fSgrange  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14cfd0487fSgrange  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15cfd0487fSgrange  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16cfd0487fSgrange  */
17cfd0487fSgrange 
18cfd0487fSgrange /*
19cfd0487fSgrange  * Device attachment and detachment notifications.
20cfd0487fSgrange  */
21cfd0487fSgrange 
22cfd0487fSgrange #include <sys/param.h>
23cfd0487fSgrange #include <sys/systm.h>
24cfd0487fSgrange #include <sys/device.h>
259e667ebcSmvs #include <sys/event.h>
26cfd0487fSgrange #include <sys/fcntl.h>
27cfd0487fSgrange #include <sys/hotplug.h>
28cfd0487fSgrange #include <sys/ioctl.h>
299e667ebcSmvs #include <sys/mutex.h>
30cfd0487fSgrange #include <sys/vnode.h>
31cfd0487fSgrange 
32ccee5369Stedu #define HOTPLUG_MAXEVENTS	64
33cfd0487fSgrange 
349e667ebcSmvs /*
359e667ebcSmvs  * Locks used to protect struct members and global data
369e667ebcSmvs  *	 M	hotplug_mtx
379e667ebcSmvs  */
389e667ebcSmvs 
399e667ebcSmvs static struct mutex hotplug_mtx = MUTEX_INITIALIZER(IPL_MPFLOOR);
409e667ebcSmvs 
41cfd0487fSgrange static int opened;
42d14ac25bStedu static struct hotplug_event evqueue[HOTPLUG_MAXEVENTS];
439e667ebcSmvs static int evqueue_head, evqueue_tail, evqueue_count;	/* [M] */
449e667ebcSmvs static struct klist hotplug_klist;			/* [M] */
45cfd0487fSgrange 
46cfd0487fSgrange void filt_hotplugrdetach(struct knote *);
47cfd0487fSgrange int  filt_hotplugread(struct knote *, long);
489e667ebcSmvs int  filt_hotplugmodify(struct kevent *, struct knote *);
499e667ebcSmvs int  filt_hotplugprocess(struct knote *, struct kevent *);
50cfd0487fSgrange 
5194321eb4Svisa const struct filterops hotplugread_filtops = {
529e667ebcSmvs 	.f_flags	= FILTEROP_ISFD | FILTEROP_MPSAFE,
5394321eb4Svisa 	.f_attach	= NULL,
5494321eb4Svisa 	.f_detach	= filt_hotplugrdetach,
5594321eb4Svisa 	.f_event	= filt_hotplugread,
569e667ebcSmvs 	.f_modify	= filt_hotplugmodify,
579e667ebcSmvs 	.f_process	= filt_hotplugprocess,
5894321eb4Svisa };
59cfd0487fSgrange 
60cfd0487fSgrange #define EVQUEUE_NEXT(p) (p == HOTPLUG_MAXEVENTS - 1 ? 0 : p + 1)
61cfd0487fSgrange 
62cfd0487fSgrange 
63cfd0487fSgrange int hotplug_put_event(struct hotplug_event *);
64cfd0487fSgrange int hotplug_get_event(struct hotplug_event *);
65cfd0487fSgrange 
66cfd0487fSgrange void hotplugattach(int);
67cfd0487fSgrange 
68cfd0487fSgrange void
69cfd0487fSgrange hotplugattach(int count)
70cfd0487fSgrange {
71cfd0487fSgrange 	opened = 0;
72cfd0487fSgrange 	evqueue_head = 0;
73cfd0487fSgrange 	evqueue_tail = 0;
74cfd0487fSgrange 	evqueue_count = 0;
759e667ebcSmvs 
769e667ebcSmvs 	klist_init_mutex(&hotplug_klist, &hotplug_mtx);
77cfd0487fSgrange }
78cfd0487fSgrange 
79cfd0487fSgrange void
807c80f029Smk hotplug_device_attach(enum devclass class, char *name)
81cfd0487fSgrange {
82cfd0487fSgrange 	struct hotplug_event he;
83cfd0487fSgrange 
84cfd0487fSgrange 	he.he_type = HOTPLUG_DEVAT;
85cfd0487fSgrange 	he.he_devclass = class;
86cfd0487fSgrange 	strlcpy(he.he_devname, name, sizeof(he.he_devname));
87cfd0487fSgrange 	hotplug_put_event(&he);
88cfd0487fSgrange }
89cfd0487fSgrange 
90cfd0487fSgrange void
917c80f029Smk hotplug_device_detach(enum devclass class, char *name)
92cfd0487fSgrange {
93cfd0487fSgrange 	struct hotplug_event he;
94cfd0487fSgrange 
95cfd0487fSgrange 	he.he_type = HOTPLUG_DEVDT;
96cfd0487fSgrange 	he.he_devclass = class;
97cfd0487fSgrange 	strlcpy(he.he_devname, name, sizeof(he.he_devname));
98cfd0487fSgrange 	hotplug_put_event(&he);
99cfd0487fSgrange }
100cfd0487fSgrange 
101cfd0487fSgrange int
102cfd0487fSgrange hotplug_put_event(struct hotplug_event *he)
103cfd0487fSgrange {
1049e667ebcSmvs 	mtx_enter(&hotplug_mtx);
105d6be6782Sgrange 	if (evqueue_count == HOTPLUG_MAXEVENTS && opened) {
1069e667ebcSmvs 		mtx_leave(&hotplug_mtx);
107cfd0487fSgrange 		printf("hotplug: event lost, queue full\n");
108cfd0487fSgrange 		return (1);
109cfd0487fSgrange 	}
110cfd0487fSgrange 
111cfd0487fSgrange 	evqueue[evqueue_head] = *he;
112cfd0487fSgrange 	evqueue_head = EVQUEUE_NEXT(evqueue_head);
1138928c497Sdrahn 	if (evqueue_count == HOTPLUG_MAXEVENTS)
1148928c497Sdrahn 		evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
1158928c497Sdrahn 	else
116cfd0487fSgrange 		evqueue_count++;
1179e667ebcSmvs 	knote_locked(&hotplug_klist, 0);
118cfd0487fSgrange 	wakeup(&evqueue);
1199e667ebcSmvs 	mtx_leave(&hotplug_mtx);
120cfd0487fSgrange 	return (0);
121cfd0487fSgrange }
122cfd0487fSgrange 
123cfd0487fSgrange int
124cfd0487fSgrange hotplug_get_event(struct hotplug_event *he)
125cfd0487fSgrange {
126cfd0487fSgrange 	if (evqueue_count == 0)
127cfd0487fSgrange 		return (1);
128cfd0487fSgrange 
129cfd0487fSgrange 	*he = evqueue[evqueue_tail];
130cfd0487fSgrange 	evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
131cfd0487fSgrange 	evqueue_count--;
132cfd0487fSgrange 	return (0);
133cfd0487fSgrange }
134cfd0487fSgrange 
135cfd0487fSgrange int
136cfd0487fSgrange hotplugopen(dev_t dev, int flag, int mode, struct proc *p)
137cfd0487fSgrange {
138cfd0487fSgrange 	if (minor(dev) != 0)
139cfd0487fSgrange 		return (ENXIO);
140cfd0487fSgrange 	if ((flag & FWRITE))
141cfd0487fSgrange 		return (EPERM);
142cfd0487fSgrange 	if (opened)
143cfd0487fSgrange 		return (EBUSY);
144cfd0487fSgrange 	opened = 1;
145cfd0487fSgrange 	return (0);
146cfd0487fSgrange }
147cfd0487fSgrange 
148cfd0487fSgrange int
149cfd0487fSgrange hotplugclose(dev_t dev, int flag, int mode, struct proc *p)
150cfd0487fSgrange {
15108fbe7e7Sderaadt 	struct hotplug_event he;
15208fbe7e7Sderaadt 
1539e667ebcSmvs 	mtx_enter(&hotplug_mtx);
154b6a3a3ddSmiod 	while (hotplug_get_event(&he) == 0)
15542fa0292Stedu 		continue;
1569e667ebcSmvs 	mtx_leave(&hotplug_mtx);
1579e667ebcSmvs 	klist_invalidate(&hotplug_klist);
158cfd0487fSgrange 	opened = 0;
159cfd0487fSgrange 	return (0);
160cfd0487fSgrange }
161cfd0487fSgrange 
162cfd0487fSgrange int
163cfd0487fSgrange hotplugread(dev_t dev, struct uio *uio, int flags)
164cfd0487fSgrange {
165cfd0487fSgrange 	struct hotplug_event he;
166cfd0487fSgrange 	int error;
167cfd0487fSgrange 
168cfd0487fSgrange 	if (uio->uio_resid != sizeof(he))
169cfd0487fSgrange 		return (EINVAL);
170cfd0487fSgrange 
1719e667ebcSmvs 	mtx_enter(&hotplug_mtx);
1729e667ebcSmvs 	while (hotplug_get_event(&he)) {
1739e667ebcSmvs 		if (flags & IO_NDELAY) {
1749e667ebcSmvs 			mtx_leave(&hotplug_mtx);
175cfd0487fSgrange 			return (EAGAIN);
1769e667ebcSmvs 		}
177cfd0487fSgrange 
1789e667ebcSmvs 		error = msleep_nsec(&evqueue, &hotplug_mtx, PRIBIO | PCATCH,
1799e667ebcSmvs 		    "htplev", INFSLP);
1809e667ebcSmvs 		if (error) {
1819e667ebcSmvs 			mtx_leave(&hotplug_mtx);
182cfd0487fSgrange 			return (error);
1839e667ebcSmvs 		}
1849e667ebcSmvs 	}
1859e667ebcSmvs 	mtx_leave(&hotplug_mtx);
1869e667ebcSmvs 
1879e667ebcSmvs 	return (uiomove(&he, sizeof(he), uio));
188cfd0487fSgrange }
189cfd0487fSgrange 
190cfd0487fSgrange int
191cfd0487fSgrange hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
192cfd0487fSgrange {
193cfd0487fSgrange 	switch (cmd) {
194cfd0487fSgrange 	case FIOASYNC:
195cfd0487fSgrange 		/* ignore */
196cfd0487fSgrange 	default:
197cfd0487fSgrange 		return (ENOTTY);
198cfd0487fSgrange 	}
199cfd0487fSgrange 
200cfd0487fSgrange 	return (0);
201cfd0487fSgrange }
202cfd0487fSgrange 
203cfd0487fSgrange int
204cfd0487fSgrange hotplugkqfilter(dev_t dev, struct knote *kn)
205cfd0487fSgrange {
206cfd0487fSgrange 	switch (kn->kn_filter) {
207cfd0487fSgrange 	case EVFILT_READ:
208cfd0487fSgrange 		kn->kn_fop = &hotplugread_filtops;
209cfd0487fSgrange 		break;
210cfd0487fSgrange 	default:
211b8d5a5fbSnicm 		return (EINVAL);
212cfd0487fSgrange 	}
213cfd0487fSgrange 
2149e667ebcSmvs 	klist_insert(&hotplug_klist, kn);
215cfd0487fSgrange 	return (0);
216cfd0487fSgrange }
217cfd0487fSgrange 
218cfd0487fSgrange void
219cfd0487fSgrange filt_hotplugrdetach(struct knote *kn)
220cfd0487fSgrange {
2219e667ebcSmvs 	klist_remove(&hotplug_klist, kn);
222cfd0487fSgrange }
223cfd0487fSgrange 
224cfd0487fSgrange int
225cfd0487fSgrange filt_hotplugread(struct knote *kn, long hint)
226cfd0487fSgrange {
227cfd0487fSgrange 	kn->kn_data = evqueue_count;
228cfd0487fSgrange 
229cfd0487fSgrange 	return (evqueue_count > 0);
230cfd0487fSgrange }
2319e667ebcSmvs 
2329e667ebcSmvs int
2339e667ebcSmvs filt_hotplugmodify(struct kevent *kev, struct knote *kn)
2349e667ebcSmvs {
2359e667ebcSmvs 	int active;
2369e667ebcSmvs 
2379e667ebcSmvs 	mtx_enter(&hotplug_mtx);
2389e667ebcSmvs 	active = knote_modify(kev, kn);
2399e667ebcSmvs 	mtx_leave(&hotplug_mtx);
2409e667ebcSmvs 
2419e667ebcSmvs 	return (active);
2429e667ebcSmvs }
2439e667ebcSmvs 
2449e667ebcSmvs int
2459e667ebcSmvs filt_hotplugprocess(struct knote *kn, struct kevent *kev)
2469e667ebcSmvs {
2479e667ebcSmvs 	int active;
2489e667ebcSmvs 
2499e667ebcSmvs 	mtx_enter(&hotplug_mtx);
2509e667ebcSmvs 	active = knote_process(kn, kev);
2519e667ebcSmvs 	mtx_leave(&hotplug_mtx);
2529e667ebcSmvs 
2539e667ebcSmvs 	return (active);
2549e667ebcSmvs }
255