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
62static dev_info_t *pool_devi;	/* pool device information */
63static int pool_openctl;	/* poolctl device is already open */
64
65/*ARGSUSED*/
66static int
67pool_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
89static int
90pool_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
112static int
113pool_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*/
143static int
144pool_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*/
173static int
174pool_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 */
195static int
196pool_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
656static 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
674static 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 */
692static struct modldrv modldrv = {
693	&mod_driverops,		/* this one is a pseudo driver */
694	"pool driver",
695	&pool_ops
696};
697
698static struct modlinkage modlinkage = {
699	MODREV_1,
700	&modldrv,
701	NULL
702};
703
704int
705_init(void)
706{
707	return (mod_install(&modlinkage));
708}
709
710int
711_fini(void)
712{
713	return (mod_remove(&modlinkage));
714}
715
716int
717_info(struct modinfo *modinfop)
718{
719	return (mod_info(&modlinkage, modinfop));
720}
721