clone.c revision 170719
1170159Sariff/*-
2170159Sariff * Copyright (c) 2007 Ariff Abdullah <ariff@FreeBSD.org>
3170159Sariff * All rights reserved.
4170159Sariff *
5170159Sariff * Redistribution and use in source and binary forms, with or without
6170159Sariff * modification, are permitted provided that the following conditions
7170159Sariff * are met:
8170159Sariff * 1. Redistributions of source code must retain the above copyright
9170159Sariff *    notice, this list of conditions and the following disclaimer.
10170159Sariff * 2. Redistributions in binary form must reproduce the above copyright
11170159Sariff *    notice, this list of conditions and the following disclaimer in the
12170159Sariff *    documentation and/or other materials provided with the distribution.
13170159Sariff *
14170159Sariff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15170159Sariff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16170159Sariff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17170159Sariff * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18170159Sariff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19170159Sariff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20170159Sariff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21170159Sariff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22170159Sariff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23170159Sariff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24170159Sariff * SUCH DAMAGE.
25170159Sariff *
26170159Sariff * $FreeBSD: head/sys/dev/sound/clone.c 170719 2007-06-14 11:10:21Z ariff $
27170159Sariff */
28170159Sariff
29170159Sariff#include <sys/param.h>
30170159Sariff#include <sys/systm.h>
31170159Sariff#include <sys/conf.h>
32170159Sariff#include <sys/kernel.h>
33170159Sariff#include <sys/malloc.h>
34170159Sariff#include <sys/proc.h>
35170159Sariff
36170159Sariff#if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG)
37170159Sariff#include <dev/sound/pcm/sound.h>
38170159Sariff#endif
39170159Sariff
40170159Sariff#include <dev/sound/clone.h>
41170159Sariff
42170159Sariff/*
43170159Sariff * So here we go again, another clonedevs manager. Unlike default clonedevs,
44170159Sariff * this clone manager is designed to withstand various abusive behavior
45170159Sariff * (such as 'while : ; do ls /dev/whatever ; done', etc.), reusable object
46170159Sariff * after reaching certain expiration threshold, aggressive garbage collector,
47170159Sariff * transparent device allocator and concurrency handling across multiple
48170159Sariff * thread/proc. Due to limited information given by dev_clone EVENTHANDLER,
49170159Sariff * we don't have much clues whether the caller wants a real open() or simply
50170159Sariff * making fun of us with things like stat(), mtime() etc. Assuming that:
51170159Sariff * 1) Time window between dev_clone EH <-> real open() should be small
52170159Sariff * enough and 2) mtime()/stat() etc. always looks like a half way / stalled
53170159Sariff * operation, we can decide whether a new cdev must be created, old
54170159Sariff * (expired) cdev can be reused or an existing cdev can be shared.
55170159Sariff *
56170159Sariff * Most of the operations and logics are generic enough and can be applied
57170159Sariff * on other places (such as if_tap, snp, etc).  Perhaps this can be
58170159Sariff * rearranged to complement clone_*(). However, due to this still being
59170159Sariff * specific to the sound driver (and as a proof of concept on how it can be
60170159Sariff * done), si_drv2 is used to keep the pointer of the clone list entry to
61170159Sariff * avoid expensive lookup.
62170159Sariff */
63170159Sariff
64170159Sariff/* clone entry */
65170159Sariffstruct snd_clone_entry {
66170159Sariff	TAILQ_ENTRY(snd_clone_entry) link;
67170159Sariff	struct snd_clone *parent;
68170159Sariff	struct cdev *devt;
69170159Sariff	struct timespec tsp;
70170159Sariff	uint32_t flags;
71170159Sariff	pid_t pid;
72170159Sariff	int unit;
73170159Sariff};
74170159Sariff
75170159Sariff/* clone manager */
76170159Sariffstruct snd_clone {
77170159Sariff	TAILQ_HEAD(link_head, snd_clone_entry) head;
78170159Sariff	struct timespec tsp;
79170159Sariff	int refcount;
80170159Sariff	int size;
81170159Sariff	int typemask;
82170159Sariff	int maxunit;
83170159Sariff	int deadline;
84170159Sariff	uint32_t flags;
85170159Sariff};
86170159Sariff
87170159Sariff#ifdef SND_DIAGNOSTIC
88170159Sariff#define SND_CLONE_ASSERT(x, y)		do {			\
89170159Sariff	if (!(x))						\
90170159Sariff		panic y;					\
91170159Sariff} while(0)
92170159Sariff#else
93170159Sariff#define SND_CLONE_ASSERT(x...)		KASSERT(x)
94170159Sariff#endif
95170159Sariff
96170159Sariff/*
97170159Sariff * Shamelessly ripped off from vfs_subr.c
98170159Sariff * We need at least 1/HZ precision as default timestamping.
99170159Sariff */
100170159Sariffenum { SND_TSP_SEC, SND_TSP_HZ, SND_TSP_USEC, SND_TSP_NSEC };
101170159Sariff
102170159Sariffstatic int snd_timestamp_precision = SND_TSP_HZ;
103170159SariffTUNABLE_INT("hw.snd.timestamp_precision", &snd_timestamp_precision);
104170159Sariff
105170159Sariffvoid
106170159Sariffsnd_timestamp(struct timespec *tsp)
107170159Sariff{
108170159Sariff	struct timeval tv;
109170159Sariff
110170159Sariff	switch (snd_timestamp_precision) {
111170159Sariff	case SND_TSP_SEC:
112170159Sariff		tsp->tv_sec = time_second;
113170159Sariff		tsp->tv_nsec = 0;
114170159Sariff		break;
115170159Sariff	case SND_TSP_HZ:
116170159Sariff		getnanouptime(tsp);
117170159Sariff		break;
118170159Sariff	case SND_TSP_USEC:
119170159Sariff		microuptime(&tv);
120170159Sariff		TIMEVAL_TO_TIMESPEC(&tv, tsp);
121170159Sariff		break;
122170159Sariff	case SND_TSP_NSEC:
123170159Sariff		nanouptime(tsp);
124170159Sariff		break;
125170159Sariff	default:
126170159Sariff		snd_timestamp_precision = SND_TSP_HZ;
127170159Sariff		getnanouptime(tsp);
128170159Sariff		break;
129170159Sariff	}
130170159Sariff}
131170159Sariff
132170159Sariff#if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG)
133170159Sariffstatic int
134170159Sariffsysctl_hw_snd_timestamp_precision(SYSCTL_HANDLER_ARGS)
135170159Sariff{
136170159Sariff	int err, val;
137170159Sariff
138170159Sariff	val = snd_timestamp_precision;
139170289Sdwmalone	err = sysctl_handle_int(oidp, &val, 0, req);
140170159Sariff	if (err == 0 && req->newptr != NULL) {
141170159Sariff		switch (val) {
142170159Sariff		case SND_TSP_SEC:
143170159Sariff		case SND_TSP_HZ:
144170159Sariff		case SND_TSP_USEC:
145170159Sariff		case SND_TSP_NSEC:
146170159Sariff			snd_timestamp_precision = val;
147170159Sariff			break;
148170159Sariff		default:
149170159Sariff			break;
150170159Sariff		}
151170159Sariff	}
152170159Sariff
153170159Sariff	return (err);
154170159Sariff}
155170159SariffSYSCTL_PROC(_hw_snd, OID_AUTO, timestamp_precision, CTLTYPE_INT | CTLFLAG_RW,
156170159Sariff    0, sizeof(int), sysctl_hw_snd_timestamp_precision, "I",
157170159Sariff    "timestamp precision (0=s 1=hz 2=us 3=ns)");
158170159Sariff#endif
159170159Sariff
160170159Sariff/*
161170719Sariff * snd_clone_create() : Return opaque allocated clone manager.
162170159Sariff */
163170159Sariffstruct snd_clone *
164170719Sariffsnd_clone_create(int typemask, int maxunit, int deadline, uint32_t flags)
165170159Sariff{
166170159Sariff	struct snd_clone *c;
167170159Sariff
168170159Sariff	SND_CLONE_ASSERT(!(typemask & ~SND_CLONE_MAXUNIT),
169170159Sariff	    ("invalid typemask: 0x%08x", typemask));
170170159Sariff	SND_CLONE_ASSERT(maxunit == -1 ||
171170159Sariff	    !(maxunit & ~(~typemask & SND_CLONE_MAXUNIT)),
172170159Sariff	    ("maxunit overflow: typemask=0x%08x maxunit=%d",
173170159Sariff	    typemask, maxunit));
174170159Sariff	SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK),
175170159Sariff	    ("invalid clone flags=0x%08x", flags));
176170159Sariff
177170159Sariff	c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO);
178170159Sariff	c->refcount = 0;
179170159Sariff	c->size = 0;
180170159Sariff	c->typemask = typemask;
181170159Sariff	c->maxunit = (maxunit == -1) ? (~typemask & SND_CLONE_MAXUNIT) :
182170159Sariff	    maxunit;
183170159Sariff	c->deadline = deadline;
184170159Sariff	c->flags = flags;
185170159Sariff	snd_timestamp(&c->tsp);
186170159Sariff	TAILQ_INIT(&c->head);
187170159Sariff
188170159Sariff	return (c);
189170159Sariff}
190170159Sariff
191170159Sariffint
192170159Sariffsnd_clone_busy(struct snd_clone *c)
193170159Sariff{
194170159Sariff	struct snd_clone_entry *ce;
195170159Sariff
196170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
197170159Sariff
198170159Sariff	if (c->size == 0)
199170159Sariff		return (0);
200170159Sariff
201170159Sariff	TAILQ_FOREACH(ce, &c->head, link) {
202170159Sariff		if ((ce->flags & SND_CLONE_BUSY) ||
203170159Sariff		    (ce->devt != NULL && ce->devt->si_threadcount != 0))
204170159Sariff			return (EBUSY);
205170159Sariff	}
206170159Sariff
207170159Sariff	return (0);
208170159Sariff}
209170159Sariff
210170159Sariff/*
211170159Sariff * snd_clone_enable()/disable() : Suspend/resume clone allocation through
212170159Sariff * snd_clone_alloc(). Everything else will not be affected by this.
213170159Sariff */
214170159Sariffint
215170159Sariffsnd_clone_enable(struct snd_clone *c)
216170159Sariff{
217170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
218170159Sariff
219170159Sariff	if (c->flags & SND_CLONE_ENABLE)
220170159Sariff		return (EINVAL);
221170159Sariff
222170159Sariff	c->flags |= SND_CLONE_ENABLE;
223170159Sariff
224170159Sariff	return (0);
225170159Sariff}
226170159Sariff
227170159Sariffint
228170159Sariffsnd_clone_disable(struct snd_clone *c)
229170159Sariff{
230170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
231170159Sariff
232170159Sariff	if (!(c->flags & SND_CLONE_ENABLE))
233170159Sariff		return (EINVAL);
234170159Sariff
235170159Sariff	c->flags &= ~SND_CLONE_ENABLE;
236170159Sariff
237170159Sariff	return (0);
238170159Sariff}
239170159Sariff
240170159Sariff/*
241170159Sariff * Getters / Setters. Not worth explaining :)
242170159Sariff */
243170159Sariffint
244170159Sariffsnd_clone_getsize(struct snd_clone *c)
245170159Sariff{
246170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
247170159Sariff
248170159Sariff	return (c->size);
249170159Sariff}
250170159Sariff
251170159Sariffint
252170159Sariffsnd_clone_getmaxunit(struct snd_clone *c)
253170159Sariff{
254170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
255170159Sariff
256170159Sariff	return (c->maxunit);
257170159Sariff}
258170159Sariff
259170159Sariffint
260170159Sariffsnd_clone_setmaxunit(struct snd_clone *c, int maxunit)
261170159Sariff{
262170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
263170159Sariff	SND_CLONE_ASSERT(maxunit == -1 ||
264170159Sariff	    !(maxunit & ~(~c->typemask & SND_CLONE_MAXUNIT)),
265170159Sariff	    ("maxunit overflow: typemask=0x%08x maxunit=%d",
266170159Sariff	    c->typemask, maxunit));
267170159Sariff
268170159Sariff	c->maxunit = (maxunit == -1) ? (~c->typemask & SND_CLONE_MAXUNIT) :
269170159Sariff	    maxunit;
270170159Sariff
271170159Sariff	return (c->maxunit);
272170159Sariff}
273170159Sariff
274170159Sariffint
275170159Sariffsnd_clone_getdeadline(struct snd_clone *c)
276170159Sariff{
277170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
278170159Sariff
279170159Sariff	return (c->deadline);
280170159Sariff}
281170159Sariff
282170159Sariffint
283170159Sariffsnd_clone_setdeadline(struct snd_clone *c, int deadline)
284170159Sariff{
285170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
286170159Sariff
287170159Sariff	c->deadline = deadline;
288170159Sariff
289170159Sariff	return (c->deadline);
290170159Sariff}
291170159Sariff
292170159Sariffint
293170159Sariffsnd_clone_gettime(struct snd_clone *c, struct timespec *tsp)
294170159Sariff{
295170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
296170159Sariff	SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec"));
297170159Sariff
298170159Sariff	*tsp = c->tsp;
299170159Sariff
300170159Sariff	return (0);
301170159Sariff}
302170159Sariff
303170159Sariffuint32_t
304170159Sariffsnd_clone_getflags(struct snd_clone *c)
305170159Sariff{
306170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
307170159Sariff
308170159Sariff	return (c->flags);
309170159Sariff}
310170159Sariff
311170159Sariffuint32_t
312170159Sariffsnd_clone_setflags(struct snd_clone *c, uint32_t flags)
313170159Sariff{
314170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
315170159Sariff	SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK),
316170159Sariff	    ("invalid clone flags=0x%08x", flags));
317170159Sariff
318170159Sariff	c->flags = flags;
319170159Sariff
320170159Sariff	return (c->flags);
321170159Sariff}
322170159Sariff
323170159Sariffint
324170159Sariffsnd_clone_getdevtime(struct cdev *dev, struct timespec *tsp)
325170159Sariff{
326170159Sariff	struct snd_clone_entry *ce;
327170159Sariff
328170159Sariff	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
329170159Sariff	SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec"));
330170159Sariff
331170159Sariff	ce = dev->si_drv2;
332170159Sariff	if (ce == NULL)
333170159Sariff		return (ENODEV);
334170159Sariff
335170159Sariff	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
336170159Sariff
337170159Sariff	*tsp = ce->tsp;
338170159Sariff
339170159Sariff	return (0);
340170159Sariff}
341170159Sariff
342170159Sariffuint32_t
343170159Sariffsnd_clone_getdevflags(struct cdev *dev)
344170159Sariff{
345170159Sariff	struct snd_clone_entry *ce;
346170159Sariff
347170159Sariff	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
348170159Sariff
349170159Sariff	ce = dev->si_drv2;
350170159Sariff	if (ce == NULL)
351170159Sariff		return (0xffffffff);
352170159Sariff
353170159Sariff	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
354170159Sariff
355170159Sariff	return (ce->flags);
356170159Sariff}
357170159Sariff
358170159Sariffuint32_t
359170159Sariffsnd_clone_setdevflags(struct cdev *dev, uint32_t flags)
360170159Sariff{
361170159Sariff	struct snd_clone_entry *ce;
362170159Sariff
363170159Sariff	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
364170159Sariff	SND_CLONE_ASSERT(!(flags & ~SND_CLONE_DEVMASK),
365170159Sariff	    ("invalid clone dev flags=0x%08x", flags));
366170159Sariff
367170159Sariff	ce = dev->si_drv2;
368170159Sariff	if (ce == NULL)
369170159Sariff		return (0xffffffff);
370170159Sariff
371170159Sariff	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
372170159Sariff
373170159Sariff	ce->flags = flags;
374170159Sariff
375170159Sariff	return (ce->flags);
376170159Sariff}
377170159Sariff
378170159Sariff/* Elapsed time conversion to ms */
379170159Sariff#define SND_CLONE_ELAPSED(x, y)						\
380170159Sariff	((((x)->tv_sec - (y)->tv_sec) * 1000) +				\
381170159Sariff	(((y)->tv_nsec > (x)->tv_nsec) ?				\
382170159Sariff	(((1000000000L + (x)->tv_nsec -					\
383170159Sariff	(y)->tv_nsec) / 1000000) - 1000) :				\
384170159Sariff	(((x)->tv_nsec - (y)->tv_nsec) / 1000000)))
385170159Sariff
386170159Sariff#define SND_CLONE_EXPIRED(x, y, z)					\
387170159Sariff	((x)->deadline < 1 ||						\
388170159Sariff	((y)->tv_sec - (z)->tv_sec) > ((x)->deadline / 1000) ||		\
389170159Sariff	SND_CLONE_ELAPSED(y, z) > (x)->deadline)
390170159Sariff
391170159Sariff/*
392170159Sariff * snd_clone_gc() : Garbage collector for stalled, expired objects. Refer to
393170159Sariff * clone.h for explanations on GC settings.
394170159Sariff */
395170159Sariffint
396170159Sariffsnd_clone_gc(struct snd_clone *c)
397170159Sariff{
398170159Sariff	struct snd_clone_entry *ce, *tce;
399170159Sariff	struct timespec now;
400170159Sariff	int pruned;
401170159Sariff
402170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
403170159Sariff
404170159Sariff	if (!(c->flags & SND_CLONE_GC_ENABLE) || c->size == 0)
405170159Sariff		return (0);
406170159Sariff
407170159Sariff	snd_timestamp(&now);
408170159Sariff
409170159Sariff	/*
410170159Sariff	 * Bail out if the last clone handler was invoked below the deadline
411170159Sariff	 * threshold.
412170159Sariff	 */
413170159Sariff	if ((c->flags & SND_CLONE_GC_EXPIRED) &&
414170159Sariff	    !SND_CLONE_EXPIRED(c, &now, &c->tsp))
415170159Sariff		return (0);
416170159Sariff
417170159Sariff	pruned = 0;
418170159Sariff
419170159Sariff	/*
420170159Sariff	 * Visit each object in reverse order. If the object is still being
421170159Sariff	 * referenced by a valid open(), skip it. Look for expired objects
422170159Sariff	 * and either revoke its clone invocation status or mercilessly
423170159Sariff	 * throw it away.
424170159Sariff	 */
425170159Sariff	TAILQ_FOREACH_REVERSE_SAFE(ce, &c->head, link_head, link, tce) {
426170159Sariff		if (!(ce->flags & SND_CLONE_BUSY) &&
427170159Sariff		    (!(ce->flags & SND_CLONE_INVOKE) ||
428170159Sariff		    SND_CLONE_EXPIRED(c, &now, &ce->tsp))) {
429170159Sariff			if ((c->flags & SND_CLONE_GC_REVOKE) ||
430170159Sariff			    ce->devt->si_threadcount != 0) {
431170159Sariff				ce->flags &= ~SND_CLONE_INVOKE;
432170159Sariff				ce->pid = -1;
433170159Sariff			} else {
434170159Sariff				TAILQ_REMOVE(&c->head, ce, link);
435170159Sariff				destroy_dev(ce->devt);
436170159Sariff				free(ce, M_DEVBUF);
437170159Sariff				c->size--;
438170159Sariff			}
439170159Sariff			pruned++;
440170159Sariff		}
441170159Sariff	}
442170159Sariff
443170159Sariff	/* return total pruned objects */
444170159Sariff	return (pruned);
445170159Sariff}
446170159Sariff
447170159Sariffvoid
448170159Sariffsnd_clone_destroy(struct snd_clone *c)
449170159Sariff{
450170719Sariff	struct snd_clone_entry *ce, *tmp;
451170159Sariff
452170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
453170159Sariff
454170719Sariff	ce = TAILQ_FIRST(&c->head);
455170719Sariff	while (ce != NULL) {
456170719Sariff		tmp = TAILQ_NEXT(ce, link);
457170159Sariff		if (ce->devt != NULL)
458170159Sariff			destroy_dev(ce->devt);
459170159Sariff		free(ce, M_DEVBUF);
460170719Sariff		ce = tmp;
461170159Sariff	}
462170159Sariff
463170159Sariff	free(c, M_DEVBUF);
464170159Sariff}
465170159Sariff
466170159Sariff/*
467170159Sariff * snd_clone_acquire() : The vital part of concurrency management. Must be
468170159Sariff * called somewhere at the beginning of open() handler. ENODEV is not really
469170159Sariff * fatal since it just tell the caller that this is not cloned stuff.
470170159Sariff * EBUSY is *real*, don't forget that!
471170159Sariff */
472170159Sariffint
473170159Sariffsnd_clone_acquire(struct cdev *dev)
474170159Sariff{
475170159Sariff	struct snd_clone_entry *ce;
476170159Sariff
477170159Sariff	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
478170159Sariff
479170159Sariff	ce = dev->si_drv2;
480170159Sariff	if (ce == NULL)
481170159Sariff		return (ENODEV);
482170159Sariff
483170159Sariff	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
484170159Sariff
485170159Sariff	ce->flags &= ~SND_CLONE_INVOKE;
486170159Sariff
487170159Sariff	if (ce->flags & SND_CLONE_BUSY)
488170159Sariff		return (EBUSY);
489170159Sariff
490170159Sariff	ce->flags |= SND_CLONE_BUSY;
491170159Sariff
492170159Sariff	return (0);
493170159Sariff}
494170159Sariff
495170159Sariff/*
496170159Sariff * snd_clone_release() : Release busy status. Must be called somewhere at
497170159Sariff * the end of close() handler, or somewhere after fail open().
498170159Sariff */
499170159Sariffint
500170159Sariffsnd_clone_release(struct cdev *dev)
501170159Sariff{
502170159Sariff	struct snd_clone_entry *ce;
503170159Sariff
504170159Sariff	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
505170159Sariff
506170159Sariff	ce = dev->si_drv2;
507170159Sariff	if (ce == NULL)
508170159Sariff		return (ENODEV);
509170159Sariff
510170159Sariff	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
511170159Sariff
512170159Sariff	ce->flags &= ~SND_CLONE_INVOKE;
513170159Sariff
514170159Sariff	if (!(ce->flags & SND_CLONE_BUSY))
515170159Sariff		return (EBADF);
516170159Sariff
517170159Sariff	ce->flags &= ~SND_CLONE_BUSY;
518170159Sariff	ce->pid = -1;
519170159Sariff
520170159Sariff	return (0);
521170159Sariff}
522170159Sariff
523170159Sariff/*
524170159Sariff * snd_clone_ref/unref() : Garbage collector reference counter. To make
525170159Sariff * garbage collector run automatically, the sequence must be something like
526170159Sariff * this (both in open() and close() handlers):
527170159Sariff *
528170159Sariff *  open() - 1) snd_clone_acquire()
529170159Sariff *           2) .... check check ... if failed, snd_clone_release()
530170159Sariff *           3) Success. Call snd_clone_ref()
531170159Sariff *
532170159Sariff * close() - 1) .... check check check ....
533170159Sariff *           2) Success. snd_clone_release()
534170159Sariff *           3) snd_clone_unref() . Garbage collector will run at this point
535170159Sariff *              if this is the last referenced object.
536170159Sariff */
537170159Sariffint
538170159Sariffsnd_clone_ref(struct cdev *dev)
539170159Sariff{
540170159Sariff	struct snd_clone_entry *ce;
541170159Sariff	struct snd_clone *c;
542170159Sariff
543170159Sariff	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
544170159Sariff
545170159Sariff	ce = dev->si_drv2;
546170159Sariff	if (ce == NULL)
547170159Sariff		return (0);
548170159Sariff
549170159Sariff	c = ce->parent;
550170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL parent"));
551170159Sariff	SND_CLONE_ASSERT(c->refcount >= 0, ("refcount < 0"));
552170159Sariff
553170159Sariff	return (++c->refcount);
554170159Sariff}
555170159Sariff
556170159Sariffint
557170159Sariffsnd_clone_unref(struct cdev *dev)
558170159Sariff{
559170159Sariff	struct snd_clone_entry *ce;
560170159Sariff	struct snd_clone *c;
561170159Sariff
562170159Sariff	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
563170159Sariff
564170159Sariff	ce = dev->si_drv2;
565170159Sariff	if (ce == NULL)
566170159Sariff		return (0);
567170159Sariff
568170159Sariff	c = ce->parent;
569170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL parent"));
570170159Sariff	SND_CLONE_ASSERT(c->refcount > 0, ("refcount <= 0"));
571170159Sariff
572170159Sariff	c->refcount--;
573170159Sariff
574170159Sariff	/*
575170159Sariff	 * Run automatic garbage collector, if needed.
576170159Sariff	 */
577170159Sariff	if ((c->flags & SND_CLONE_GC_UNREF) &&
578170159Sariff	    (!(c->flags & SND_CLONE_GC_LASTREF) ||
579170159Sariff	    (c->refcount == 0 && (c->flags & SND_CLONE_GC_LASTREF))))
580170159Sariff		(void)snd_clone_gc(c);
581170159Sariff
582170159Sariff	return (c->refcount);
583170159Sariff}
584170159Sariff
585170159Sariffvoid
586170159Sariffsnd_clone_register(struct snd_clone_entry *ce, struct cdev *dev)
587170159Sariff{
588170159Sariff	SND_CLONE_ASSERT(ce != NULL, ("NULL snd_clone_entry"));
589170159Sariff	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
590170159Sariff	SND_CLONE_ASSERT(dev->si_drv2 == NULL, ("dev->si_drv2 not NULL"));
591170159Sariff	SND_CLONE_ASSERT((ce->flags & SND_CLONE_ALLOC) == SND_CLONE_ALLOC,
592170159Sariff	    ("invalid clone alloc flags=0x%08x", ce->flags));
593170159Sariff	SND_CLONE_ASSERT(ce->devt == NULL, ("ce->devt not NULL"));
594170159Sariff	SND_CLONE_ASSERT(ce->unit == dev2unit(dev),
595170159Sariff	    ("invalid unit ce->unit=0x%08x dev2unit=0x%08x",
596170159Sariff	    ce->unit, dev2unit(dev)));
597170159Sariff
598170159Sariff	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
599170159Sariff
600170159Sariff	dev->si_drv2 = ce;
601170159Sariff	ce->devt = dev;
602170159Sariff	ce->flags &= ~SND_CLONE_ALLOC;
603170159Sariff	ce->flags |= SND_CLONE_INVOKE;
604170159Sariff}
605170159Sariff
606170159Sariffstruct snd_clone_entry *
607170159Sariffsnd_clone_alloc(struct snd_clone *c, struct cdev **dev, int *unit, int tmask)
608170159Sariff{
609170159Sariff	struct snd_clone_entry *ce, *after, *bce, *cce, *nce, *tce;
610170159Sariff	struct timespec now;
611170159Sariff	int cunit, allocunit;
612170159Sariff	pid_t curpid;
613170159Sariff
614170159Sariff	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
615170159Sariff	SND_CLONE_ASSERT(dev != NULL, ("NULL dev pointer"));
616170159Sariff	SND_CLONE_ASSERT((c->typemask & tmask) == tmask,
617170159Sariff	    ("invalid tmask: typemask=0x%08x tmask=0x%08x",
618170159Sariff	    c->typemask, tmask));
619170159Sariff	SND_CLONE_ASSERT(unit != NULL, ("NULL unit pointer"));
620170159Sariff	SND_CLONE_ASSERT(*unit == -1 || !(*unit & (c->typemask | tmask)),
621170159Sariff	    ("typemask collision: typemask=0x%08x tmask=0x%08x *unit=%d",
622170159Sariff	    c->typemask, tmask, *unit));
623170159Sariff
624170159Sariff	if (!(c->flags & SND_CLONE_ENABLE) ||
625170159Sariff	    (*unit != -1 && *unit > c->maxunit))
626170159Sariff		return (NULL);
627170159Sariff
628170159Sariff	ce = NULL;
629170159Sariff	after = NULL;
630170159Sariff	bce = NULL;	/* "b"usy candidate */
631170159Sariff	cce = NULL;	/* "c"urthread/proc candidate */
632170159Sariff	nce = NULL;	/* "n"ull, totally unbusy candidate */
633170159Sariff	tce = NULL;	/* Last "t"ry candidate */
634170159Sariff	cunit = 0;
635170159Sariff	allocunit = (*unit == -1) ? 0 : *unit;
636170159Sariff	curpid = curthread->td_proc->p_pid;
637170159Sariff
638170159Sariff	snd_timestamp(&now);
639170159Sariff
640170159Sariff	TAILQ_FOREACH(ce, &c->head, link) {
641170159Sariff		/*
642170159Sariff		 * Sort incrementally according to device type.
643170159Sariff		 */
644170159Sariff		if (tmask > (ce->unit & c->typemask)) {
645170159Sariff			if (cunit == 0)
646170159Sariff				after = ce;
647170159Sariff			continue;
648170159Sariff		} else if (tmask < (ce->unit & c->typemask))
649170159Sariff			break;
650170159Sariff
651170159Sariff		/*
652170159Sariff		 * Shoot.. this is where the grumpiness begin. Just
653170159Sariff		 * return immediately.
654170159Sariff		 */
655170159Sariff		if (*unit != -1 && *unit == (ce->unit & ~tmask))
656170159Sariff			goto snd_clone_alloc_out;
657170159Sariff
658170159Sariff		cunit++;
659170159Sariff		/*
660170159Sariff		 * Simmilar device type. Sort incrementally according
661170159Sariff		 * to allocation unit. While here, look for free slot
662170159Sariff		 * and possible collision for new / future allocation.
663170159Sariff		 */
664170159Sariff		if (*unit == -1 && (ce->unit & ~tmask) == allocunit)
665170159Sariff			allocunit++;
666170159Sariff		if ((ce->unit & ~tmask) < allocunit)
667170159Sariff			after = ce;
668170159Sariff		/*
669170159Sariff		 * Clone logic:
670170159Sariff		 *   1. Look for non busy, but keep track of the best
671170159Sariff		 *      possible busy cdev.
672170159Sariff		 *   2. Look for the best (oldest referenced) entry that is
673170159Sariff		 *      in a same process / thread.
674170159Sariff		 *   3. Look for the best (oldest referenced), absolute free
675170159Sariff		 *      entry.
676170159Sariff		 *   4. Lastly, look for the best (oldest referenced)
677170159Sariff		 *      any entries that doesn't fit with anything above.
678170159Sariff		 */
679170159Sariff		if (ce->flags & SND_CLONE_BUSY) {
680170159Sariff			if (ce->devt != NULL && (bce == NULL ||
681170159Sariff			    timespeccmp(&ce->tsp, &bce->tsp, <)))
682170159Sariff				bce = ce;
683170159Sariff			continue;
684170159Sariff		}
685170159Sariff		if (ce->pid == curpid &&
686170159Sariff		    (cce == NULL || timespeccmp(&ce->tsp, &cce->tsp, <)))
687170159Sariff			cce = ce;
688170159Sariff		else if (!(ce->flags & SND_CLONE_INVOKE) &&
689170159Sariff		    (nce == NULL || timespeccmp(&ce->tsp, &nce->tsp, <)))
690170159Sariff			nce = ce;
691170159Sariff		else if (tce == NULL || timespeccmp(&ce->tsp, &tce->tsp, <))
692170159Sariff			tce = ce;
693170159Sariff	}
694170159Sariff	if (*unit != -1)
695170159Sariff		goto snd_clone_alloc_new;
696170159Sariff	else if (cce != NULL) {
697170159Sariff		/* Same proc entry found, go for it */
698170159Sariff		ce = cce;
699170159Sariff		goto snd_clone_alloc_out;
700170159Sariff	} else if (nce != NULL) {
701170159Sariff		/*
702170159Sariff		 * Next, try absolute free entry. If the calculated
703170159Sariff		 * allocunit is smaller, create new entry instead.
704170159Sariff		 */
705170159Sariff		if (allocunit < (nce->unit & ~tmask))
706170159Sariff			goto snd_clone_alloc_new;
707170159Sariff		ce = nce;
708170159Sariff		goto snd_clone_alloc_out;
709170159Sariff	} else if (allocunit > c->maxunit) {
710170159Sariff		/*
711170159Sariff		 * Maximum allowable unit reached. Try returning any
712170159Sariff		 * available cdev and hope for the best. If the lookup is
713170159Sariff		 * done for things like stat(), mtime() etc. , things should
714170159Sariff		 * be ok. Otherwise, open() handler should do further checks
715170159Sariff		 * and decide whether to return correct error code or not.
716170159Sariff		 */
717170159Sariff		if (tce != NULL) {
718170159Sariff			ce = tce;
719170159Sariff			goto snd_clone_alloc_out;
720170159Sariff		} else if (bce != NULL) {
721170159Sariff			ce = bce;
722170159Sariff			goto snd_clone_alloc_out;
723170159Sariff		}
724170159Sariff		return (NULL);
725170159Sariff	}
726170159Sariff
727170159Sariffsnd_clone_alloc_new:
728170159Sariff	/*
729170159Sariff	 * No free entries found, and we still haven't reached maximum
730170159Sariff	 * allowable units. Allocate, setup a minimal unique entry with busy
731170719Sariff	 * status so nobody will monkey on this new entry. Unit magic is set
732170719Sariff	 * right here to avoid collision with other contesting handler.
733170719Sariff	 * The caller must be carefull here to maintain its own
734170719Sariff	 * synchronization, as long as it will not conflict with malloc(9)
735170719Sariff	 * operations.
736170719Sariff	 *
737170719Sariff	 * That said, go figure.
738170159Sariff	 */
739170719Sariff	ce = malloc(sizeof(*ce), M_DEVBUF,
740170719Sariff	    ((c->flags & SND_CLONE_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
741170159Sariff	if (ce == NULL) {
742170159Sariff		if (*unit != -1)
743170159Sariff			return (NULL);
744170159Sariff		/*
745170159Sariff		 * We're being dense, ignorance is bliss,
746170159Sariff		 * Super Regulatory Measure (TM).. TRY AGAIN!
747170159Sariff		 */
748170159Sariff		if (nce != NULL) {
749170159Sariff			ce = nce;
750170159Sariff			goto snd_clone_alloc_out;
751170159Sariff		} else if (tce != NULL) {
752170159Sariff			ce = tce;
753170159Sariff			goto snd_clone_alloc_out;
754170159Sariff		} else if (bce != NULL) {
755170159Sariff			ce = bce;
756170159Sariff			goto snd_clone_alloc_out;
757170159Sariff		}
758170159Sariff		return (NULL);
759170159Sariff	}
760170159Sariff	/* Setup new entry */
761170159Sariff	ce->parent = c;
762170159Sariff	ce->unit = tmask | allocunit;
763170159Sariff	ce->pid = curpid;
764170159Sariff	ce->tsp = now;
765170159Sariff	ce->flags |= SND_CLONE_ALLOC;
766170159Sariff	if (after != NULL) {
767170159Sariff		TAILQ_INSERT_AFTER(&c->head, after, ce, link);
768170159Sariff	} else {
769170159Sariff		TAILQ_INSERT_HEAD(&c->head, ce, link);
770170159Sariff	}
771170159Sariff	c->size++;
772170159Sariff	c->tsp = now;
773170159Sariff	/*
774170159Sariff	 * Save new allocation unit for caller which will be used
775170159Sariff	 * by make_dev().
776170159Sariff	 */
777170159Sariff	*unit = allocunit;
778170159Sariff
779170159Sariff	return (ce);
780170159Sariff
781170159Sariffsnd_clone_alloc_out:
782170159Sariff	/*
783170159Sariff	 * Set, mark, timestamp the entry if this is a truly free entry.
784170159Sariff	 * Leave busy entry alone.
785170159Sariff	 */
786170159Sariff	if (!(ce->flags & SND_CLONE_BUSY)) {
787170159Sariff		ce->pid = curpid;
788170159Sariff		ce->tsp = now;
789170159Sariff		ce->flags |= SND_CLONE_INVOKE;
790170159Sariff	}
791170159Sariff	c->tsp = now;
792170159Sariff	*dev = ce->devt;
793170159Sariff
794170159Sariff	return (NULL);
795170159Sariff}
796