xref: /illumos-gate/usr/src/uts/common/io/devpool.c (revision 19397407)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 #include <sys/types.h>
28 #include <sys/file.h>
29 #include <sys/errno.h>
30 #include <sys/open.h>
31 #include <sys/cred.h>
32 #include <sys/conf.h>
33 #include <sys/modctl.h>
34 #include <sys/stat.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/policy.h>
38 #include <sys/pool.h>
39 #include <sys/pool_impl.h>
40 
41 /*
42  * The kernel pools subsystem is accessed and manipulated through the pool
43  * device, which has two minor nodes /dev/pool, and /dev/poolctl.  User
44  * processes can comminicate with pools through ioctls on these devices.
45  *
46  * The poolctl device (POOL_CTL_PARENT) can be used to modify and take
47  * snapshot of the current configuration.  Only one process on the system
48  * can have it open at any given time.  This device is also used to enable
49  * or disable pools.  If pools are disabled, the pool driver can be unloaded
50  * and completely removed from the system.
51  *
52  * The pool "info" device (POOL_INFO_PARENT) can only be used to obtain
53  * snapshots of the current configuration and change/query pool bindings.
54  * While some reconfiguration transaction via the poolctl device is in
55  * progress, all processes using this "info" device will be provided with
56  * the snapshot taken at the beginning of that transaction.
57  */
58 
59 #define	POOL_CTL_PARENT		0
60 #define	POOL_INFO_PARENT	1
61 
62 static dev_info_t *pool_devi;	/* pool device information */
63 static int pool_openctl;	/* poolctl device is already open */
64 
65 /*ARGSUSED*/
66 static int
pool_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)67 pool_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
68 {
69 	int error = DDI_FAILURE;
70 
71 	switch (infocmd) {
72 	case DDI_INFO_DEVT2DEVINFO:
73 		*result = pool_devi;
74 		error = DDI_SUCCESS;
75 		break;
76 	case DDI_INFO_DEVT2INSTANCE:
77 		/*
78 		 * All dev_t's map to the same, single instance.
79 		 */
80 		*result = NULL;
81 		error = DDI_SUCCESS;
82 		break;
83 	default:
84 		break;
85 	}
86 	return (error);
87 }
88 
89 static int
pool_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)90 pool_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
91 {
92 	int ret = DDI_SUCCESS;
93 
94 	switch (cmd) {
95 	case DDI_DETACH:
96 		pool_lock();
97 		if (pool_state == POOL_ENABLED) {
98 			ret = DDI_FAILURE;
99 			pool_unlock();
100 			break;
101 		}
102 		ddi_remove_minor_node(devi, NULL);
103 		pool_devi = NULL;
104 		pool_unlock();
105 		break;
106 	default:
107 		ret = DDI_FAILURE;
108 	}
109 	return (ret);
110 }
111 
112 static int
pool_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)113 pool_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
114 {
115 	switch (cmd) {
116 	case DDI_ATTACH:
117 		if (pool_devi != NULL)
118 			return (DDI_FAILURE);
119 		if (ddi_create_minor_node(devi, "poolctl", S_IFCHR,
120 		    POOL_CTL_PARENT, DDI_PSEUDO, 0) == DDI_FAILURE ||
121 		    ddi_create_minor_node(devi, "pool", S_IFCHR,
122 		    POOL_INFO_PARENT, DDI_PSEUDO, 0) == DDI_FAILURE) {
123 			ddi_remove_minor_node(devi, NULL);
124 			return (DDI_FAILURE);
125 		}
126 		pool_devi = devi;
127 		ddi_report_dev(devi);
128 		break;
129 	case DDI_RESUME:
130 		break;
131 	default:
132 		return (DDI_FAILURE);
133 	}
134 	return (DDI_SUCCESS);
135 
136 }
137 
138 /*
139  * There is only one instance of the pool control device, poolctl,
140  * and multiple instances of the pool info device, pool.
141  */
142 /*ARGSUSED*/
143 static int
pool_open(dev_t * devp,int flag,int otype,cred_t * credp)144 pool_open(dev_t *devp, int flag, int otype, cred_t *credp)
145 {
146 	minor_t minor = getminor(*devp);
147 
148 	if (otype != OTYP_CHR)
149 		return (EINVAL);
150 
151 	switch (minor) {
152 	case POOL_CTL_PARENT:
153 		if (secpolicy_pool(CRED()) != 0)
154 			return (EPERM);
155 		if (pool_lock_intr() != 0)
156 			return (EINTR);
157 		if (pool_openctl == 1) {
158 			pool_unlock();
159 			return (EBUSY);
160 		}
161 		pool_openctl = 1;
162 		pool_unlock();
163 		break;
164 	case POOL_INFO_PARENT:
165 		break;
166 	default:
167 		return (ENXIO);
168 	}
169 	return (0);
170 }
171 
172 /*ARGSUSED*/
173 static int
pool_close(dev_t dev,int flag,int otype,cred_t * credp)174 pool_close(dev_t dev, int flag, int otype, cred_t *credp)
175 {
176 	if (otype != OTYP_CHR)
177 		return (EINVAL);
178 	if (getminor(dev) == 0) {
179 		/*
180 		 * We could be closing the poolctl device without finishing
181 		 * the commit transaction first, so do that now.
182 		 */
183 		pool_lock();
184 		(void) pool_commit(0);	/* cannot fail since arg is 0 */
185 		pool_openctl = 0;
186 		pool_unlock();
187 	}
188 	return (0);
189 }
190 
191 /*
192  * Main pool interface.
193  */
194 /* ARGSUSED4 */
195 static int
pool_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)196 pool_ioctl(dev_t dev, int cmd, intptr_t arg, int  mode, cred_t *credp,
197     int *rvalp)
198 {
199 	pool_xtransfer_t xtransfer;
200 	pool_transfer_t transfer;
201 	pool_destroy_t destroy;
202 	pool_propget_t propget;
203 	pool_propput_t propput;
204 	pool_proprm_t proprm;
205 	pool_status_t status;
206 	pool_dissoc_t dissoc;
207 	pool_create_t create;
208 	pool_assoc_t assoc;
209 	pool_bindq_t bindq;
210 	pool_query_t query;
211 	pool_bind_t bind;
212 #ifdef	_MULTI_DATAMODEL
213 	pool_xtransfer32_t xtransfer32;
214 	pool_propput32_t propput32;
215 	pool_propget32_t propget32;
216 	pool_proprm32_t proprm32;
217 	pool_query32_t query32;
218 #endif	/* _MULTI_DATAMODEL */
219 	char *kbuf = NULL;
220 	size_t kbufsz = 0;
221 	int snapshot = 0;
222 	char *prop_name;
223 	size_t size = 0;
224 	nvlist_t *list;
225 	nvpair_t *pair;
226 	char *listbuf;
227 	minor_t minor;
228 	uint_t model;
229 	id_t *id_buf;
230 	int ret = 0;
231 
232 	model = ddi_model_convert_from(mode & FMODELS);
233 	minor = getminor(dev);
234 
235 	/*
236 	 * Check basic permissions first.
237 	 */
238 	switch (cmd) {
239 	case POOL_STATUS:
240 	case POOL_CREATE:
241 	case POOL_ASSOC:
242 	case POOL_DISSOC:
243 	case POOL_DESTROY:
244 	case POOL_TRANSFER:
245 	case POOL_XTRANSFER:
246 	case POOL_PROPPUT:
247 	case POOL_PROPRM:
248 	case POOL_COMMIT:
249 		if (minor != POOL_CTL_PARENT)
250 			return (EINVAL);
251 		/*FALLTHROUGH*/
252 	case POOL_BIND:
253 		if (secpolicy_pool(CRED()) != 0)
254 			return (EPERM);
255 		break;
256 	}
257 
258 	switch (cmd) {
259 	case POOL_STATUS:
260 		if (ddi_copyin((void *)arg, &status,
261 		    sizeof (pool_status_t), mode) != 0)
262 			return (EFAULT);
263 		if (pool_lock_intr() != 0)
264 			return (EINTR);
265 		ret = pool_status(status.ps_io_state);
266 		pool_unlock();
267 		break;
268 	case POOL_STATUSQ:
269 		/*
270 		 * No need to grab pool_lock() to look at the current state.
271 		 */
272 		status.ps_io_state = pool_state;
273 		if (ddi_copyout(&status, (void *)arg,
274 		    sizeof (pool_status_t), mode) != 0)
275 			return (EFAULT);
276 		break;
277 	case POOL_QUERY:
278 		switch (model) {
279 #ifdef _MULTI_DATAMODEL
280 		case DDI_MODEL_ILP32:
281 			if (ddi_copyin((void *)arg, &query32,
282 			    sizeof (pool_query32_t), mode) != 0)
283 				return (EFAULT);
284 			query.pq_io_bufsize = query32.pq_io_bufsize;
285 			query.pq_io_buf = (char *)(uintptr_t)query32.pq_io_buf;
286 			break;
287 #endif	/* _MULTI_DATAMODEL */
288 		default:
289 		case DDI_MODEL_NONE:
290 			if (ddi_copyin((void *)arg, &query,
291 			    sizeof (pool_query_t), mode) != 0)
292 				return (EFAULT);
293 		}
294 		if (pool_lock_intr() != 0)
295 			return (EINTR);
296 		if (pool_state == POOL_DISABLED) {
297 			pool_unlock();
298 			return (ENOTACTIVE);
299 		}
300 		if (minor != 0 && pool_buf != NULL) {
301 			/*
302 			 * Return last snapshot if some
303 			 * transaction is still in progress
304 			 */
305 			if (kbufsz != 0 && pool_bufsz > kbufsz) {
306 				pool_unlock();
307 				return (ENOMEM);
308 			}
309 			kbuf = pool_buf;
310 			kbufsz = size = pool_bufsz;
311 			snapshot = 1;
312 		} else if (query.pq_io_bufsize != 0) {
313 			kbufsz = query.pq_io_bufsize;
314 			kbuf = kmem_alloc(kbufsz, KM_NOSLEEP);
315 			if (kbuf == NULL) {
316 				pool_unlock();
317 				return (ENOMEM);
318 			}
319 			ret = pool_pack_conf(kbuf, kbufsz, &size);
320 		} else {
321 			ret = pool_pack_conf(NULL, 0, &size);
322 		}
323 		if (ret == 0) {
324 			switch (model) {
325 #ifdef	_MULTI_DATAMODEL
326 			case DDI_MODEL_ILP32:
327 				query32.pq_io_bufsize = size;
328 				if (ddi_copyout((caddr_t)&query32, (void *)arg,
329 				    sizeof (pool_query32_t), mode) != 0)
330 					ret = EFAULT;
331 				break;
332 #endif	/* _MULTI_DATAMODEL */
333 			default:
334 			case DDI_MODEL_NONE:
335 				query.pq_io_bufsize = size;
336 				if (ddi_copyout(&query, (void *)arg,
337 				    sizeof (pool_query_t), mode) != 0)
338 					ret = EFAULT;
339 			}
340 			if (ret == 0 && query.pq_io_buf != NULL &&
341 			    ddi_copyout(kbuf, query.pq_io_buf, size, mode) != 0)
342 				ret = EFAULT;
343 		}
344 		pool_unlock();
345 		if (snapshot == 0)
346 			kmem_free(kbuf, kbufsz);
347 		break;
348 	case POOL_CREATE:
349 		if (ddi_copyin((void *)arg,
350 		    &create, sizeof (pool_create_t), mode) != 0)
351 			return (EFAULT);
352 		if (pool_lock_intr() != 0)
353 			return (EINTR);
354 		ret = pool_create(create.pc_o_type,
355 		    create.pc_o_sub_type, &create.pc_i_id);
356 		pool_unlock();
357 		if (ret == 0 && ddi_copyout(&create, (void *)arg,
358 		    sizeof (pool_create_t), mode) != 0)
359 			ret = EFAULT;
360 		break;
361 	case POOL_ASSOC:
362 		if (ddi_copyin((void *)arg, &assoc,
363 		    sizeof (pool_assoc_t), mode) != 0)
364 			return (EFAULT);
365 		if (pool_lock_intr() != 0)
366 			return (EINTR);
367 		ret = pool_assoc(assoc.pa_o_pool_id,
368 		    assoc.pa_o_id_type, assoc.pa_o_res_id);
369 		pool_unlock();
370 		break;
371 	case POOL_DISSOC:
372 		if (ddi_copyin((void *)arg, &dissoc,
373 		    sizeof (pool_dissoc_t), mode) != 0)
374 			return (EFAULT);
375 		if (pool_lock_intr() != 0)
376 			return (EINTR);
377 		ret = pool_dissoc(dissoc.pd_o_pool_id, dissoc.pd_o_id_type);
378 		pool_unlock();
379 		break;
380 	case POOL_DESTROY:
381 		if (ddi_copyin((void *)arg, &destroy,
382 		    sizeof (pool_destroy_t), mode) != 0)
383 			return (EFAULT);
384 		if (pool_lock_intr() != 0)
385 			return (EINTR);
386 		ret = pool_destroy(destroy.pd_o_type, destroy.pd_o_sub_type,
387 		    destroy.pd_o_id);
388 		pool_unlock();
389 		break;
390 	case POOL_TRANSFER:
391 		if (ddi_copyin((void *)arg, &transfer,
392 		    sizeof (pool_transfer_t), mode) != 0)
393 			return (EFAULT);
394 		if (pool_lock_intr() != 0)
395 			return (EINTR);
396 		ret = pool_transfer(transfer.pt_o_id_type, transfer.pt_o_src_id,
397 		    transfer.pt_o_tgt_id, transfer.pt_o_qty);
398 		pool_unlock();
399 		break;
400 	case POOL_XTRANSFER:
401 		switch (model) {
402 #ifdef _MULTI_DATAMODEL
403 		case DDI_MODEL_ILP32:
404 			if (ddi_copyin((void *)arg, &xtransfer32,
405 			    sizeof (pool_xtransfer32_t), mode) != 0)
406 				return (EFAULT);
407 			xtransfer.px_o_id_type = xtransfer32.px_o_id_type;
408 			xtransfer.px_o_src_id = xtransfer32.px_o_src_id;
409 			xtransfer.px_o_tgt_id = xtransfer32.px_o_tgt_id;
410 			xtransfer.px_o_complist_size =
411 			    xtransfer32.px_o_complist_size;
412 			xtransfer.px_o_comp_list =
413 			    (id_t *)(uintptr_t)xtransfer32.px_o_comp_list;
414 			break;
415 #endif /* _MULTI_DATAMODEL */
416 		default:
417 		case DDI_MODEL_NONE:
418 			if (ddi_copyin((void *)arg, &xtransfer,
419 			    sizeof (pool_xtransfer_t), mode) != 0)
420 				return (EFAULT);
421 		}
422 		/*
423 		 * Copy in IDs to transfer from the userland
424 		 */
425 		if (xtransfer.px_o_complist_size > POOL_IDLIST_SIZE)
426 			return (EINVAL);
427 		id_buf = kmem_alloc(xtransfer.px_o_complist_size *
428 		    sizeof (id_t), KM_SLEEP);
429 		if (ddi_copyin((void *)xtransfer.px_o_comp_list, id_buf,
430 		    xtransfer.px_o_complist_size * sizeof (id_t), mode) != 0) {
431 			kmem_free(id_buf, xtransfer.px_o_complist_size *
432 			    sizeof (id_t));
433 			return (EFAULT);
434 		}
435 		if (pool_lock_intr() != 0) {
436 			kmem_free(id_buf, xtransfer.px_o_complist_size *
437 			    sizeof (id_t));
438 			return (EINTR);
439 		}
440 		ret = pool_xtransfer(xtransfer.px_o_id_type,
441 		    xtransfer.px_o_src_id, xtransfer.px_o_tgt_id,
442 		    xtransfer.px_o_complist_size, id_buf);
443 		pool_unlock();
444 		kmem_free(id_buf, xtransfer.px_o_complist_size *
445 		    sizeof (id_t));
446 		break;
447 	case POOL_BIND:
448 		if (ddi_copyin((void *)arg, &bind,
449 		    sizeof (pool_bind_t), mode) != 0)
450 			return (EFAULT);
451 		if (pool_lock_intr() != 0)
452 			return (EINTR);
453 		ret = pool_bind(bind.pb_o_pool_id, bind.pb_o_id_type,
454 		    bind.pb_o_id);
455 		pool_unlock();
456 		break;
457 	case POOL_BINDQ:
458 		if (ddi_copyin((void *)arg, &bindq,
459 		    sizeof (pool_bindq_t), mode) != 0) {
460 			return (EFAULT);
461 		}
462 		if (pool_lock_intr() != 0)
463 			return (EINTR);
464 		if ((ret = pool_query_binding(bindq.pb_o_id_type,
465 		    bindq.pb_o_id, &bindq.pb_i_id)) == 0 &&
466 		    ddi_copyout(&bindq, (void *)arg,
467 		    sizeof (pool_bindq_t), mode) != 0)
468 			ret = EFAULT;
469 		pool_unlock();
470 		break;
471 	case POOL_PROPGET:
472 		switch (model) {
473 #ifdef _MULTI_DATAMODEL
474 		case DDI_MODEL_ILP32:
475 			if (ddi_copyin((void *)arg, &propget32,
476 			    sizeof (pool_propget32_t), mode) != 0)
477 				return (EFAULT);
478 			propget.pp_o_id = propget32.pp_o_id;
479 			propget.pp_o_id_type = propget32.pp_o_id_type;
480 			propget.pp_o_id_subtype = propget32.pp_o_id_subtype;
481 			propget.pp_o_prop_name =
482 			    (char *)(uintptr_t)propget32.pp_o_prop_name;
483 			propget.pp_o_prop_name_size =
484 			    propget32.pp_o_prop_name_size;
485 			propget.pp_i_buf =
486 			    (char *)(uintptr_t)propget32.pp_i_buf;
487 			propget.pp_i_bufsize = propget32.pp_i_bufsize;
488 			break;
489 #endif	/* _MULTI_DATAMODEL */
490 		default:
491 		case DDI_MODEL_NONE:
492 			if (ddi_copyin((void *)arg, &propget,
493 			    sizeof (pool_propget_t), mode) != 0)
494 				return (EFAULT);
495 		}
496 		if (propget.pp_o_prop_name_size + 1 > POOL_PROPNAME_SIZE)
497 			return (EINVAL);
498 		prop_name = kmem_alloc(propget.pp_o_prop_name_size + 1,
499 		    KM_SLEEP);
500 		if (ddi_copyin(propget.pp_o_prop_name, prop_name,
501 		    propget.pp_o_prop_name_size + 1, mode) != 0) {
502 			kmem_free(prop_name, propget.pp_o_prop_name_size + 1);
503 			return (EFAULT);
504 		}
505 		list = NULL;
506 		if (pool_lock_intr() != 0) {
507 			kmem_free(prop_name, propget.pp_o_prop_name_size + 1);
508 			return (EINTR);
509 		}
510 		ret = pool_propget(prop_name, propget.pp_o_id_type,
511 		    propget.pp_o_id_subtype, propget.pp_o_id, &list);
512 		pool_unlock();
513 		kmem_free(prop_name, propget.pp_o_prop_name_size + 1);
514 		if (ret != 0)
515 			return (ret);
516 		ret = nvlist_pack(list, &kbuf, &kbufsz, NV_ENCODE_NATIVE, 0);
517 		if (ret != 0) {
518 			nvlist_free(list);
519 			return (ret);
520 		}
521 		switch (model) {
522 #ifdef	_MULTI_DATAMODEL
523 		case DDI_MODEL_ILP32:
524 			propget32.pp_i_bufsize = kbufsz;
525 			if (ddi_copyout((caddr_t)&propget32, (void *)arg,
526 			    sizeof (pool_propget32_t), mode) != 0)
527 				ret = EFAULT;
528 			break;
529 #endif	/* _MULTI_DATAMODEL */
530 		default:
531 		case DDI_MODEL_NONE:
532 			if (ddi_copyout(&propget, (void *)arg,
533 			    sizeof (pool_propget_t), mode) != 0)
534 				ret = EFAULT;
535 		}
536 		if (ret == 0) {
537 			if (propget.pp_i_buf == NULL) {
538 				ret = 0;
539 			} else if (propget.pp_i_bufsize >= kbufsz) {
540 				if (ddi_copyout(kbuf, propget.pp_i_buf,
541 				    kbufsz, mode) != 0)
542 					ret = EFAULT;
543 			} else {
544 				ret = ENOMEM;
545 			}
546 		}
547 		kmem_free(kbuf, kbufsz);
548 		nvlist_free(list);
549 		break;
550 	case POOL_PROPPUT:
551 		switch (model) {
552 #ifdef _MULTI_DATAMODEL
553 		case DDI_MODEL_ILP32:
554 			if (ddi_copyin((void *)arg, &propput32,
555 			    sizeof (pool_propput32_t), mode) != 0)
556 				return (EFAULT);
557 			propput.pp_o_id_type = propput32.pp_o_id_type;
558 			propput.pp_o_id_sub_type = propput32.pp_o_id_sub_type;
559 			propput.pp_o_id = propput32.pp_o_id;
560 			propput.pp_o_bufsize = propput32.pp_o_bufsize;
561 			propput.pp_o_buf =
562 			    (char *)(uintptr_t)propput32.pp_o_buf;
563 			break;
564 #endif	/* _MULTI_DATAMODEL */
565 		default:
566 		case DDI_MODEL_NONE:
567 			if (ddi_copyin((void *)arg, &propput,
568 			    sizeof (pool_propput_t), mode) != 0)
569 				return (EFAULT);
570 		}
571 		if (propput.pp_o_bufsize > POOL_PROPBUF_SIZE)
572 			return (EINVAL);
573 		listbuf = kmem_alloc(propput.pp_o_bufsize, KM_SLEEP);
574 		if (ddi_copyin(propput.pp_o_buf, listbuf,
575 		    propput.pp_o_bufsize, mode) != 0) {
576 			kmem_free(listbuf, propput.pp_o_bufsize);
577 			return (EFAULT);
578 		}
579 		if (nvlist_unpack(listbuf, propput.pp_o_bufsize,
580 		    &list, KM_SLEEP) != 0) {
581 			kmem_free(listbuf, propput.pp_o_bufsize);
582 			return (EFAULT);
583 		}
584 		if (pool_lock_intr() != 0) {
585 			nvlist_free(list);
586 			kmem_free(listbuf, propput.pp_o_bufsize);
587 			return (EINTR);
588 		}
589 		/*
590 		 * Extract the nvpair from the list. The list may
591 		 * contain multiple properties.
592 		 */
593 		for (pair = nvlist_next_nvpair(list, NULL); pair != NULL;
594 		    pair = nvlist_next_nvpair(list, pair)) {
595 			if ((ret = pool_propput(propput.pp_o_id_type,
596 			    propput.pp_o_id_sub_type,
597 			    propput.pp_o_id, pair)) != 0)
598 				break;
599 		}
600 		pool_unlock();
601 		nvlist_free(list);
602 		kmem_free(listbuf, propput.pp_o_bufsize);
603 		break;
604 	case POOL_PROPRM:
605 		switch (model) {
606 #ifdef _MULTI_DATAMODEL
607 		case DDI_MODEL_ILP32:
608 			if (ddi_copyin((void *)arg, &proprm32,
609 			    sizeof (pool_proprm32_t), mode) != 0)
610 				return (EFAULT);
611 			proprm.pp_o_id_type = proprm32.pp_o_id_type;
612 			proprm.pp_o_id_sub_type = proprm32.pp_o_id_sub_type;
613 			proprm.pp_o_id = proprm32.pp_o_id;
614 			proprm.pp_o_prop_name_size =
615 			    proprm32.pp_o_prop_name_size;
616 			proprm.pp_o_prop_name =
617 			    (void *)(uintptr_t)proprm32.pp_o_prop_name;
618 			break;
619 #endif	/* _MULTI_DATAMODEL */
620 		default:
621 		case DDI_MODEL_NONE:
622 			if (ddi_copyin((void *)arg, &proprm,
623 			    sizeof (pool_proprm_t), mode) != 0)
624 				return (EFAULT);
625 		}
626 		if (proprm.pp_o_prop_name_size + 1 > POOL_PROPNAME_SIZE)
627 			return (EINVAL);
628 		prop_name = kmem_alloc(proprm.pp_o_prop_name_size + 1,
629 		    KM_SLEEP);
630 		if (ddi_copyin(proprm.pp_o_prop_name, prop_name,
631 		    proprm.pp_o_prop_name_size + 1, mode) != 0) {
632 			kmem_free(prop_name, proprm.pp_o_prop_name_size + 1);
633 			return (EFAULT);
634 		}
635 		if (pool_lock_intr() != 0) {
636 			kmem_free(prop_name, proprm.pp_o_prop_name_size + 1);
637 			return (EINTR);
638 		}
639 		ret = pool_proprm(proprm.pp_o_id_type,
640 		    proprm.pp_o_id_sub_type, proprm.pp_o_id, prop_name);
641 		pool_unlock();
642 		kmem_free(prop_name, proprm.pp_o_prop_name_size + 1);
643 		break;
644 	case POOL_COMMIT:
645 		if (pool_lock_intr() != 0)
646 			return (EINTR);
647 		ret = pool_commit((int)arg);
648 		pool_unlock();
649 		break;
650 	default:
651 		return (EINVAL);
652 	}
653 	return (ret);
654 }
655 
656 static struct cb_ops pool_cb_ops = {
657 	pool_open,		/* open */
658 	pool_close,		/* close */
659 	nodev,			/* strategy */
660 	nodev,			/* print */
661 	nodev,			/* dump */
662 	nodev,			/* read */
663 	nodev,			/* write */
664 	pool_ioctl,		/* ioctl */
665 	nodev,			/* devmap */
666 	nodev,			/* mmap */
667 	nodev,			/* segmap */
668 	nochpoll,		/* poll */
669 	nodev,			/* cb_prop_op */
670 	(struct streamtab *)0,	/* streamtab */
671 	D_NEW | D_MP		/* driver compatibility flags */
672 };
673 
674 static struct dev_ops pool_ops = {
675 	DEVO_REV,		/* devo_rev */
676 	0,			/* refcnt */
677 	pool_info,		/* info */
678 	nulldev,		/* identify */
679 	nulldev,		/* probe */
680 	pool_attach,		/* attach */
681 	pool_detach,		/* detach */
682 	nodev,			/* reset */
683 	&pool_cb_ops,		/* cb_ops */
684 	(struct bus_ops *)NULL,	/* bus_ops */
685 	nulldev,		/* power */
686 	ddi_quiesce_not_needed,		/* quiesce */
687 };
688 
689 /*
690  * Module linkage information for the kernel
691  */
692 static struct modldrv modldrv = {
693 	&mod_driverops,		/* this one is a pseudo driver */
694 	"pool driver",
695 	&pool_ops
696 };
697 
698 static struct modlinkage modlinkage = {
699 	MODREV_1,
700 	&modldrv,
701 	NULL
702 };
703 
704 int
_init(void)705 _init(void)
706 {
707 	return (mod_install(&modlinkage));
708 }
709 
710 int
_fini(void)711 _fini(void)
712 {
713 	return (mod_remove(&modlinkage));
714 }
715 
716 int
_info(struct modinfo * modinfop)717 _info(struct modinfo *modinfop)
718 {
719 	return (mod_info(&modlinkage, modinfop));
720 }
721