xref: /netbsd-src/external/cddl/osnet/sys/kern/ddi.c (revision d909946ca08dceb44d7d0f22ec9488679695d976)
1 
2 /*
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License, Version 1.0 only
7  * (the "License").  You may not use this file except in compliance
8  * with the License.
9  *
10  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11  * or http://www.opensolaris.org/os/licensing.
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 /*
24  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*	Copyright (c) 1988 AT&T	*/
29 /*	All Rights Reserved */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <sys/types.h>
34 #include <sys/ddi.h>
35 #include <sys/debug.h>
36 #include <sys/errno.h>
37 #include <sys/param.h>
38 #include <sys/lwp.h>
39 #include <sys/kernel.h>
40 #include <sys/kmem.h>
41 #include <sys/cmn_err.h>
42 #include <sys/namei.h>
43 #include <sys/stat.h>
44 #include <sys/vfs_syscalls.h>
45 
46 __strong_alias(ddi_strtol,ddi_strtoul)
47 
48 /*
49  * String to integer conversion routines.
50  *
51  * This file is derived from usr/src/common/util/strtol.c
52  *
53  * We cannot use the user land versions as there is no errno to report
54  * error in kernel.  So the return value is used to return an error,
55  * and the result is stored in an extra parameter passed by reference.
56  * Otherwise, the following functions are identical to the user land
57  * versions.
58  */
59 
60 /*
61  * We should have a kernel version of ctype.h.
62  */
63 #define	isalnum(ch)	(isalpha(ch) || isdigit(ch))
64 #define	isalpha(ch)	(isupper(ch) || islower(ch))
65 #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
66 #define	islower(ch)	((ch) >= 'a' && (ch) <= 'z')
67 #define	isspace(ch)	(((ch) == ' ') || ((ch) == '\r') || ((ch) == '\n') || \
68 			((ch) == '\t') || ((ch) == '\f'))
69 #define	isupper(ch)	((ch) >= 'A' && (ch) <= 'Z')
70 #define	isxdigit(ch)	(isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
71 			((ch) >= 'A' && (ch) <= 'F'))
72 
73 #define	DIGIT(x)	\
74 	(isdigit(x) ? (x) - '0' : islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A')
75 
76 #define	MBASE	('z' - 'a' + 1 + 10)
77 
78 /*
79  * The following macro is a local version of isalnum() which limits
80  * alphabetic characters to the ranges a-z and A-Z; locale dependent
81  * characters will not return 1. The members of a-z and A-Z are
82  * assumed to be in ascending order and contiguous
83  */
84 #define	lisalnum(x)	\
85 	(isdigit(x) || ((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z'))
86 
87 static int
88 do_mkdirp(const char *path)
89 {
90 	struct lwp *l = curlwp;
91 	int mode;
92 	int error;
93 	register_t ret;
94 
95 	const char *s, *e;
96 	char *here;
97 
98 	error = 0;
99 	mode = 493;
100 
101 	if (*path != '/')
102 		panic("Not an absolute path");
103 
104 	here = PNBUF_GET();
105 	for (s = path;; s = e) {
106 		e = strchr(s + 1, '/');
107 		if (e == NULL)
108 			break;
109 
110 		strlcpy(here, path, e - path + 1);
111 		error = do_sys_mkdir((const char *)here, mode, UIO_SYSSPACE);
112 	}
113 	PNBUF_PUT(here);
114 
115 	if (error == EEXIST)
116 		error = 0;
117 
118 	return error;
119 }
120 
121 int
122 ddi_strtoul(const char *str, char **nptr, int base, unsigned long *result)
123 {
124 	unsigned long val;
125 	int c;
126 	int xx;
127 	unsigned long	multmax;
128 	int neg = 0;
129 	const char **ptr = (const char **)nptr;
130 	const unsigned char	*ustr = (const unsigned char *)str;
131 
132 	if (ptr != (const char **)0)
133 		*ptr = (char *)ustr; /* in case no number is formed */
134 	if (base < 0 || base > MBASE || base == 1) {
135 		/* base is invalid -- should be a fatal error */
136 		return (EINVAL);
137 	}
138 	if (!isalnum(c = *ustr)) {
139 		while (isspace(c))
140 			c = *++ustr;
141 		switch (c) {
142 		case '-':
143 			neg++;
144 			/* FALLTHROUGH */
145 		case '+':
146 			c = *++ustr;
147 		}
148 	}
149 	if (base == 0)
150 		if (c != '0')
151 			base = 10;
152 		else if (ustr[1] == 'x' || ustr[1] == 'X')
153 			base = 16;
154 		else
155 			base = 8;
156 	/*
157 	 * for any base > 10, the digits incrementally following
158 	 *	9 are assumed to be "abc...z" or "ABC...Z"
159 	 */
160 	if (!lisalnum(c) || (xx = DIGIT(c)) >= base)
161 		return (EINVAL); /* no number formed */
162 	if (base == 16 && c == '0' && (ustr[1] == 'x' || ustr[1] == 'X') &&
163 	    isxdigit(ustr[2]))
164 		c = *(ustr += 2); /* skip over leading "0x" or "0X" */
165 
166 	multmax = ULONG_MAX / (unsigned long)base;
167 	val = DIGIT(c);
168 	for (c = *++ustr; lisalnum(c) && (xx = DIGIT(c)) < base; ) {
169 		if (val > multmax)
170 			goto overflow;
171 		val *= base;
172 		if (ULONG_MAX - val < xx)
173 			goto overflow;
174 		val += xx;
175 		c = *++ustr;
176 	}
177 	if (ptr != (const char **)0)
178 		*ptr = (char *)ustr;
179 	*result = neg ? -val : val;
180 	return (0);
181 
182 overflow:
183 	for (c = *++ustr; lisalnum(c) && (xx = DIGIT(c)) < base; (c = *++ustr))
184 		;
185 	if (ptr != (const char **)0)
186 		*ptr = (char *)ustr;
187 	return (ERANGE);
188 }
189 
190 /*
191  * Find first bit set in a mask (returned counting from 1 up)
192  */
193 
194 int
195 ddi_ffs(long mask)
196 {
197 	return (ffs(mask));
198 }
199 
200 /*
201  * Find last bit set. Take mask and clear
202  * all but the most significant bit, and
203  * then let ffs do the rest of the work.
204  *
205  * Algorithm courtesy of Steve Chessin.
206  */
207 
208 int
209 ddi_fls(long mask)
210 {
211 	while (mask) {
212 		long nx;
213 
214 		if ((nx = (mask & (mask - 1))) == 0)
215 			break;
216 		mask = nx;
217 	}
218 	return (ffs(mask));
219 }
220 
221 /*
222  * The next five routines comprise generic storage management utilities
223  * for driver soft state structures (in "the old days," this was done
224  * with a statically sized array - big systems and dynamic loading
225  * and unloading make heap allocation more attractive)
226  */
227 
228 /*
229  * Allocate a set of pointers to 'n_items' objects of size 'size'
230  * bytes.  Each pointer is initialized to nil.
231  *
232  * The 'size' and 'n_items' values are stashed in the opaque
233  * handle returned to the caller.
234  *
235  * This implementation interprets 'set of pointers' to mean 'array
236  * of pointers' but note that nothing in the interface definition
237  * precludes an implementation that uses, for example, a linked list.
238  * However there should be a small efficiency gain from using an array
239  * at lookup time.
240  *
241  * NOTE	As an optimization, we make our growable array allocations in
242  *	powers of two (bytes), since that's how much kmem_alloc (currently)
243  *	gives us anyway.  It should save us some free/realloc's ..
244  *
245  *	As a further optimization, we make the growable array start out
246  *	with MIN_N_ITEMS in it.
247  */
248 
249 /*
250  * This data structure is entirely private to the soft state allocator.
251  */
252 struct i_ddi_soft_state {
253 	void		**array;	/* the array of pointers */
254 	kmutex_t	lock;	/* serialize access to this struct */
255 	size_t		size;	/* how many bytes per state struct */
256 	size_t		n_items;	/* how many structs herein */
257 	struct i_ddi_soft_state *next;	/* 'dirty' elements */
258 };
259 
260 #define	MIN_N_ITEMS	8	/* 8 void *'s == 32 bytes */
261 
262 int
263 ddi_soft_state_init(void **state_p, size_t size, size_t n_items)
264 {
265 	struct i_ddi_soft_state *ss;
266 
267 	if (state_p == NULL || *state_p != NULL || size == 0)
268 		return (EINVAL);
269 
270 	ss = kmem_zalloc(sizeof (*ss), KM_SLEEP);
271 	mutex_init(&ss->lock, NULL, MUTEX_DRIVER, NULL);
272 	ss->size = size;
273 
274 	if (n_items < MIN_N_ITEMS)
275 		ss->n_items = MIN_N_ITEMS;
276 	else {
277 		int bitlog;
278 
279 		if ((bitlog = ddi_fls(n_items)) == ddi_ffs(n_items))
280 			bitlog--;
281 		ss->n_items = 1 << bitlog;
282 	}
283 
284 	ASSERT(ss->n_items >= n_items);
285 
286 	ss->array = kmem_zalloc(ss->n_items * sizeof (void *), KM_SLEEP);
287 
288 	*state_p = ss;
289 
290 	return (0);
291 }
292 
293 
294 /*
295  * Allocate a state structure of size 'size' to be associated
296  * with item 'item'.
297  *
298  * In this implementation, the array is extended to
299  * allow the requested offset, if needed.
300  */
301 int
302 ddi_soft_state_zalloc(void *state, int item)
303 {
304 	struct i_ddi_soft_state *ss;
305 	void **array;
306 	void *new_element;
307 
308 	if ((ss = state) == NULL || item < 0)
309 		return (DDI_FAILURE);
310 
311 	mutex_enter(&ss->lock);
312 	if (ss->size == 0) {
313 		mutex_exit(&ss->lock);
314 		cmn_err(CE_WARN, "ddi_soft_state_zalloc: bad handle");
315 		return (DDI_FAILURE);
316 	}
317 
318 	array = ss->array;	/* NULL if ss->n_items == 0 */
319 	ASSERT(ss->n_items != 0 && array != NULL);
320 
321 	/*
322 	 * refuse to tread on an existing element
323 	 */
324 	if (item < ss->n_items && array[item] != NULL) {
325 		mutex_exit(&ss->lock);
326 		return (DDI_FAILURE);
327 	}
328 
329 	/*
330 	 * Allocate a new element to plug in
331 	 */
332 	new_element = kmem_zalloc(ss->size, KM_SLEEP);
333 
334 	/*
335 	 * Check if the array is big enough, if not, grow it.
336 	 */
337 	if (item >= ss->n_items) {
338 		void	**new_array;
339 		size_t	new_n_items;
340 		struct i_ddi_soft_state *dirty;
341 
342 		/*
343 		 * Allocate a new array of the right length, copy
344 		 * all the old pointers to the new array, then
345 		 * if it exists at all, put the old array on the
346 		 * dirty list.
347 		 *
348 		 * Note that we can't kmem_free() the old array.
349 		 *
350 		 * Why -- well the 'get' operation is 'mutex-free', so we
351 		 * can't easily catch a suspended thread that is just about
352 		 * to dereference the array we just grew out of.  So we
353 		 * cons up a header and put it on a list of 'dirty'
354 		 * pointer arrays.  (Dirty in the sense that there may
355 		 * be suspended threads somewhere that are in the middle
356 		 * of referencing them).  Fortunately, we -can- garbage
357 		 * collect it all at ddi_soft_state_fini time.
358 		 */
359 		new_n_items = ss->n_items;
360 		while (new_n_items < (1 + item))
361 			new_n_items <<= 1;	/* double array size .. */
362 
363 		ASSERT(new_n_items >= (1 + item));	/* sanity check! */
364 
365 		new_array = kmem_zalloc(new_n_items * sizeof (void *),
366 		    KM_SLEEP);
367 		/*
368 		 * Copy the pointers into the new array
369 		 */
370 		bcopy(array, new_array, ss->n_items * sizeof (void *));
371 
372 		/*
373 		 * Save the old array on the dirty list
374 		 */
375 		dirty = kmem_zalloc(sizeof (*dirty), KM_SLEEP);
376 		dirty->array = ss->array;
377 		dirty->n_items = ss->n_items;
378 		dirty->next = ss->next;
379 		ss->next = dirty;
380 
381 		ss->array = (array = new_array);
382 		ss->n_items = new_n_items;
383 	}
384 
385 	ASSERT(array != NULL && item < ss->n_items && array[item] == NULL);
386 
387 	array[item] = new_element;
388 
389 	mutex_exit(&ss->lock);
390 	return (DDI_SUCCESS);
391 }
392 
393 
394 /*
395  * Fetch a pointer to the allocated soft state structure.
396  *
397  * This is designed to be cheap.
398  *
399  * There's an argument that there should be more checking for
400  * nil pointers and out of bounds on the array.. but we do a lot
401  * of that in the alloc/free routines.
402  *
403  * An array has the convenience that we don't need to lock read-access
404  * to it c.f. a linked list.  However our "expanding array" strategy
405  * means that we should hold a readers lock on the i_ddi_soft_state
406  * structure.
407  *
408  * However, from a performance viewpoint, we need to do it without
409  * any locks at all -- this also makes it a leaf routine.  The algorithm
410  * is 'lock-free' because we only discard the pointer arrays at
411  * ddi_soft_state_fini() time.
412  */
413 void *
414 ddi_get_soft_state(void *state, int item)
415 {
416 	struct i_ddi_soft_state *ss = state;
417 
418 	ASSERT(ss != NULL && item >= 0);
419 
420 	if (item < ss->n_items && ss->array != NULL)
421 		return (ss->array[item]);
422 	return (NULL);
423 }
424 
425 /*
426  * Free the state structure corresponding to 'item.'   Freeing an
427  * element that has either gone or was never allocated is not
428  * considered an error.  Note that we free the state structure, but
429  * we don't shrink our pointer array, or discard 'dirty' arrays,
430  * since even a few pointers don't really waste too much memory.
431  *
432  * Passing an item number that is out of bounds, or a null pointer will
433  * provoke an error message.
434  */
435 void
436 ddi_soft_state_free(void *state, int item)
437 {
438 	struct i_ddi_soft_state *ss;
439 	void **array;
440 	void *element;
441 	static char msg[] = "ddi_soft_state_free:";
442 
443 	if ((ss = state) == NULL) {
444 		cmn_err(CE_WARN, "%s null handle",
445 		    msg);
446 		return;
447 	}
448 
449 	element = NULL;
450 
451 	mutex_enter(&ss->lock);
452 
453 	if ((array = ss->array) == NULL || ss->size == 0) {
454 		cmn_err(CE_WARN, "%s bad handle",
455 		    msg);
456 	} else if (item < 0 || item >= ss->n_items) {
457 		cmn_err(CE_WARN, "%s item %d not in range [0..%lu]",
458 		    msg, item, ss->n_items - 1);
459 	} else if (array[item] != NULL) {
460 		element = array[item];
461 		array[item] = NULL;
462 	}
463 
464 	mutex_exit(&ss->lock);
465 
466 	if (element)
467 		kmem_free(element, ss->size);
468 }
469 
470 
471 /*
472  * Free the entire set of pointers, and any
473  * soft state structures contained therein.
474  *
475  * Note that we don't grab the ss->lock mutex, even though
476  * we're inspecting the various fields of the data structure.
477  *
478  * There is an implicit assumption that this routine will
479  * never run concurrently with any of the above on this
480  * particular state structure i.e. by the time the driver
481  * calls this routine, there should be no other threads
482  * running in the driver.
483  */
484 void
485 ddi_soft_state_fini(void **state_p)
486 {
487 	struct i_ddi_soft_state *ss, *dirty;
488 	int item;
489 	static char msg[] = "ddi_soft_state_fini:";
490 
491 	if (state_p == NULL || (ss = *state_p) == NULL) {
492 		cmn_err(CE_WARN, "%s null handle",
493 		    msg);
494 		return;
495 	}
496 
497 	if (ss->size == 0) {
498 		cmn_err(CE_WARN, "%s bad handle",
499 		    msg);
500 		return;
501 	}
502 
503 	if (ss->n_items > 0) {
504 		for (item = 0; item < ss->n_items; item++)
505 			ddi_soft_state_free(ss, item);
506 		kmem_free(ss->array, ss->n_items * sizeof (void *));
507 	}
508 
509 	/*
510 	 * Now delete any dirty arrays from previous 'grow' operations
511 	 */
512 	for (dirty = ss->next; dirty; dirty = ss->next) {
513 		ss->next = dirty->next;
514 		kmem_free(dirty->array, dirty->n_items * sizeof (void *));
515 		kmem_free(dirty, sizeof (*dirty));
516 	}
517 
518 	mutex_destroy(&ss->lock);
519 	kmem_free(ss, sizeof (*ss));
520 
521 	*state_p = NULL;
522 }
523 
524 int
525 ddi_create_minor_node(dev_info_t *dip, char *name, int spec_type,
526     minor_t minor_num, char *node_type, int flag)
527 {
528 	struct lwp *l = curlwp;
529 	char *pn;
530 	dev_t dev;
531 	int error;
532 	register_t ret;
533 
534 	printf("ddi_create_minor_node: name %s\n", name);
535 
536 	dev = makedev(flag, minor_num);
537 
538 	pn = PNBUF_GET();
539 	if (spec_type == S_IFCHR)
540 		snprintf(pn, MAXPATHLEN, "/dev/zvol/rdsk/%s", name);
541 	else
542 		snprintf(pn, MAXPATHLEN, "/dev/zvol/dsk/%s", name);
543 
544 	if ((error = do_mkdirp(pn)) != 0)
545 		goto exit;
546 
547 	error = do_sys_mknod(l, (const char *)pn, spec_type, dev, &ret, UIO_SYSSPACE);
548 
549 exit:
550 	PNBUF_PUT(pn);
551 
552 	return error;
553 }
554 
555 void
556 ddi_remove_minor_node(dev_info_t *dip, char *name)
557 {
558 	char *pn;
559 	int error;
560 
561 	pn = PNBUF_GET();
562 	snprintf(pn, MAXPATHLEN, "/dev/zvol/dsk/%s", name);
563 	(void)do_sys_unlink(pn, UIO_SYSSPACE);
564 	PNBUF_PUT(pn);
565 
566 	/* We need to remove raw and block device nodes */
567 	pn = PNBUF_GET();
568 	snprintf(pn, MAXPATHLEN, "/dev/zvol/rdsk/%s", name);
569 	(void)do_sys_unlink(pn, UIO_SYSSPACE);
570 	PNBUF_PUT(pn);
571 }
572 
573 clock_t
574 ddi_get_lbolt()
575 {
576 
577 	return hardclock_ticks;
578 }
579 
580 int64_t
581 ddi_get_lbolt64()
582 {
583 
584 	return hardclock_ticks;
585 }
586