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