xref: /titanic_50/usr/src/uts/common/avs/ns/nsctl/nsctl.c (revision a1e9eea083a8f257157edb8a1efb5bbd300eb4bf)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/ksynch.h>
28 #include <sys/kmem.h>
29 #include <sys/file.h>
30 #include <sys/errno.h>
31 #include <sys/open.h>
32 #include <sys/cred.h>
33 #include <sys/conf.h>
34 #include <sys/uio.h>
35 #include <sys/cmn_err.h>
36 #include <sys/modctl.h>
37 #include <sys/ddi.h>
38 
39 #define	__NSC_GEN__
40 #include <sys/nsctl/nsc_dev.h>
41 #include <sys/nsctl/nsc_gen.h>
42 #include <sys/nsctl/nsc_ioctl.h>
43 #include <sys/nsctl/nsc_power.h>
44 #include <sys/nsctl/nsc_mem.h>
45 #include "../nsctl.h"
46 
47 #include <sys/nsctl/nsvers.h>
48 
49 #ifdef DS_DDICT
50 #include "../contract.h"
51 #endif
52 
53 extern void nscsetup();
54 extern int _nsc_init_raw();
55 extern void _nsc_deinit_raw();
56 extern void _nsc_init_start();
57 extern void _nsc_init_os(), _nsc_deinit_os();
58 extern void _nsc_init_dev(), _nsc_init_mem();
59 extern void _nsc_init_gen(), _nsc_init_rmlock();
60 extern void _nsc_init_resv(), _nsc_deinit_resv();
61 extern void _nsc_init_frz(), _nsc_deinit_frz();
62 extern void _nsc_init_ncio(), _nsc_deinit_ncio();
63 extern void _nsc_deinit_mem(), _nsc_deinit_rmlock();
64 extern void _nsc_deinit_dev();
65 
66 extern int _nsc_frz_start(), _nsc_frz_stop(), _nsc_frz_isfrozen();
67 
68 extern nsc_mem_t *_nsc_local_mem;
69 extern nsc_rmhdr_t *_nsc_rmhdr_ptr;
70 extern nsc_def_t _nsc_raw_def[];
71 extern int _nsc_raw_flags;
72 
73 int nsc_devflag = D_MP;
74 
75 int _nsc_init_done = 0;
76 
77 kmutex_t _nsc_drv_lock;
78 nsc_io_t *_nsc_file_io;
79 nsc_io_t *_nsc_vchr_io;
80 nsc_io_t *_nsc_raw_io;
81 
82 nsc_fd_t **_nsc_minor_fd;
83 kmutex_t **_nsc_minor_slp;
84 
85 
86 /* Maximum number of devices - tunable in nsctl.conf */
87 static int _nsc_max_devices;
88 
89 /* Internal version of _nsc_max_devices */
90 int _nsc_maxdev;
91 
92 extern void _nsc_global_setup(void);
93 
94 static int nsc_load(), nsc_unload();
95 static void nscteardown();
96 
97 /*
98  * Solaris specific driver module interface code.
99  */
100 
101 extern int nscopen(dev_t *, int, int, cred_t *);
102 extern int nscioctl(dev_t, int, intptr_t, int, cred_t *, int *);
103 extern int nscclose(dev_t, int, int, cred_t *);
104 extern int nscread(dev_t, uio_t *, cred_t *);
105 extern int nscwrite(dev_t, uio_t *, cred_t *);
106 
107 static dev_info_t *nsctl_dip;		/* Single DIP for driver */
108 
109 static int _nsctl_print(dev_t, char *);
110 
111 static	struct	cb_ops nsctl_cb_ops = {
112 	nscopen,		/* open */
113 	nscclose,	/* close */
114 	nodev,		/* not a block driver, strategy not an entry point */
115 	_nsctl_print,	/* no print routine */
116 	nodev,		/* no dump routine */
117 	nscread,		/* read */
118 	nscwrite,	/* write */
119 	(int (*)()) nscioctl,	/* ioctl */
120 	nodev,		/* no devmap routine */
121 	nodev,		/* no mmap routine */
122 	nodev,		/* no segmap routine */
123 	nochpoll,	/* no chpoll routine */
124 	ddi_prop_op,
125 	0,		/* not a STREAMS driver, no cb_str routine */
126 	D_NEW | D_MP | D_64BIT,	/* safe for multi-thread/multi-processor */
127 	CB_REV,
128 	nodev,		/* aread */
129 	nodev,		/* awrite */
130 };
131 
132 static int _nsctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
133 static int _nsctl_attach(dev_info_t *, ddi_attach_cmd_t);
134 static int _nsctl_detach(dev_info_t *, ddi_detach_cmd_t);
135 
136 static	struct	dev_ops nsctl_ops = {
137 	DEVO_REV,			/* Driver build version */
138 	0,				/* device reference count */
139 	_nsctl_getinfo,
140 	nulldev,			/* Identify */
141 	nulldev,			/* Probe */
142 	_nsctl_attach,
143 	_nsctl_detach,
144 	nodev,				/* Reset */
145 	&nsctl_cb_ops,
146 	(struct bus_ops *)0
147 };
148 
149 static struct modldrv nsctl_ldrv = {
150 	&mod_driverops,
151 	"nws:Control:" ISS_VERSION_STR,
152 	&nsctl_ops
153 };
154 
155 static	struct modlinkage nsctl_modlinkage = {
156 	MODREV_1,
157 	&nsctl_ldrv,
158 	NULL
159 };
160 
161 /*
162  * Solaris module load time code
163  */
164 
165 int nsc_min_nodeid;
166 int nsc_max_nodeid;
167 
168 int
169 _init(void)
170 {
171 	int err;
172 
173 	err = nsc_load();
174 
175 	if (!err)
176 		err = mod_install(&nsctl_modlinkage);
177 
178 	if (err) {
179 		(void) nsc_unload();
180 		cmn_err(CE_NOTE, "nsctl_init: err %d", err);
181 	}
182 
183 	return (err);
184 
185 }
186 
187 /*
188  * Solaris module unload time code
189  */
190 
191 int
192 _fini(void)
193 {
194 	int err;
195 
196 	if ((err = mod_remove(&nsctl_modlinkage)) == 0) {
197 		err = nsc_unload();
198 	}
199 	return (err);
200 }
201 
202 /*
203  * Solaris module info code
204  */
205 int
206 _info(struct modinfo *modinfop)
207 {
208 	return (mod_info(&nsctl_modlinkage, modinfop));
209 }
210 
211 /*
212  * Attach an instance of the device. This happens before an open
213  * can succeed.
214  */
215 static int
216 _nsctl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
217 {
218 	int rc;
219 
220 	if (cmd == DDI_ATTACH) {
221 		nsctl_dip = dip;
222 
223 		/* Announce presence of the device */
224 		ddi_report_dev(dip);
225 
226 		/*
227 		 * Get the node parameters now that we can look up.
228 		 */
229 		nsc_min_nodeid = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
230 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
231 		    "nsc_min_nodeid", 0);
232 
233 		nsc_max_nodeid = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
234 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
235 		    "nsc_max_nodeid", 5);
236 
237 		_nsc_max_devices = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
238 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
239 		    "nsc_max_devices", 128);
240 
241 		_nsc_maxdev = _nsc_max_devices;
242 		nscsetup();
243 
244 		/*
245 		 * Init raw requires the _nsc_max_devices value and so
246 		 * cannot be done before the nsc_max_devices property has
247 		 * been read which can only be done after the module is
248 		 * attached and we have a dip.
249 		 */
250 
251 		if ((rc = _nsc_init_raw(_nsc_max_devices)) != 0) {
252 			cmn_err(CE_WARN,
253 			    "nsctl: unable to initialize raw io provider: %d",
254 			    rc);
255 			return (DDI_FAILURE);
256 		}
257 
258 		/*
259 		 * Init rest of soft state structure
260 		 */
261 
262 		rc = ddi_create_minor_node(dip, "c,nsctl", S_IFCHR, 0,
263 		    DDI_PSEUDO, 0);
264 		if (rc != DDI_SUCCESS) {
265 			/* free anything we allocated here */
266 			cmn_err(CE_WARN,
267 			    "_nsctl_attach: ddi_create_minor_node failed %d",
268 			    rc);
269 			return (DDI_FAILURE);
270 		}
271 
272 		/* Announce presence of the device */
273 		ddi_report_dev(dip);
274 
275 		/* mark the device as attached, opens may proceed */
276 		return (DDI_SUCCESS);
277 	} else
278 		return (DDI_FAILURE);
279 }
280 
281 static int
282 _nsctl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
283 {
284 	if (cmd == DDI_DETACH) {
285 		nscteardown();
286 		_nsc_deinit_raw();
287 
288 		ddi_remove_minor_node(dip, NULL);
289 		nsctl_dip = NULL;
290 
291 		return (DDI_SUCCESS);
292 	}
293 	else
294 		return (DDI_FAILURE);
295 }
296 
297 
298 /* ARGSUSED */
299 static int
300 _nsctl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
301 {
302 	dev_t dev;
303 	int rc;
304 
305 	switch (cmd) {
306 		case DDI_INFO_DEVT2INSTANCE:
307 			/* The "instance" number is the minor number */
308 			dev = (dev_t)arg;
309 			*result = (void *)(unsigned long)getminor(dev);
310 			rc = DDI_SUCCESS;
311 			break;
312 
313 		case DDI_INFO_DEVT2DEVINFO:
314 			*result = nsctl_dip;
315 			rc = DDI_SUCCESS;
316 			break;
317 
318 		default:
319 			rc = DDI_FAILURE;
320 			break;
321 	}
322 
323 	return (rc);
324 }
325 
326 
327 /* ARGSUSED */
328 static int
329 _nsctl_print(dev_t dev, char *s)
330 {
331 	cmn_err(CE_WARN, "nsctl:%s", s);
332 	return (0);
333 }
334 
335 
336 void
337 nsc_init()
338 {
339 	if (_nsc_init_done)
340 		return;
341 
342 	_nsc_init_start();
343 	_nsc_init_gen();
344 	_nsc_init_svc();
345 	_nsc_init_mem();
346 	_nsc_init_dev();
347 	_nsc_init_rmlock();
348 	_nsc_init_resv();
349 	_nsc_init_os();
350 	(void) _nsc_init_power();
351 
352 	/*
353 	 * When using mc, nscsetup is done through mc callback to global_init.
354 	 */
355 	nscsetup();
356 
357 	mutex_init(&_nsc_drv_lock, NULL, MUTEX_DRIVER, NULL);
358 
359 	_nsc_raw_io = nsc_register_io("raw",
360 	    NSC_RAW_ID | _nsc_raw_flags, _nsc_raw_def);
361 
362 	if (!_nsc_raw_io)
363 		cmn_err(CE_WARN, "_nsc_init: register io failed - raw");
364 
365 	_nsc_init_ncio();
366 	_nsc_init_frz();
367 
368 	_nsc_init_done = 1;
369 }
370 
371 
372 /*
373  * Called after the mc refresh is complete (SEG_INIT callbacks have
374  * been received) and module _attach() is done.  Only does any real
375  * work when all of the above conditions have been met.
376  */
377 void
378 nscsetup()
379 {
380 	if (nsc_max_devices() == 0 || _nsc_minor_fd != NULL)
381 		return;
382 
383 	_nsc_minor_fd = nsc_kmem_zalloc(sizeof (nsc_fd_t *)*_nsc_maxdev,
384 	    0, _nsc_local_mem);
385 
386 	if (!_nsc_minor_fd) {
387 		cmn_err(CE_WARN, "nscsetup - alloc failed");
388 		return;
389 	}
390 
391 	_nsc_minor_slp = nsc_kmem_zalloc(sizeof (kmutex_t *)*_nsc_maxdev,
392 	    0, _nsc_local_mem);
393 
394 	if (!_nsc_minor_slp)  {
395 		cmn_err(CE_WARN, "nscsetup - alloc failed");
396 		nsc_kmem_free(_nsc_minor_fd, sizeof (nsc_fd_t *) * _nsc_maxdev);
397 		_nsc_minor_fd = (nsc_fd_t **)NULL;
398 	}
399 }
400 
401 static void
402 nscteardown()
403 {
404 	int i;
405 
406 	if (_nsc_minor_fd == NULL)
407 		return;
408 
409 #ifdef DEBUG
410 	/* Check all devices were closed.  Index 0 is the prototype dev. */
411 	for (i = 1; i < _nsc_maxdev; i++) {
412 		ASSERT(_nsc_minor_slp[i] == NULL);
413 		ASSERT(_nsc_minor_fd[i] == NULL);
414 	}
415 #endif /* DEBUG */
416 
417 	nsc_kmem_free(_nsc_minor_fd, sizeof (nsc_fd_t *) * _nsc_maxdev);
418 	nsc_kmem_free(_nsc_minor_slp, sizeof (kmutex_t *) * _nsc_maxdev);
419 
420 	_nsc_minor_fd = (nsc_fd_t **)NULL;
421 	_nsc_minor_slp = (kmutex_t **)NULL;
422 }
423 
424 int
425 nsc_load()
426 {
427 	nsc_init();
428 	return (0);
429 }
430 
431 
432 int
433 nsc_unload()
434 {
435 	if (!_nsc_init_done) {
436 		return (0);
437 	}
438 
439 	nscteardown();
440 
441 	(void) _nsc_deinit_power();
442 	_nsc_deinit_resv();
443 	_nsc_deinit_mem();
444 	_nsc_deinit_rmlock();
445 	_nsc_deinit_svc();
446 	_nsc_deinit_frz();
447 	_nsc_deinit_ncio();
448 
449 	if (_nsc_vchr_io)
450 		(void) nsc_unregister_io(_nsc_vchr_io, 0);
451 
452 	if (_nsc_file_io)
453 		(void) nsc_unregister_io(_nsc_file_io, 0);
454 
455 	_nsc_vchr_io = NULL;
456 	_nsc_file_io = NULL;
457 
458 	if (_nsc_raw_io)
459 		(void) nsc_unregister_io(_nsc_raw_io, 0);
460 
461 	_nsc_raw_io = NULL;
462 
463 	_nsc_deinit_dev();
464 	_nsc_deinit_os();
465 
466 	_nsc_init_done = 0;
467 	return (0);
468 }
469 
470 
471 /* ARGSUSED */
472 
473 int
474 nscopen(dev_t *devp, int flag, int otyp, cred_t *crp)
475 {
476 	kmutex_t *slp;
477 	int i, error;
478 
479 	if (error = drv_priv(crp))
480 		return (error);
481 
482 	if (!_nsc_minor_fd || !_nsc_minor_slp)
483 		return (ENXIO);
484 
485 	if (getminor(*devp) != 0)
486 		return (ENXIO);
487 
488 	slp = nsc_kmem_alloc(sizeof (kmutex_t), 0, _nsc_local_mem);
489 	mutex_init(slp, NULL, MUTEX_DRIVER, NULL);
490 
491 	mutex_enter(&_nsc_drv_lock);
492 
493 	for (i = 1; i < _nsc_maxdev; i++) {
494 		if (_nsc_minor_slp[i] == NULL) {
495 			_nsc_minor_slp[i] = slp;
496 			break;
497 		}
498 	}
499 
500 	mutex_exit(&_nsc_drv_lock);
501 
502 	if (i >= _nsc_maxdev) {
503 		mutex_destroy(slp);
504 		nsc_kmem_free(slp, sizeof (kmutex_t));
505 		return (EAGAIN);
506 	}
507 
508 	*devp = makedevice(getmajor(*devp), i);
509 
510 	return (0);
511 }
512 
513 
514 int
515 _nscopen(dev_t dev, intptr_t arg, int mode, int *rvp)
516 {
517 	minor_t mindev = getminor(dev);
518 	struct nscioc_open *op;
519 	nsc_fd_t *fd;
520 	int rc;
521 
522 	op = nsc_kmem_alloc(sizeof (*op), KM_SLEEP, _nsc_local_mem);
523 	if (op == NULL) {
524 		return (ENOMEM);
525 	}
526 
527 	if (ddi_copyin((void *)arg, op, sizeof (*op), mode) < 0) {
528 		nsc_kmem_free(op, sizeof (*op));
529 		return (EFAULT);
530 	}
531 
532 	mutex_enter(_nsc_minor_slp[mindev]);
533 
534 	if (_nsc_minor_fd[mindev]) {
535 		mutex_exit(_nsc_minor_slp[mindev]);
536 		nsc_kmem_free(op, sizeof (*op));
537 		return (EBUSY);
538 	}
539 
540 	op->path[sizeof (op->path)-1] = 0;
541 
542 	fd = nsc_open(op->path, (op->flag & NSC_TYPES), 0, 0, &rc);
543 
544 	if (fd == NULL) {
545 		mutex_exit(_nsc_minor_slp[mindev]);
546 		nsc_kmem_free(op, sizeof (*op));
547 		return (rc);
548 	}
549 
550 	mode |= (op->mode - FOPEN);
551 
552 	if (mode & (FWRITE|FEXCL)) {
553 		if ((rc = nsc_reserve(fd, NSC_PCATCH)) != 0) {
554 			mutex_exit(_nsc_minor_slp[mindev]);
555 			(void) nsc_close(fd);
556 			nsc_kmem_free(op, sizeof (*op));
557 			return (rc);
558 		}
559 	}
560 
561 	*rvp = 0;
562 	_nsc_minor_fd[mindev] = fd;
563 
564 	mutex_exit(_nsc_minor_slp[mindev]);
565 	nsc_kmem_free(op, sizeof (*op));
566 	return (0);
567 }
568 
569 
570 /* ARGSUSED */
571 
572 int
573 nscclose(dev_t dev, int flag, int otyp, cred_t *crp)
574 {
575 	minor_t mindev = getminor(dev);
576 	kmutex_t *slp;
577 	nsc_fd_t *fd;
578 
579 	if (!_nsc_minor_fd || !_nsc_minor_slp)
580 		return (0);
581 
582 	if ((slp = _nsc_minor_slp[mindev]) == 0)
583 		return (0);
584 
585 	if ((fd = _nsc_minor_fd[mindev]) != NULL)
586 		(void) nsc_close(fd);
587 
588 	_nsc_minor_fd[mindev] = NULL;
589 	_nsc_minor_slp[mindev] = NULL;
590 
591 	mutex_destroy(slp);
592 	nsc_kmem_free(slp, sizeof (kmutex_t));
593 	return (0);
594 }
595 
596 
597 /* ARGSUSED */
598 
599 int
600 nscread(dev_t dev, uio_t *uiop, cred_t *crp)
601 {
602 	minor_t mindev = getminor(dev);
603 	int rc, resv;
604 	nsc_fd_t *fd;
605 
606 	if ((fd = _nsc_minor_fd[mindev]) == 0)
607 		return (EIO);
608 
609 	mutex_enter(_nsc_minor_slp[mindev]);
610 
611 	resv = (nsc_held(fd) == 0);
612 
613 	if (resv && (rc = nsc_reserve(fd, NSC_PCATCH)) != 0) {
614 		mutex_exit(_nsc_minor_slp[mindev]);
615 		return (rc);
616 	}
617 
618 	rc = nsc_uread(fd, uiop, crp);
619 
620 	if (resv)
621 		nsc_release(fd);
622 
623 	mutex_exit(_nsc_minor_slp[mindev]);
624 	return (rc);
625 }
626 
627 
628 /* ARGSUSED */
629 
630 int
631 nscwrite(dev_t dev, uio_t *uiop, cred_t *crp)
632 {
633 	minor_t mindev = getminor(dev);
634 	int rc, resv;
635 	nsc_fd_t *fd;
636 
637 	if ((fd = _nsc_minor_fd[mindev]) == 0)
638 		return (EIO);
639 
640 	mutex_enter(_nsc_minor_slp[mindev]);
641 
642 	resv = (nsc_held(fd) == 0);
643 
644 	if (resv && (rc = nsc_reserve(fd, NSC_PCATCH)) != 0) {
645 		mutex_exit(_nsc_minor_slp[mindev]);
646 		return (rc);
647 	}
648 
649 	rc = nsc_uwrite(fd, uiop, crp);
650 
651 	if (resv)
652 		nsc_release(fd);
653 
654 	mutex_exit(_nsc_minor_slp[mindev]);
655 	return (rc);
656 }
657 
658 
659 int
660 _nscreserve(dev_t dev, int *rvp)
661 {
662 	minor_t mindev = getminor(dev);
663 	nsc_fd_t *fd;
664 	int rc;
665 
666 	if ((fd = _nsc_minor_fd[mindev]) == 0)
667 		return (EIO);
668 
669 	mutex_enter(_nsc_minor_slp[mindev]);
670 
671 	if (nsc_held(fd)) {
672 		mutex_exit(_nsc_minor_slp[mindev]);
673 		return (EBUSY);
674 	}
675 
676 	if ((rc = nsc_reserve(fd, NSC_PCATCH)) != 0) {
677 		mutex_exit(_nsc_minor_slp[mindev]);
678 		return (rc);
679 	}
680 
681 	*rvp = 0;
682 
683 	mutex_exit(_nsc_minor_slp[mindev]);
684 	return (0);
685 }
686 
687 
688 int
689 _nscrelease(dev_t dev, int *rvp)
690 {
691 	minor_t mindev = getminor(dev);
692 	nsc_fd_t *fd;
693 
694 	if ((fd = _nsc_minor_fd[mindev]) == 0)
695 		return (EIO);
696 
697 	mutex_enter(_nsc_minor_slp[mindev]);
698 
699 	if (!nsc_held(fd)) {
700 		mutex_exit(_nsc_minor_slp[mindev]);
701 		return (EINVAL);
702 	}
703 
704 	nsc_release(fd);
705 
706 	*rvp = 0;
707 
708 	mutex_exit(_nsc_minor_slp[mindev]);
709 	return (0);
710 }
711 
712 
713 int
714 _nscpartsize(dev_t dev, intptr_t arg, int mode)
715 {
716 	struct nscioc_partsize partsize;
717 	minor_t mindev = getminor(dev);
718 	nsc_size_t size;
719 	int rc, resv;
720 	nsc_fd_t *fd;
721 
722 	if ((fd = _nsc_minor_fd[mindev]) == 0)
723 		return (EIO);
724 
725 	mutex_enter(_nsc_minor_slp[mindev]);
726 
727 	resv = (nsc_held(fd) == 0);
728 
729 	if (resv && (rc = nsc_reserve(fd, NSC_PCATCH)) != 0) {
730 		mutex_exit(_nsc_minor_slp[mindev]);
731 		return (rc);
732 	}
733 
734 	rc = nsc_partsize(fd, &size);
735 	partsize.partsize = (uint64_t)size;
736 
737 	if (resv)
738 		nsc_release(fd);
739 
740 	mutex_exit(_nsc_minor_slp[mindev]);
741 
742 	if (ddi_copyout((void *)&partsize, (void *)arg,
743 	    sizeof (partsize), mode) < 0) {
744 		return (EFAULT);
745 	}
746 
747 	return (rc);
748 }
749 
750 
751 /* ARGSUSED */
752 
753 int
754 nscioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *crp, int *rvp)
755 {
756 	struct nscioc_bsize *bsize = NULL;
757 	char *path = NULL;
758 	int rc = 0;
759 
760 	*rvp = 0;
761 
762 	switch (cmd) {
763 	case NSCIOC_OPEN:
764 		rc = _nscopen(dev, arg, mode, rvp);
765 		break;
766 
767 	case NSCIOC_RESERVE:
768 		rc = _nscreserve(dev, rvp);
769 		break;
770 
771 	case NSCIOC_RELEASE:
772 		rc = _nscrelease(dev, rvp);
773 		break;
774 
775 	case NSCIOC_PARTSIZE:
776 		rc = _nscpartsize(dev, arg, mode);
777 		break;
778 
779 	case NSCIOC_FREEZE:
780 		path = nsc_kmem_alloc(NSC_MAXPATH, KM_SLEEP, _nsc_local_mem);
781 		if (path == NULL) {
782 			rc = ENOMEM;
783 			break;
784 		}
785 		if (ddi_copyin((void *)arg, path, NSC_MAXPATH, mode) < 0)
786 			rc = EFAULT;
787 		else {
788 			path[NSC_MAXPATH-1] = 0;
789 			rc = _nsc_frz_start(path, rvp);
790 		}
791 		break;
792 
793 	case NSCIOC_UNFREEZE:
794 		path = nsc_kmem_alloc(NSC_MAXPATH, KM_SLEEP, _nsc_local_mem);
795 		if (path == NULL) {
796 			rc = ENOMEM;
797 			break;
798 		}
799 		if (ddi_copyin((void *)arg, path, NSC_MAXPATH, mode) < 0)
800 			rc = EFAULT;
801 		else {
802 			path[NSC_MAXPATH-1] = 0;
803 			rc = _nsc_frz_stop(path, rvp);
804 		}
805 		break;
806 
807 	case NSCIOC_ISFROZEN:
808 		path = nsc_kmem_alloc(NSC_MAXPATH, KM_SLEEP, _nsc_local_mem);
809 		if (path == NULL) {
810 			rc = ENOMEM;
811 			break;
812 		}
813 		if (ddi_copyin((void *)arg, path, NSC_MAXPATH, mode) < 0)
814 			rc = EFAULT;
815 		else {
816 			path[NSC_MAXPATH-1] = 0;
817 			rc = _nsc_frz_isfrozen(path, rvp);
818 		}
819 		break;
820 
821 #ifdef ENABLE_POWER_MSG
822 	case NSCIOC_POWERMSG:
823 		rc = _nsc_power((void *)arg, rvp);
824 		break;
825 #endif
826 
827 	case NSCIOC_NSKERND:
828 		rc = nskernd_command(arg, mode, rvp);
829 		break;
830 
831 	/* return sizes of global memory segments */
832 	case NSCIOC_GLOBAL_SIZES:
833 		if (!_nsc_init_done) {
834 			rc = EINVAL;
835 			break;
836 		}
837 
838 		rc = _nsc_get_global_sizes((void *)arg, rvp);
839 
840 		break;
841 
842 	/* return contents of global segments */
843 	case NSCIOC_GLOBAL_DATA:
844 		if (!_nsc_init_done) {
845 			rc = EINVAL;
846 			break;
847 		}
848 
849 		rc = _nsc_get_global_data((void *)arg, rvp);
850 		break;
851 
852 	/*
853 	 * nvmem systems:
854 	 * clear the hdr dirty bit to prevent loading from nvme on reboot
855 	 */
856 	case NSCIOC_NVMEM_CLEANF:
857 		rc = _nsc_clear_dirty(1);	/* dont be nice about it */
858 		break;
859 	case NSCIOC_NVMEM_CLEAN:
860 		rc = _nsc_clear_dirty(0);
861 		break;
862 
863 	case NSCIOC_BSIZE:
864 		bsize = nsc_kmem_alloc(sizeof (*bsize), KM_SLEEP,
865 		    _nsc_local_mem);
866 		if (bsize == NULL) {
867 			rc = ENOMEM;
868 			break;
869 		}
870 
871 		if (ddi_copyin((void *)arg, bsize, sizeof (*bsize), mode) < 0) {
872 			rc = EFAULT;
873 			break;
874 		}
875 
876 		rc = nskern_bsize(bsize, rvp);
877 		if (rc == 0) {
878 			if (ddi_copyout(bsize, (void *)arg,
879 			    sizeof (*bsize), mode) < 0) {
880 				rc = EFAULT;
881 				break;
882 			}
883 		}
884 
885 		break;
886 
887 	default:
888 		return (ENOTTY);
889 	}
890 
891 	if (bsize != NULL) {
892 		nsc_kmem_free(bsize, sizeof (*bsize));
893 		bsize = NULL;
894 	}
895 	if (path != NULL) {
896 		nsc_kmem_free(path, NSC_MAXPATH);
897 		path = NULL;
898 	}
899 	return (rc);
900 }
901 
902 
903 int
904 nsc_max_devices(void)
905 {
906 	return (_nsc_max_devices);
907 }
908 
909 
910 /*
911  * Used by _nsc_global_setup() in case nvram is dirty and has saved a different
912  * value for nsc_max_devices. We need to use the saved value, not the new
913  * one configured by the user.
914  */
915 void
916 _nsc_set_max_devices(int maxdev)
917 {
918 	_nsc_max_devices = maxdev;
919 	_nsc_maxdev = _nsc_max_devices;
920 }
921