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 /*
23 * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
24 */
25
26 /*
27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30
31 /*
32 * Copyright 2023 Oxide Computer Company
33 */
34
35 #include <sys/errno.h>
36 #include <sys/modctl.h>
37 #include <sys/stat.h>
38 #include <sys/kmem.h>
39 #include <sys/ksynch.h>
40 #include <sys/stream.h>
41 #include <sys/stropts.h>
42 #include <sys/termio.h>
43 #include <sys/ddi.h>
44 #include <sys/file.h>
45 #include <sys/disp.h>
46 #include <sys/sunddi.h>
47 #include <sys/sunldi.h>
48 #include <sys/sunndi.h>
49 #include <sys/kbio.h>
50 #include <sys/prom_plat.h>
51 #include <sys/oplmsu/oplmsu.h>
52 #include <sys/oplmsu/oplmsu_proto.h>
53
54 extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
55
56 #define MOD_ID 0xe145
57 #define MOD_NAME "oplmsu"
58
59 #define META_NAME "oplmsu"
60 #define USER_NAME "a"
61
62 struct module_info oplmsu_mod_info = {
63 MOD_ID,
64 MOD_NAME,
65 0,
66 16384,
67 14336,
68 2048
69 };
70
71 struct qinit oplmsu_urinit = {
72 NULL,
73 oplmsu_ursrv,
74 oplmsu_open,
75 oplmsu_close,
76 NULL,
77 &oplmsu_mod_info,
78 NULL
79 };
80
81 struct qinit oplmsu_uwinit = {
82 oplmsu_uwput,
83 oplmsu_uwsrv,
84 oplmsu_open,
85 oplmsu_close,
86 NULL,
87 &oplmsu_mod_info,
88 NULL
89 };
90
91 struct qinit oplmsu_lrinit = {
92 oplmsu_lrput,
93 oplmsu_lrsrv,
94 oplmsu_open,
95 oplmsu_close,
96 NULL,
97 &oplmsu_mod_info,
98 NULL
99 };
100
101 struct qinit oplmsu_lwinit = {
102 NULL,
103 oplmsu_lwsrv,
104 oplmsu_open,
105 oplmsu_close,
106 NULL,
107 &oplmsu_mod_info,
108 NULL
109 };
110
111 struct streamtab oplmsu_info = {
112 &oplmsu_urinit,
113 &oplmsu_uwinit,
114 &oplmsu_lrinit,
115 &oplmsu_lwinit
116 };
117
118 static struct cb_ops cb_oplmsu_ops = {
119 nulldev, /* cb_open */
120 nulldev, /* cb_close */
121 nodev, /* cb_strategy */
122 nodev, /* cb_print */
123 nodev, /* cb_dump */
124 nodev, /* cb_read */
125 nodev, /* cb_write */
126 nodev, /* cb_ioctl */
127 nodev, /* cb_devmap */
128 nodev, /* cb_mmap */
129 nodev, /* cb_segmap */
130 nochpoll, /* cb_chpoll */
131 ddi_prop_op, /* cb_prop_op */
132 (&oplmsu_info), /* cb_stream */
133 (int)(D_NEW|D_MP|D_HOTPLUG) /* cb_flag */
134 };
135
136 static struct dev_ops oplmsu_ops = {
137 DEVO_REV, /* devo_rev */
138 0, /* devo_refcnt */
139 (oplmsu_getinfo), /* devo_getinfo */
140 (nulldev), /* devo_identify */
141 (nulldev), /* devo_probe */
142 (oplmsu_attach), /* devo_attach */
143 (oplmsu_detach), /* devo_detach */
144 (nodev), /* devo_reset */
145 &(cb_oplmsu_ops), /* devo_cb_ops */
146 (struct bus_ops *)NULL, /* devo_bus_ops */
147 NULL, /* devo_power */
148 ddi_quiesce_not_needed, /* dev_quiesce */
149 };
150
151 struct modldrv modldrv = {
152 &mod_driverops,
153 "OPL serial mux driver",
154 &oplmsu_ops
155 };
156
157 struct modlinkage modlinkage = {
158 MODREV_1,
159 (void *)&modldrv,
160 NULL
161 };
162
163 uinst_t oplmsu_uinst_local; /* upper_instance_table structure */
164 uinst_t *oplmsu_uinst = &oplmsu_uinst_local;
165 int oplmsu_queue_flag; /* Enable/disable queueing flag */
166 int oplmsu_check_su; /* Check super-user flag */
167
168 #ifdef DEBUG
169 int oplmsu_debug_mode = 0; /* Enable/disable debug mode */
170 int oplmsu_trace_on; /* Enable/disable trace */
171 uint_t oplmsu_ltrc_size; /* Trace buffer size */
172 msu_trc_t *oplmsu_ltrc_top; /* Top of trace data area */
173 msu_trc_t *oplmsu_ltrc_tail; /* Tail of trace data area */
174 msu_trc_t *oplmsu_ltrc_cur; /* Current pointer of trace data area */
175 ulong_t oplmsu_ltrc_ccnt; /* Current counter */
176 kmutex_t oplmsu_ltrc_lock; /* Lock table for trace mode */
177 #endif
178
179 /* oplmsu_conf_st */
180 #define MSU_CONFIGURED 2
181 #define MSU_CONFIGURING 1
182 #define MSU_UNCONFIGURED 0
183
184 static kmutex_t oplmsu_bthrd_excl;
185 static kthread_id_t oplmsu_bthrd_id = NULL;
186 static int oplmsu_conf_st = MSU_UNCONFIGURED;
187 static kcondvar_t oplmsu_conf_cv;
188
189
190 /*
191 * Locking hierarcy of oplmsu driver. This driver have 5 locks in uinst_t.
192 *
193 * Each mutex guards as follows.
194 *
195 * uinst_t->lock: This mutex is read/write mutex.
196 * read lock : acquired if the member of uinst_t is refered only.
197 * write lock: acquired if the member of uinst_t is changed.
198 *
199 * uinst_t->u_lock: This mutex is normal mutex.
200 * This mutex is acquired at reading/changing the member of all upath_t.
201 *
202 * uinst_t->l_lock: This mutex is normal mutex.
203 * This mutex is acquired at reading/changing the member of all lpath_t.
204 *
205 * uinst_t->c_lock: This mutex is normal mutex.
206 * This mutex is acquired at reading/changing the member of the ctrl_t.
207 *
208 * oplmsu_bthrd_excl: This mutex is normal mutex.
209 * This mutex is used only to start/stop the configuring thread of the
210 * multiplexed STREAMS.
211 * This mutex is exclusively acquired with the above-mentioned 4 mutexes.
212 *
213 * To guard of the deadlock by cross locking, the base locking hierarcy
214 * is as follows:
215 *
216 * uisnt->lock ==> uinst->u_lock ==> uinst->l_lock ==> uinst->c_lock
217 *
218 */
219
220
221 int
_init(void)222 _init(void)
223 {
224 int rval;
225
226 /* Initialize R/W lock for uinst_t */
227 rw_init(&oplmsu_uinst->lock, "uinst rwlock", RW_DRIVER, NULL);
228
229 /* Initialize mutex for upath_t */
230 mutex_init(&oplmsu_uinst->u_lock, "upath lock", MUTEX_DRIVER, NULL);
231
232 /* Initialize mutex for lpath_t */
233 mutex_init(&oplmsu_uinst->l_lock, "lpath lock", MUTEX_DRIVER, NULL);
234
235 /* Initialize mutex for ctrl_t */
236 mutex_init(&oplmsu_uinst->c_lock, "ctrl lock", MUTEX_DRIVER, NULL);
237
238 /* Initialize mutex for protecting background thread */
239 mutex_init(&oplmsu_bthrd_excl, NULL, MUTEX_DRIVER, NULL);
240
241 /* Initialize condition variable */
242 cv_init(&oplmsu_conf_cv, NULL, CV_DRIVER, NULL);
243
244 rval = mod_install(&modlinkage);
245 if (rval != DDI_SUCCESS) {
246 cv_destroy(&oplmsu_conf_cv);
247 mutex_destroy(&oplmsu_bthrd_excl);
248 mutex_destroy(&oplmsu_uinst->c_lock);
249 mutex_destroy(&oplmsu_uinst->l_lock);
250 mutex_destroy(&oplmsu_uinst->u_lock);
251 rw_destroy(&oplmsu_uinst->lock);
252 }
253 return (rval);
254 }
255
256 int
_fini(void)257 _fini(void)
258 {
259 int rval;
260
261 rval = mod_remove(&modlinkage);
262 if (rval == DDI_SUCCESS) {
263 cv_destroy(&oplmsu_conf_cv);
264 mutex_destroy(&oplmsu_bthrd_excl);
265 mutex_destroy(&oplmsu_uinst->c_lock);
266 mutex_destroy(&oplmsu_uinst->l_lock);
267 mutex_destroy(&oplmsu_uinst->u_lock);
268 rw_destroy(&oplmsu_uinst->lock);
269 }
270 return (rval);
271 }
272
273 int
_info(struct modinfo * modinfop)274 _info(struct modinfo *modinfop)
275 {
276 return (mod_info(&modlinkage, modinfop));
277 }
278
279 /* ARGSUSED */
280 int
oplmsu_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)281 oplmsu_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
282 {
283 dev_t dev = (dev_t)arg;
284 minor_t inst;
285 int rval = DDI_SUCCESS;
286
287 switch (cmd) {
288 case DDI_INFO_DEVT2DEVINFO :
289 if (oplmsu_uinst->msu_dip == NULL) {
290 rval = DDI_FAILURE;
291 } else {
292 *resultp = oplmsu_uinst->msu_dip;
293 }
294 break;
295
296 case DDI_INFO_DEVT2INSTANCE :
297 inst = getminor(dev) & ~(META_NODE_MASK|USER_NODE_MASK);
298 *resultp = (void *)(uintptr_t)inst;
299 break;
300
301 default :
302 rval = DDI_FAILURE;
303 break;
304 }
305 return (rval);
306 }
307
308 int
oplmsu_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)309 oplmsu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
310 {
311 minor_t meta_minor, user_minor;
312 int rval = 0;
313 int instance;
314 #define CNTRL(c) ((c) & 037)
315 char abt_ch_seq[3] = { '\r', '~', CNTRL('b') };
316
317 if (cmd == DDI_RESUME) {
318 return (DDI_SUCCESS);
319 }
320
321 if (cmd != DDI_ATTACH) {
322 return (DDI_FAILURE);
323 }
324
325 instance = ddi_get_instance(dip);
326 if (instance != 0) {
327 cmn_err(CE_WARN, "oplmsu: attach: "
328 "Invaild instance => %d", instance);
329 return (DDI_FAILURE);
330 }
331
332 /* Create minor number for meta control node */
333 meta_minor = instance | META_NODE_MASK;
334 /* Create minor number for user access node */
335 user_minor = instance | USER_NODE_MASK;
336
337 /* Create minor node for user access */
338 rval = ddi_create_minor_node(dip, USER_NAME, S_IFCHR, user_minor,
339 DDI_NT_SERIAL, 0);
340 if (rval != DDI_SUCCESS) {
341 cmn_err(CE_WARN, "oplmsu: attach: "
342 "ddi_create_minor_node failed. errno = %d", rval);
343 ddi_remove_minor_node(dip, NULL);
344 return (rval);
345 }
346
347 /* Create minor node for meta control */
348 rval = ddi_create_internal_pathname(dip, META_NAME, S_IFCHR,
349 meta_minor);
350 if (rval != DDI_SUCCESS) {
351 cmn_err(CE_WARN, "oplmsu: attach: "
352 "ddi_create_internal_pathname failed. errno = %d", rval);
353 ddi_remove_minor_node(dip, NULL);
354 return (rval);
355 }
356
357 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
358
359 /* Get each properties */
360 oplmsu_check_su = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
361 (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "check-superuser", 1);
362
363 /*
364 * Initialize members of uinst_t
365 */
366
367 oplmsu_uinst->inst_status = INST_STAT_UNCONFIGURED;
368 oplmsu_uinst->path_num = UNDEFINED;
369 oplmsu_uinst->msu_dip = dip;
370 (void) strcpy(oplmsu_uinst->abts, abt_ch_seq);
371
372 #ifdef DEBUG
373 oplmsu_trace_on = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
374 (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "trace-mode", 1);
375 oplmsu_ltrc_size = (uint_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
376 (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "trace-bufsize", 128);
377
378 if (oplmsu_trace_on == MSU_TRACE_ON) {
379 /* Initialize mutex for msu_trc_t */
380 mutex_init(&oplmsu_ltrc_lock, "trc lock", MUTEX_DRIVER, NULL);
381
382 mutex_enter(&oplmsu_ltrc_lock);
383 oplmsu_ltrc_top = (msu_trc_t *)kmem_zalloc(
384 (sizeof (msu_trc_t) * oplmsu_ltrc_size), KM_SLEEP);
385 oplmsu_ltrc_cur = (msu_trc_t *)(oplmsu_ltrc_top - 1);
386 oplmsu_ltrc_tail =
387 (msu_trc_t *)(oplmsu_ltrc_top + (oplmsu_ltrc_size - 1));
388 mutex_exit(&oplmsu_ltrc_lock);
389 }
390 #endif
391 rw_exit(&oplmsu_uinst->lock);
392 ddi_report_dev(dip);
393 return (rval);
394 }
395
396 int
oplmsu_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)397 oplmsu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
398 {
399 lpath_t *lpath, *next_lpath;
400
401 if (cmd == DDI_SUSPEND) {
402 return (DDI_SUCCESS);
403 }
404
405 if (cmd != DDI_DETACH) {
406 return (DDI_FAILURE);
407 }
408
409 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
410
411 /* Delete all upath_t */
412 oplmsu_delete_upath_info();
413
414 /* Delete all lpath_t */
415 mutex_enter(&oplmsu_uinst->l_lock);
416 lpath = oplmsu_uinst->first_lpath;
417 oplmsu_uinst->first_lpath = NULL;
418 oplmsu_uinst->last_lpath = NULL;
419 mutex_exit(&oplmsu_uinst->l_lock);
420
421 #ifdef DEBUG
422 if (oplmsu_trace_on == MSU_TRACE_ON) {
423 mutex_enter(&oplmsu_ltrc_lock);
424 if (oplmsu_ltrc_top != NULL) {
425 kmem_free(oplmsu_ltrc_top,
426 (sizeof (msu_trc_t) * oplmsu_ltrc_size));
427 }
428 oplmsu_ltrc_top = NULL;
429 oplmsu_ltrc_cur = NULL;
430 oplmsu_ltrc_tail = NULL;
431 mutex_exit(&oplmsu_ltrc_lock);
432
433 mutex_destroy(&oplmsu_ltrc_lock);
434 }
435 #endif
436 rw_exit(&oplmsu_uinst->lock);
437
438 while (lpath) {
439 if (lpath->rbuf_id) {
440 unbufcall(lpath->rbuf_id);
441 }
442
443 if (lpath->rtout_id) {
444 (void) untimeout(lpath->rtout_id);
445 }
446
447 if (lpath->rbuftbl) {
448 kmem_free(lpath->rbuftbl, sizeof (struct buf_tbl));
449 }
450
451 cv_destroy(&lpath->sw_cv);
452 next_lpath = lpath->l_next;
453 kmem_free(lpath, sizeof (lpath_t));
454 lpath = next_lpath;
455 }
456 ddi_remove_minor_node(dip, NULL);
457 return (DDI_SUCCESS);
458 }
459
460 /* ARGSUSED */
461 int
oplmsu_open(queue_t * urq,dev_t * dev,int oflag,int sflag,cred_t * cred_p)462 oplmsu_open(queue_t *urq, dev_t *dev, int oflag, int sflag, cred_t *cred_p)
463 {
464 ctrl_t *ctrl;
465 minor_t mindev = 0;
466 minor_t qmindev = 0;
467 major_t majdev;
468 ulong_t node_flag;
469
470 DBG_PRINT((CE_NOTE, "oplmsu: open: "
471 "devt = 0x%lx, sflag = 0x%x", *dev, sflag));
472
473 if (sflag == CLONEOPEN) {
474 return (EINVAL);
475 }
476
477 /* Get minor device number */
478 qmindev = (minor_t)getminor(*dev);
479 /* Get node type */
480 node_flag = MSU_NODE_TYPE(qmindev);
481 if ((node_flag != MSU_NODE_USER) && (node_flag != MSU_NODE_META)) {
482 return (EINVAL);
483 }
484
485 mutex_enter(&oplmsu_bthrd_excl);
486 if ((node_flag == MSU_NODE_USER) &&
487 (oplmsu_conf_st != MSU_CONFIGURED)) { /* User access & First open */
488 int cv_rval;
489
490 DBG_PRINT((CE_NOTE, "oplmsu: open: "
491 "oplmsu_conf_st = %x", oplmsu_conf_st));
492
493 if (oplmsu_conf_st == MSU_UNCONFIGURED) {
494 oplmsu_conf_st = MSU_CONFIGURING;
495
496 /* Start up background thread */
497 oplmsu_bthrd_id = thread_create(NULL, 2 * DEFAULTSTKSZ,
498 oplmsu_setup, (void *)oplmsu_uinst, 0, &p0, TS_RUN,
499 minclsyspri);
500 }
501
502 /*
503 * Wait with cv_wait_sig() until background thread is
504 * completed.
505 */
506 while (oplmsu_conf_st == MSU_CONFIGURING) {
507 cv_rval =
508 cv_wait_sig(&oplmsu_conf_cv, &oplmsu_bthrd_excl);
509 if (cv_rval == 0) {
510 mutex_exit(&oplmsu_bthrd_excl);
511 return (EINTR);
512 }
513 }
514 }
515 mutex_exit(&oplmsu_bthrd_excl);
516
517 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
518
519 /*
520 * If the node which will open is meta-control-node or
521 * user-access-node, and q_ptr, this is queue_t queue
522 * table member, is not NULL, then oplmsu returns
523 * SUCCESS immidiately.
524 * This process is used to protect dual open.
525 */
526
527 if ((urq != NULL) && (urq->q_ptr != NULL)) {
528 rw_exit(&oplmsu_uinst->lock);
529 return (SUCCESS);
530 }
531
532 /*
533 * If the node which will open is User-Access-Node, and instance
534 * status of oplmsu is no ONLINE, then oplmsu_open process fails
535 * with return value 'EIO'.
536 */
537
538 if ((node_flag == MSU_NODE_USER) &&
539 (oplmsu_uinst->inst_status != INST_STAT_ONLINE)) {
540 rw_exit(&oplmsu_uinst->lock);
541 return (EIO);
542 }
543
544 mindev |= qmindev; /* Create minor device number */
545 majdev = getmajor(*dev); /* Get major device number */
546 *dev = makedevice(majdev, mindev); /* Make device number */
547
548 /* Allocate kernel memory for ctrl_t */
549 ctrl = (ctrl_t *)kmem_zalloc(sizeof (ctrl_t), KM_SLEEP);
550
551 /*
552 * Initialize members of ctrl_t
553 */
554 ctrl->minor = (minor_t)mindev;
555 ctrl->queue = urq;
556 ctrl->sleep_flag = CV_WAKEUP;
557 ctrl->node_type = node_flag;
558 ctrl->wbuftbl =
559 (struct buf_tbl *)kmem_zalloc(sizeof (struct buf_tbl), KM_SLEEP);
560 cv_init(&ctrl->cvp, "oplmsu ctrl_tbl condvar", CV_DRIVER, NULL);
561
562 mutex_enter(&oplmsu_uinst->c_lock);
563
564 if (node_flag == MSU_NODE_USER) { /* User access node */
565
566 oplmsu_uinst->user_ctrl = ctrl;
567 oplmsu_queue_flag = 0;
568
569 } else { /* Meta control node */
570
571 oplmsu_uinst->meta_ctrl = ctrl;
572 }
573
574 RD(urq)->q_ptr = ctrl;
575 WR(urq)->q_ptr = ctrl;
576
577 mutex_exit(&oplmsu_uinst->c_lock);
578 rw_exit(&oplmsu_uinst->lock);
579
580 OPLMSU_TRACE(urq, (mblk_t *)node_flag, MSU_TRC_OPN);
581
582 qprocson(urq); /* Enable put and service routine */
583 return (SUCCESS);
584 }
585
586 /* ARGSUSED */
587 int
oplmsu_close(queue_t * urq,int flag,cred_t * cred_p)588 oplmsu_close(queue_t *urq, int flag, cred_t *cred_p)
589 {
590 ctrl_t *ctrl;
591 minor_t qmindev = 0;
592 lpath_t *lpath;
593 ulong_t node_flag;
594 bufcall_id_t wbuf_id;
595 timeout_id_t wtout_id;
596
597 rw_enter(&oplmsu_uinst->lock, RW_READER);
598 mutex_enter(&oplmsu_uinst->l_lock);
599 mutex_enter(&oplmsu_uinst->c_lock);
600 if ((ctrl = urq->q_ptr) == NULL) {
601 mutex_exit(&oplmsu_uinst->c_lock);
602 mutex_exit(&oplmsu_uinst->l_lock);
603 rw_exit(&oplmsu_uinst->lock);
604
605 DBG_PRINT((CE_NOTE, "oplmsu: close: "
606 "close has already been completed"));
607 return (FAILURE);
608 }
609 qmindev = ctrl->minor;
610
611 DBG_PRINT((CE_NOTE, "oplmsu: close: ctrl->minor = 0x%x", qmindev));
612
613 node_flag = MSU_NODE_TYPE(qmindev);
614 if (node_flag > MSU_NODE_META) {
615 mutex_exit(&oplmsu_uinst->c_lock);
616 mutex_exit(&oplmsu_uinst->l_lock);
617 rw_exit(&oplmsu_uinst->lock);
618 return (EINVAL);
619 }
620
621 /*
622 * Check that queue which is waiting for response from lower stream
623 * exist. If queue exists, oplmsu sets CV_SLEEP to sleep_flag.
624 */
625
626 for (lpath = oplmsu_uinst->first_lpath; lpath; ) {
627 if (((RD(urq) == lpath->hndl_uqueue) ||
628 (WR(urq) == lpath->hndl_uqueue)) &&
629 (lpath->hndl_mp != NULL)) {
630 ctrl->sleep_flag = CV_SLEEP;
631 break;
632 }
633
634 lpath = lpath->l_next;
635 }
636 mutex_exit(&oplmsu_uinst->l_lock);
637 rw_exit(&oplmsu_uinst->lock);
638
639 /* If sleep_flag is not CV_SLEEP, oplmsu calls cv_wait. */
640 if (lpath) {
641 while (ctrl->sleep_flag != CV_WAKEUP) {
642 cv_wait(&ctrl->cvp, &oplmsu_uinst->c_lock);
643 }
644 }
645
646 flushq(RD(urq), FLUSHALL);
647 flushq(WR(urq), FLUSHALL);
648 mutex_exit(&oplmsu_uinst->c_lock);
649 qprocsoff(urq); /* Disable queuing of queue */
650
651 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
652 switch (node_flag) {
653 case MSU_NODE_USER : /* User access node */
654 oplmsu_uinst->user_ctrl = NULL;
655 oplmsu_queue_flag = 0;
656 break;
657
658 case MSU_NODE_META : /* Meta control node */
659 oplmsu_uinst->meta_ctrl = NULL;
660 break;
661
662 default :
663 cmn_err(CE_WARN, "oplmsu: close: node_flag = 0x%lx", node_flag);
664 }
665
666 ctrl->minor = 0;
667 ctrl->queue = NULL;
668 wbuf_id = ctrl->wbuf_id;
669 wtout_id = ctrl->wtout_id;
670 ctrl->wbuf_id = 0;
671 ctrl->wtout_id = 0;
672
673 cv_destroy(&ctrl->cvp);
674 kmem_free(ctrl->wbuftbl, sizeof (struct buf_tbl));
675 ctrl->wbuftbl = NULL;
676
677 RD(urq)->q_ptr = NULL;
678 WR(urq)->q_ptr = NULL;
679 rw_exit(&oplmsu_uinst->lock);
680
681 if (wbuf_id != 0) {
682 unbufcall(wbuf_id);
683 }
684
685 if (wtout_id != 0) {
686 (void) untimeout(wtout_id);
687 }
688
689 /* Free kernel memory for ctrl_t */
690 kmem_free(ctrl, sizeof (ctrl_t));
691
692 OPLMSU_TRACE(urq, (mblk_t *)node_flag, MSU_TRC_CLS);
693 return (SUCCESS);
694 }
695
696 /*
697 * Upper write put procedure
698 */
699 int
oplmsu_uwput(queue_t * uwq,mblk_t * mp)700 oplmsu_uwput(queue_t *uwq, mblk_t *mp)
701 {
702
703 if (mp == NULL) {
704 return (SUCCESS);
705 }
706
707 if ((uwq == NULL) || (uwq->q_ptr == NULL)) {
708 freemsg(mp);
709 return (SUCCESS);
710 }
711
712 OPLMSU_TRACE(uwq, mp, MSU_TRC_UI);
713
714 rw_enter(&oplmsu_uinst->lock, RW_READER);
715 if (mp->b_datap->db_type == M_FLUSH) {
716 oplmsu_wcmn_flush_hndl(uwq, mp, RW_READER);
717 } else if (mp->b_datap->db_type >= QPCTL) {
718 ctrl_t *ctrl;
719
720 mutex_enter(&oplmsu_uinst->c_lock);
721 ctrl = (ctrl_t *)uwq->q_ptr;
722
723 /* Link high priority message to local queue */
724 oplmsu_link_high_primsg(&ctrl->first_upri_hi,
725 &ctrl->last_upri_hi, mp);
726
727 mutex_exit(&oplmsu_uinst->c_lock);
728 oplmsu_wcmn_high_qenable(WR(uwq), RW_READER);
729 } else {
730 (void) putq(WR(uwq), mp);
731 }
732 rw_exit(&oplmsu_uinst->lock);
733 return (SUCCESS);
734 }
735
736 /*
737 * Upper write service procedure
738 */
739 int
oplmsu_uwsrv(queue_t * uwq)740 oplmsu_uwsrv(queue_t *uwq)
741 {
742 struct iocblk *iocp = NULL;
743 mblk_t *mp = NULL;
744 int rval;
745
746 if ((uwq == NULL) || (uwq->q_ptr == NULL)) {
747 return (FAILURE);
748 }
749
750 rw_enter(&oplmsu_uinst->lock, RW_READER);
751
752 /* Handle high priority message */
753 while (mp = oplmsu_wcmn_high_getq(uwq)) {
754 if (mp->b_datap->db_type == M_FLUSH) {
755 oplmsu_wcmn_flush_hndl(uwq, mp, RW_READER);
756 continue;
757 }
758
759 if (oplmsu_wcmn_through_hndl(uwq, mp, MSU_HIGH, RW_READER) ==
760 FAILURE) {
761 rw_exit(&oplmsu_uinst->lock);
762 return (SUCCESS);
763 }
764 }
765 rw_exit(&oplmsu_uinst->lock);
766
767 /* Handle normal priority message */
768 while (mp = getq(uwq)) {
769 rval = SUCCESS;
770 switch (mp->b_datap->db_type) {
771 case M_IOCTL :
772 iocp = (struct iocblk *)mp->b_rptr;
773 switch (iocp->ioc_cmd) {
774 case I_PLINK :
775 if (oplmsu_cmn_pullup_msg(uwq, mp) != FAILURE) {
776 rval = oplmsu_uwioctl_iplink(uwq, mp);
777 }
778 break;
779
780 case I_PUNLINK :
781 if (oplmsu_cmn_pullup_msg(uwq, mp) != FAILURE) {
782 rval = oplmsu_uwioctl_ipunlink(uwq, mp);
783 }
784 break;
785
786 case TCSETS : /* FALLTHRU */
787 case TCSETSW : /* FALLTHRU */
788 case TCSETSF : /* FALLTHRU */
789 case TIOCMSET : /* FALLTHRU */
790 case TIOCSPPS : /* FALLTHRU */
791 case TIOCSWINSZ : /* FALLTHRU */
792 case TIOCSSOFTCAR :
793 rval = oplmsu_uwioctl_termios(uwq, mp);
794 break;
795
796 default :
797 rw_enter(&oplmsu_uinst->lock, RW_READER);
798 rval = oplmsu_wcmn_through_hndl(uwq, mp,
799 MSU_NORM, RW_READER);
800 rw_exit(&oplmsu_uinst->lock);
801 break;
802 }
803 break;
804
805 default :
806 rw_enter(&oplmsu_uinst->lock, RW_READER);
807 rval = oplmsu_wcmn_through_hndl(uwq, mp, MSU_NORM,
808 RW_READER);
809 rw_exit(&oplmsu_uinst->lock);
810 break;
811 }
812
813 if (rval == FAILURE) {
814 break;
815 }
816 }
817 return (SUCCESS);
818 }
819
820 /*
821 * Lower write service procedure
822 */
823 int
oplmsu_lwsrv(queue_t * lwq)824 oplmsu_lwsrv(queue_t *lwq)
825 {
826 mblk_t *mp;
827 queue_t *dst_queue;
828 lpath_t *lpath;
829
830 rw_enter(&oplmsu_uinst->lock, RW_READER);
831 while (mp = getq(lwq)) {
832 if (mp->b_datap->db_type >= QPCTL) {
833 rw_exit(&oplmsu_uinst->lock);
834 OPLMSU_TRACE(WR(lwq), mp, MSU_TRC_LO);
835 putnext(WR(lwq), mp);
836 rw_enter(&oplmsu_uinst->lock, RW_READER);
837 continue;
838 }
839
840 dst_queue = WR(lwq);
841 if (canputnext(dst_queue)) {
842 rw_exit(&oplmsu_uinst->lock);
843 OPLMSU_TRACE(dst_queue, mp, MSU_TRC_LO);
844 putnext(dst_queue, mp);
845 rw_enter(&oplmsu_uinst->lock, RW_READER);
846 } else {
847 (void) putbq(WR(lwq), mp);
848 break;
849 }
850 }
851
852 mutex_enter(&oplmsu_uinst->l_lock);
853 lpath = (lpath_t *)lwq->q_ptr;
854 if (lpath->uwq_flag != 0) {
855 qenable(WR(lpath->uwq_queue));
856 lpath->uwq_flag = 0;
857 lpath->uwq_queue = NULL;
858 }
859 mutex_exit(&oplmsu_uinst->l_lock);
860 rw_exit(&oplmsu_uinst->lock);
861 return (SUCCESS);
862 }
863
864 /*
865 * Lower read put procedure
866 */
867 int
oplmsu_lrput(queue_t * lrq,mblk_t * mp)868 oplmsu_lrput(queue_t *lrq, mblk_t *mp)
869 {
870
871 if (mp == NULL) {
872 return (SUCCESS);
873 }
874
875 if ((lrq == NULL) || (lrq->q_ptr == NULL)) {
876 freemsg(mp);
877 return (SUCCESS);
878 }
879
880 OPLMSU_TRACE(lrq, mp, MSU_TRC_LI);
881
882 if (mp->b_datap->db_type == M_FLUSH) {
883 rw_enter(&oplmsu_uinst->lock, RW_READER);
884 oplmsu_rcmn_flush_hndl(lrq, mp);
885 rw_exit(&oplmsu_uinst->lock);
886 } else if (mp->b_datap->db_type >= QPCTL) {
887 lpath_t *lpath;
888
889 rw_enter(&oplmsu_uinst->lock, RW_READER);
890 mutex_enter(&oplmsu_uinst->l_lock);
891 lpath = lrq->q_ptr;
892
893 /* Link high priority message to local queue */
894 oplmsu_link_high_primsg(&lpath->first_lpri_hi,
895 &lpath->last_lpri_hi, mp);
896
897 mutex_exit(&oplmsu_uinst->l_lock);
898 rw_exit(&oplmsu_uinst->lock);
899 oplmsu_rcmn_high_qenable(lrq);
900 } else {
901 (void) putq(lrq, mp);
902 }
903 return (SUCCESS);
904 }
905
906 /*
907 * Lower read service procedure
908 */
909 int
oplmsu_lrsrv(queue_t * lrq)910 oplmsu_lrsrv(queue_t *lrq)
911 {
912 mblk_t *mp;
913 boolean_t aborted;
914 int rval;
915
916 if ((lrq == NULL) || (lrq->q_ptr == NULL)) {
917 return (FAILURE);
918 }
919
920 /* Handle normal priority message */
921 while (mp = getq(lrq)) {
922 if (mp->b_datap->db_type >= QPCTL) {
923 cmn_err(CE_WARN, "oplmsu: lr-srv: "
924 "Invalid db_type => %x", mp->b_datap->db_type);
925 }
926
927 switch (mp->b_datap->db_type) {
928 case M_DATA :
929 aborted = B_FALSE;
930 rw_enter(&oplmsu_uinst->lock, RW_READER);
931 if ((abort_enable == KIOCABORTALTERNATE) &&
932 (RD(oplmsu_uinst->lower_queue) == lrq)) {
933 uchar_t *rx_char = mp->b_rptr;
934 lpath_t *lpath;
935
936 mutex_enter(&oplmsu_uinst->l_lock);
937 lpath = lrq->q_ptr;
938 while (rx_char != mp->b_wptr) {
939 if (*rx_char == *lpath->abt_char) {
940 lpath->abt_char++;
941 if (*lpath->abt_char == '\0') {
942 abort_sequence_enter((char *)
943 NULL);
944 lpath->abt_char
945 = oplmsu_uinst->abts;
946 aborted = B_TRUE;
947 break;
948 }
949 } else {
950 lpath->abt_char = (*rx_char ==
951 *oplmsu_uinst->abts) ?
952 oplmsu_uinst->abts + 1 :
953 oplmsu_uinst->abts;
954 }
955 rx_char++;
956 }
957 mutex_exit(&oplmsu_uinst->l_lock);
958 }
959 rw_exit(&oplmsu_uinst->lock);
960
961 if (aborted) {
962 freemsg(mp);
963 continue;
964 }
965
966 /*
967 * When 1st byte of the received M_DATA is XON or,
968 * 1st byte is XOFF and 2nd byte is XON.
969 */
970
971 if ((*(mp->b_rptr) == MSU_XON) ||
972 (((mp->b_wptr - mp->b_rptr) == 2) &&
973 ((*(mp->b_rptr) == MSU_XOFF) &&
974 (*(mp->b_rptr + 1) == MSU_XON)))) {
975 /* Path switching by XOFF/XON */
976 if (oplmsu_lrdata_xoffxon(lrq, mp) == FAILURE) {
977 return (SUCCESS);
978 }
979 } else {
980 rw_enter(&oplmsu_uinst->lock, RW_READER);
981 rval =
982 oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM);
983 rw_exit(&oplmsu_uinst->lock);
984
985 if (rval == FAILURE) {
986 return (SUCCESS);
987 }
988 }
989 break;
990
991 case M_BREAK :
992 if ((mp->b_wptr - mp->b_rptr) == 0 && msgdsize(mp)
993 == 0) {
994 rw_enter(&oplmsu_uinst->lock, RW_READER);
995 if ((abort_enable != KIOCABORTALTERNATE) &&
996 (RD(oplmsu_uinst->lower_queue) == lrq)) {
997 abort_sequence_enter((char *)NULL);
998 }
999 rw_exit(&oplmsu_uinst->lock);
1000 freemsg(mp);
1001 break;
1002 }
1003 /* FALLTHRU */
1004
1005 default :
1006 rw_enter(&oplmsu_uinst->lock, RW_READER);
1007 (void) oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM);
1008 rw_exit(&oplmsu_uinst->lock);
1009 break;
1010 }
1011 }
1012 return (SUCCESS);
1013 }
1014
1015 /*
1016 * Upper read service procedure
1017 */
1018 int
oplmsu_ursrv(queue_t * urq)1019 oplmsu_ursrv(queue_t *urq)
1020 {
1021 mblk_t *mp;
1022 queue_t *dst_queue;
1023 lpath_t *lpath;
1024 ctrl_t *ctrl;
1025 int res_chk = 0;
1026
1027 rw_enter(&oplmsu_uinst->lock, RW_READER);
1028 while (mp = getq(urq)) {
1029 if (mp->b_datap->db_type >= QPCTL) {
1030 if ((mp->b_datap->db_type == M_IOCACK) ||
1031 (mp->b_datap->db_type == M_IOCNAK)) {
1032 res_chk = 1;
1033 }
1034 rw_exit(&oplmsu_uinst->lock);
1035 OPLMSU_TRACE(RD(urq), mp, MSU_TRC_UO);
1036 putnext(RD(urq), mp);
1037
1038 rw_enter(&oplmsu_uinst->lock, RW_READER);
1039 mutex_enter(&oplmsu_uinst->l_lock);
1040 lpath = oplmsu_uinst->first_lpath;
1041 while (lpath) {
1042 qenable(RD(lpath->lower_queue));
1043 lpath = lpath->l_next;
1044 }
1045 mutex_exit(&oplmsu_uinst->l_lock);
1046
1047 if (res_chk == 1) {
1048 mutex_enter(&oplmsu_uinst->c_lock);
1049 ctrl = (ctrl_t *)urq->q_ptr;
1050 if (ctrl != NULL) {
1051 if (ctrl->wait_queue != NULL) {
1052 qenable(WR(ctrl->wait_queue));
1053 ctrl->wait_queue = NULL;
1054 }
1055 }
1056 mutex_exit(&oplmsu_uinst->c_lock);
1057 res_chk = 0;
1058 }
1059 continue;
1060 }
1061
1062 dst_queue = RD(urq);
1063 if (canputnext(dst_queue)) {
1064 rw_exit(&oplmsu_uinst->lock);
1065 OPLMSU_TRACE(dst_queue, mp, MSU_TRC_UO);
1066 putnext(dst_queue, mp);
1067 rw_enter(&oplmsu_uinst->lock, RW_READER);
1068 } else {
1069 (void) putbq(urq, mp);
1070 break;
1071 }
1072 }
1073
1074 mutex_enter(&oplmsu_uinst->c_lock);
1075 ctrl = urq->q_ptr;
1076 if (ctrl->lrq_flag != 0) {
1077 qenable(ctrl->lrq_queue);
1078 ctrl->lrq_flag = 0;
1079 ctrl->lrq_queue = NULL;
1080 }
1081 mutex_exit(&oplmsu_uinst->c_lock);
1082 rw_exit(&oplmsu_uinst->lock);
1083 return (SUCCESS);
1084 }
1085
1086 int
oplmsu_open_msu(dev_info_t * dip,ldi_ident_t * lip,ldi_handle_t * lhp)1087 oplmsu_open_msu(dev_info_t *dip, ldi_ident_t *lip, ldi_handle_t *lhp)
1088 {
1089 dev_t devt;
1090 int rval;
1091
1092 /* Allocate LDI identifier */
1093 rval = ldi_ident_from_dip(dip, lip);
1094 if (rval != 0) {
1095 cmn_err(CE_WARN, "oplmsu: open-msu: "
1096 "ldi_ident_from_dip failed. errno = %d", rval);
1097 return (rval);
1098 }
1099
1100 /* Open oplmsu(meta ctrl node) */
1101 devt = makedevice(ddi_driver_major(dip), META_NODE_MASK);
1102 rval =
1103 ldi_open_by_dev(&devt, OTYP_CHR, (FREAD|FWRITE), kcred, lhp, *lip);
1104 if (rval != 0) {
1105 cmn_err(CE_WARN, "oplmsu: open-msu: "
1106 "ldi_open_by_dev failed. errno = %d", rval);
1107 ldi_ident_release(*lip);
1108 }
1109 return (rval);
1110 }
1111
1112 int
oplmsu_plink_serial(dev_info_t * dip,ldi_handle_t msu_lh,int * id)1113 oplmsu_plink_serial(dev_info_t *dip, ldi_handle_t msu_lh, int *id)
1114 {
1115 ldi_ident_t li = NULL;
1116 ldi_handle_t lh = NULL;
1117 int param;
1118 int rval;
1119 char pathname[MSU_PATHNAME_SIZE];
1120 char wrkbuf[MSU_PATHNAME_SIZE];
1121
1122 /* Create physical path-name for serial */
1123 (void) ddi_pathname(dip, wrkbuf);
1124 *(wrkbuf + strlen(wrkbuf)) = '\0';
1125 (void) sprintf(pathname, "/devices%s:%c", wrkbuf,
1126 'a'+ ddi_get_instance(dip));
1127
1128 /* Allocate LDI identifier */
1129 rval = ldi_ident_from_dip(dip, &li);
1130 if (rval != 0) {
1131 cmn_err(CE_WARN, "oplmsu: plink-serial: "
1132 "%s ldi_ident_from_dip failed. errno = %d", pathname, rval);
1133 return (rval);
1134 }
1135
1136 /* Open serial */
1137 rval = ldi_open_by_name(pathname, (FREAD|FWRITE|FEXCL), kcred, &lh, li);
1138 if (rval != 0) {
1139 cmn_err(CE_WARN, "oplmsu: plink-serial: "
1140 "%s open failed. errno = %d", pathname, rval);
1141 ldi_ident_release(li);
1142 return (rval);
1143 }
1144
1145 /* Try to remove the top module from the stream */
1146 param = 0;
1147 while ((ldi_ioctl(lh, I_POP, (intptr_t)0, FKIOCTL, kcred, ¶m))
1148 == 0) {
1149 continue;
1150 }
1151
1152 /* Issue ioctl(I_PLINK) */
1153 param = 0;
1154 rval = ldi_ioctl(msu_lh, I_PLINK, (intptr_t)lh, FKIOCTL, kcred, ¶m);
1155 if (rval != 0) {
1156 cmn_err(CE_WARN, "oplmsu: plink-serial: "
1157 "%s ioctl(I_PLINK) failed. errno = %d", pathname, rval);
1158 }
1159
1160 (void) ldi_close(lh, (FREAD|FWRITE|FEXCL), kcred);
1161 ldi_ident_release(li);
1162
1163 *id = param; /* Save link-id */
1164 return (rval);
1165 }
1166
1167 int
oplmsu_set_lpathnum(int lnk_id,int instance)1168 oplmsu_set_lpathnum(int lnk_id, int instance)
1169 {
1170 lpath_t *lpath;
1171 int rval = SUCCESS;
1172
1173 rw_enter(&oplmsu_uinst->lock, RW_READER);
1174 mutex_enter(&oplmsu_uinst->l_lock);
1175 lpath = oplmsu_uinst->first_lpath;
1176 while (lpath) {
1177 if ((lpath->path_no == UNDEFINED) &&
1178 (lpath->link_id == lnk_id)) {
1179 lpath->path_no = instance; /* Set instance number */
1180 lpath->src_upath = NULL;
1181 lpath->status = MSU_SETID_NU;
1182 break;
1183 }
1184 lpath = lpath->l_next;
1185 }
1186 mutex_exit(&oplmsu_uinst->l_lock);
1187 rw_exit(&oplmsu_uinst->lock);
1188
1189 if (lpath == NULL) {
1190 rval = EINVAL;
1191 }
1192 return (rval);
1193 }
1194
1195 int
oplmsu_dr_attach(dev_info_t * dip)1196 oplmsu_dr_attach(dev_info_t *dip)
1197 {
1198 ldi_ident_t msu_li = NULL;
1199 ldi_handle_t msu_lh = NULL;
1200 upath_t *upath;
1201 int len;
1202 int instance;
1203 int lnk_id = 0;
1204 int param = 0;
1205 int rval;
1206
1207 /* Get instance for serial */
1208 instance = ddi_get_instance(dip);
1209
1210 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1211 mutex_enter(&oplmsu_uinst->u_lock);
1212
1213 /* Get current number of paths */
1214 oplmsu_uinst->path_num = oplmsu_get_pathnum();
1215
1216 /* Check specified upath_t */
1217 upath = oplmsu_uinst->first_upath;
1218 while (upath) {
1219 if (instance == upath->path_no) {
1220 break;
1221 }
1222 upath = upath->u_next;
1223 }
1224 mutex_exit(&oplmsu_uinst->u_lock);
1225 rw_exit(&oplmsu_uinst->lock);
1226
1227 if (upath != NULL) {
1228 cmn_err(CE_WARN, "oplmsu: attach(dr): "
1229 "Instance %d already exist", instance);
1230 return (EINVAL);
1231 }
1232
1233 /* Open oplmsu */
1234 rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh);
1235 if (rval != 0) {
1236 cmn_err(CE_WARN, "oplmsu: attach(dr): "
1237 "msu open failed. errno = %d", rval);
1238 return (rval);
1239 }
1240
1241 /* Connect two streams */
1242 rval = oplmsu_plink_serial(dip, msu_lh, &lnk_id);
1243 if (rval != 0) {
1244 cmn_err(CE_WARN, "oplmsu: attach(dr): "
1245 "i_plink failed. errno = %d", rval);
1246 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1247 ldi_ident_release(msu_li);
1248 return (rval);
1249 }
1250
1251 rval = oplmsu_set_lpathnum(lnk_id, instance);
1252 if (rval != 0) {
1253 cmn_err(CE_WARN, "oplmsu: attach(dr): "
1254 "Link id %d is not found", lnk_id);
1255 /* Issue ioctl(I_PUNLINK) */
1256 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL,
1257 kcred, ¶m);
1258 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1259 ldi_ident_release(msu_li);
1260 return (rval);
1261 }
1262
1263 /* Add the path */
1264 rval = oplmsu_config_add(dip);
1265 if (rval != 0) {
1266 cmn_err(CE_WARN, "oplmsu: attach(dr): "
1267 "Failed to add the path. errno = %d", rval);
1268 /* Issue ioctl(I_PUNLINK) */
1269 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL,
1270 kcred, ¶m);
1271
1272 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1273 ldi_ident_release(msu_li);
1274 return (rval);
1275 }
1276
1277 /* Start to use the path */
1278 rval = oplmsu_config_start(instance);
1279 if (rval != 0) {
1280 struct msu_path *mpath;
1281 struct msu_dev *mdev;
1282
1283 cmn_err(CE_WARN, "oplmsu: attach(dr): "
1284 "Failed to start the path. errno = %d", rval);
1285
1286 len = sizeof (struct msu_path) + sizeof (struct msu_dev);
1287 mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP);
1288 mpath->num = 1;
1289 mdev = (struct msu_dev *)(mpath + 1);
1290 mdev->dip = dip;
1291
1292 /* Delete the path */
1293 if ((oplmsu_config_del(mpath)) == 0) {
1294 /* Issue ioctl(I_PUNLINK) */
1295 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id,
1296 FKIOCTL, kcred, ¶m);
1297 }
1298 kmem_free(mpath, (size_t)len);
1299 }
1300
1301 /* Close oplmsu */
1302 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1303 ldi_ident_release(msu_li);
1304 return (rval);
1305 }
1306
1307 int
oplmsu_dr_detach(dev_info_t * dip)1308 oplmsu_dr_detach(dev_info_t *dip)
1309 {
1310 ldi_ident_t msu_li = NULL;
1311 ldi_handle_t msu_lh = NULL;
1312 struct msu_path *mpath;
1313 struct msu_dev *mdev;
1314 upath_t *upath;
1315 lpath_t *lpath;
1316 int len;
1317 int instance;
1318 int count = 0;
1319 int param = 0;
1320 int status;
1321 int rval;
1322
1323 /* Get instance for serial */
1324 instance = ddi_get_instance(dip);
1325
1326 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1327 mutex_enter(&oplmsu_uinst->u_lock);
1328
1329 /* Get current number of paths */
1330 oplmsu_uinst->path_num = oplmsu_get_pathnum();
1331
1332 rval = FAILURE;
1333
1334 /* Check specified upath_t */
1335 upath = oplmsu_uinst->first_upath;
1336 while (upath) {
1337 if (instance == upath->path_no) {
1338 /* Save status of specified path */
1339 status = upath->status;
1340 rval = SUCCESS;
1341 }
1342 upath = upath->u_next;
1343 count += 1;
1344 }
1345 mutex_exit(&oplmsu_uinst->u_lock);
1346 rw_exit(&oplmsu_uinst->lock);
1347
1348 if (rval == FAILURE) {
1349 if (count <= 1) {
1350 cmn_err(CE_WARN, "oplmsu: detach(dr): "
1351 "Instance %d is last path", instance);
1352 } else {
1353 cmn_err(CE_WARN, "oplmsu: detach(dr): "
1354 "Instance %d doesn't find", instance);
1355 }
1356 return (EINVAL);
1357 }
1358
1359 /* Check status of specified path */
1360 if ((status == MSU_PSTAT_ACTIVE) || (status == MSU_PSTAT_STANDBY)) {
1361 /* Stop to use the path */
1362 rval = oplmsu_config_stop(instance);
1363 if (rval != 0) {
1364 cmn_err(CE_WARN, "oplmsu: detach(dr): "
1365 "Failed to stop the path. errno = %d", rval);
1366 return (rval);
1367 }
1368 }
1369
1370 /* Prepare to unlink the path */
1371 rval = oplmsu_config_disc(instance);
1372 if (rval != 0) {
1373 cmn_err(CE_WARN, "oplmsu: detach(dr): "
1374 "Failed to disconnect the path. errno = %d", rval);
1375 return (rval);
1376 }
1377
1378 rw_enter(&oplmsu_uinst->lock, RW_READER);
1379 mutex_enter(&oplmsu_uinst->l_lock);
1380 lpath = oplmsu_uinst->first_lpath;
1381 while (lpath) {
1382 if (lpath->path_no == instance) { /* Get link ID */
1383 break;
1384 }
1385 lpath = lpath->l_next;
1386 }
1387 mutex_exit(&oplmsu_uinst->l_lock);
1388 rw_exit(&oplmsu_uinst->lock);
1389
1390 if (lpath == NULL) {
1391 cmn_err(CE_WARN, "oplmsu: detach(dr): Can not find link ID");
1392 return (EINVAL);
1393 }
1394
1395 /* Open oplmsu */
1396 rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh);
1397 if (rval != 0) {
1398 cmn_err(CE_WARN, "oplmsu: detach(dr): "
1399 "msu open failed. errno = %d", rval);
1400 return (rval);
1401 }
1402
1403 /* Issue ioctl(I_PUNLINK) */
1404 rval = ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lpath->link_id, FKIOCTL,
1405 kcred, ¶m);
1406 if (rval != 0) {
1407 cmn_err(CE_WARN, "oplmsu: detach(dr): "
1408 "ioctl(I_PUNLINK) failed. errno = %d", rval);
1409 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1410 ldi_ident_release(msu_li);
1411 return (rval);
1412 }
1413
1414 /* Close oplmsu(meta node) */
1415 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1416 ldi_ident_release(msu_li);
1417
1418 len = sizeof (struct msu_path) + sizeof (struct msu_dev);
1419 mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP);
1420 mpath->num = 1;
1421 mdev = (struct msu_dev *)(mpath + 1);
1422 mdev->dip = dip;
1423
1424 /* Delete the path */
1425 rval = oplmsu_config_del(mpath);
1426 if (rval != 0) {
1427 cmn_err(CE_WARN, "oplmsu: detach(dr): "
1428 "Failed to delete the path. errno = %d", rval);
1429 }
1430
1431 kmem_free(mpath, (size_t)len);
1432 return (rval);
1433 }
1434
1435 /*
1436 * The ebus and the serial device path under a given CMU_CH chip
1437 * is expected to be always at the same address. So, it is safe
1438 * to hard-code the pathnames as below.
1439 */
1440 #define EBUS_PATH "ebus@1"
1441 #define SERIAL_PATH "serial@14,400000"
1442 #define EBUS_SERIAL_PATH ("/" EBUS_PATH "/" SERIAL_PATH)
1443
1444 /*
1445 * Given the CMU_CH dip, find the serial device dip.
1446 */
1447 dev_info_t *
oplmsu_find_ser_dip(dev_info_t * cmuch_dip)1448 oplmsu_find_ser_dip(dev_info_t *cmuch_dip)
1449 {
1450 dev_info_t *ebus_dip;
1451 dev_info_t *ser_dip = NULL;
1452
1453 ndi_devi_enter(cmuch_dip);
1454 ebus_dip = ndi_devi_findchild(cmuch_dip, EBUS_PATH);
1455
1456 DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: "
1457 "ebus_dip = %p", (void *)ebus_dip));
1458
1459 if (ebus_dip != NULL) {
1460 ndi_devi_enter(ebus_dip);
1461 ser_dip = ndi_devi_findchild(ebus_dip, SERIAL_PATH);
1462
1463 DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: "
1464 "ser_dip = %p", (void *)ser_dip));
1465 ndi_devi_exit(ebus_dip);
1466 }
1467 ndi_devi_exit(cmuch_dip);
1468 return (ser_dip);
1469 }
1470
1471 /*
1472 * Find all console related serial devices.
1473 */
1474 int
oplmsu_find_serial(ser_devl_t ** ser_dl)1475 oplmsu_find_serial(ser_devl_t **ser_dl)
1476 {
1477 dev_info_t *root_dip;
1478 dev_info_t *cmuch_dip;
1479 dev_info_t *dip;
1480 ser_devl_t *wrk_ser_dl;
1481 int count = 0;
1482 char pathname[MSU_PATHNAME_SIZE];
1483 dev_t devt;
1484 char *namep;
1485
1486 root_dip = ddi_root_node();
1487 ndi_devi_enter(root_dip);
1488 cmuch_dip = ddi_get_child(root_dip);
1489
1490 while (cmuch_dip != NULL) {
1491 namep = ddi_binding_name(cmuch_dip); /* Get binding name */
1492 if (namep == NULL) {
1493 cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1494 continue;
1495 }
1496
1497 DBG_PRINT((CE_NOTE, "oplmsu: find-serial: name => %s", namep));
1498
1499 if ((strcmp(namep, MSU_CMUCH_FF) != 0) &&
1500 (strcmp(namep, MSU_CMUCH_DC) != 0)) {
1501 #ifdef DEBUG
1502 if (strcmp(namep, MSU_CMUCH_DBG) != 0) {
1503 cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1504 continue;
1505 }
1506 #else
1507 cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1508 continue;
1509 #endif
1510 }
1511
1512 /*
1513 * Online the cmuch_dip so that its in the right state
1514 * to get the complete path, that is both name and address.
1515 */
1516 (void) ndi_devi_online(cmuch_dip, 0);
1517 (void) ddi_pathname(cmuch_dip, pathname);
1518 DBG_PRINT((CE_NOTE,
1519 "oplmsu: find-serial: cmu-ch path => %s", pathname));
1520 (void) strcat(pathname, EBUS_SERIAL_PATH);
1521
1522 /*
1523 * Call ddi_pathname_to_dev_t to forceload and attach
1524 * the required drivers.
1525 */
1526 devt = ddi_pathname_to_dev_t(pathname);
1527 DBG_PRINT((CE_NOTE, "oplmsu: find-serial: serial device "
1528 "dev_t = %lx", devt));
1529 if ((devt != NODEV) &&
1530 ((dip = oplmsu_find_ser_dip(cmuch_dip)) != NULL)) {
1531 wrk_ser_dl = (ser_devl_t *)
1532 kmem_zalloc(sizeof (ser_devl_t), KM_SLEEP);
1533 wrk_ser_dl->dip = dip;
1534 count += 1;
1535
1536 if (*ser_dl != NULL) {
1537 wrk_ser_dl->next = *ser_dl;
1538 }
1539 *ser_dl = wrk_ser_dl;
1540 }
1541 cmuch_dip = ddi_get_next_sibling(cmuch_dip);
1542 }
1543 ndi_devi_exit(root_dip);
1544 return (count);
1545 }
1546
1547 /* Configure STREAM */
1548 void
oplmsu_conf_stream(uinst_t * msu_uinst)1549 oplmsu_conf_stream(uinst_t *msu_uinst)
1550 {
1551 ldi_ident_t msu_li = NULL;
1552 ldi_handle_t msu_lh = NULL;
1553 struct msu_path *mpath;
1554 struct msu_dev *mdev;
1555 ser_devl_t *ser_dl = NULL, *next_ser_dl;
1556 int *plink_id;
1557 int size;
1558 int i;
1559 int param;
1560 int connected = 0;
1561 int devcnt = 0;
1562 int rval;
1563
1564 DBG_PRINT((CE_NOTE,
1565 "oplmsu: conf-stream: stream configuration start!"));
1566
1567 /* Find serial devices */
1568 devcnt = oplmsu_find_serial(&ser_dl);
1569 if ((devcnt == 0) || (ser_dl == NULL)) {
1570 cmn_err(CE_WARN, "oplmsu: conf-stream: "
1571 "Discovered serial device = %d", devcnt);
1572 return;
1573 }
1574
1575 /* Open oplmsu */
1576 rval = oplmsu_open_msu(msu_uinst->msu_dip, &msu_li, &msu_lh);
1577 if (rval != 0) {
1578 cmn_err(CE_WARN, "oplmsu: conf-stream: "
1579 "msu open failed. errno = %d", rval);
1580 return;
1581 }
1582
1583 size = (sizeof (struct msu_path) + (sizeof (struct msu_dev) * devcnt));
1584 mpath = (struct msu_path *)kmem_zalloc((size_t)size, KM_SLEEP);
1585 plink_id = (int *)kmem_zalloc((sizeof (int) * devcnt), KM_SLEEP);
1586
1587 mdev = (struct msu_dev *)(mpath + 1);
1588 for (i = 0; i < devcnt; i++) {
1589 /* Connect two streams */
1590 rval = oplmsu_plink_serial(ser_dl->dip, msu_lh, &plink_id[i]);
1591 if (rval != 0) {
1592 cmn_err(CE_WARN, "oplmsu: conf-stream: "
1593 "i_plink failed. errno = %d", rval);
1594 next_ser_dl = ser_dl->next;
1595 kmem_free(ser_dl, sizeof (ser_devl_t));
1596 ser_dl = next_ser_dl;
1597 continue;
1598 }
1599
1600 rval = oplmsu_set_lpathnum(plink_id[i],
1601 ddi_get_instance(ser_dl->dip));
1602 if (rval != 0) {
1603 cmn_err(CE_WARN, "oplmsu: conf-stream: "
1604 "Link id %d is not found", plink_id[i]);
1605 /* Issue ioctl(I_PUNLINK) */
1606 (void) ldi_ioctl(msu_lh, I_PUNLINK,
1607 (intptr_t)plink_id[i], FKIOCTL, kcred, ¶m);
1608 next_ser_dl = ser_dl->next;
1609 kmem_free(ser_dl, sizeof (ser_devl_t));
1610 ser_dl = next_ser_dl;
1611 continue;
1612 }
1613
1614 mdev->dip = ser_dl->dip;
1615 next_ser_dl = ser_dl->next;
1616 kmem_free(ser_dl, sizeof (ser_devl_t));
1617 ser_dl = next_ser_dl;
1618
1619 mdev++;
1620 connected++;
1621 }
1622
1623 if (connected == 0) {
1624 cmn_err(CE_WARN, "oplmsu: conf-stream: "
1625 "Connected paths = %d", connected);
1626 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1627 ldi_ident_release(msu_li);
1628 kmem_free(plink_id, (sizeof (int) * devcnt));
1629 kmem_free(mpath, size);
1630 return;
1631 }
1632
1633 /* Setup all structure */
1634 mpath->num = connected;
1635 rval = oplmsu_config_new(mpath);
1636 if (rval != 0) {
1637 cmn_err(CE_WARN, "oplmsu: conf-stream: "
1638 "Failed to create all paths. errno = %d", rval);
1639 oplmsu_unlinks(msu_lh, plink_id, devcnt);
1640 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1641 ldi_ident_release(msu_li);
1642 kmem_free(plink_id, (sizeof (int) * devcnt));
1643 kmem_free(mpath, size);
1644 return;
1645 }
1646
1647 /* Start to use all paths */
1648 rval = oplmsu_config_start(MSU_PATH_ALL);
1649 if (rval != 0) {
1650 cmn_err(CE_WARN, "oplmsu: conf-stream: "
1651 "Failed to start all paths. errno = %d", rval);
1652
1653 /* Delete the path */
1654 rval = oplmsu_config_del(mpath);
1655 if (rval == 0) {
1656 oplmsu_unlinks(msu_lh, plink_id, devcnt);
1657 }
1658 }
1659
1660 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred);
1661 ldi_ident_release(msu_li);
1662 kmem_free(plink_id, (sizeof (int) * devcnt));
1663 kmem_free(mpath, size);
1664
1665 DBG_PRINT((CE_NOTE, "oplmsu: conf-stream: stream configuration end!"));
1666 }
1667
1668 void
oplmsu_unlinks(ldi_handle_t msu_lh,int * plink_id,int devcnt)1669 oplmsu_unlinks(ldi_handle_t msu_lh, int *plink_id, int devcnt)
1670 {
1671 int i;
1672 int param = 0;
1673
1674 for (i = 0; i < devcnt; i++) {
1675 if (plink_id[i] == 0) {
1676 continue;
1677 }
1678
1679 /* Issue ioctl(I_PUNLINK) */
1680 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)plink_id[i],
1681 FKIOCTL, kcred, ¶m);
1682 }
1683 }
1684
1685 void
oplmsu_setup(uinst_t * msu_uinst)1686 oplmsu_setup(uinst_t *msu_uinst)
1687 {
1688
1689 DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread start!"));
1690
1691 mutex_enter(&oplmsu_bthrd_excl);
1692 if (oplmsu_conf_st == MSU_CONFIGURING) {
1693 mutex_exit(&oplmsu_bthrd_excl);
1694 oplmsu_conf_stream(msu_uinst); /* Configure stream */
1695 mutex_enter(&oplmsu_bthrd_excl);
1696 oplmsu_conf_st = MSU_CONFIGURED;
1697 cv_broadcast(&oplmsu_conf_cv); /* Wake up from cv_wait_sig() */
1698 }
1699
1700 if (oplmsu_bthrd_id != NULL) {
1701 oplmsu_bthrd_id = NULL;
1702 }
1703 mutex_exit(&oplmsu_bthrd_excl);
1704
1705 DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread end!"));
1706
1707 thread_exit();
1708 }
1709
1710 int
oplmsu_create_upath(dev_info_t * dip)1711 oplmsu_create_upath(dev_info_t *dip)
1712 {
1713 upath_t *upath;
1714 lpath_t *lpath;
1715 dev_info_t *cmuch_dip;
1716 int instance;
1717 int lsb;
1718
1719 cmuch_dip = ddi_get_parent(ddi_get_parent(dip));
1720 lsb = ddi_prop_get_int(DDI_DEV_T_ANY, cmuch_dip, 0, MSU_BOARD_PROP,
1721 FAILURE);
1722 if (lsb == FAILURE) {
1723 return (lsb);
1724 }
1725
1726 instance = ddi_get_instance(dip);
1727
1728 mutex_enter(&oplmsu_uinst->l_lock);
1729 lpath = oplmsu_uinst->first_lpath;
1730 while (lpath) {
1731 if (lpath->path_no == instance) {
1732 break;
1733 }
1734 lpath = lpath->l_next;
1735 }
1736
1737 if (lpath == NULL) {
1738 mutex_exit(&oplmsu_uinst->l_lock);
1739 return (ENODEV);
1740 }
1741
1742 upath = (upath_t *)kmem_zalloc(sizeof (upath_t), KM_SLEEP);
1743
1744 /*
1745 * Initialize members of upath_t
1746 */
1747
1748 upath->path_no = instance;
1749 upath->lpath = lpath;
1750 upath->ser_devcb.dip = dip;
1751 upath->ser_devcb.lsb = lsb;
1752 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, MSU_PSTAT_EMPTY,
1753 MSU_STOP);
1754
1755 lpath->src_upath = NULL;
1756 lpath->status = MSU_EXT_NOTUSED;
1757 mutex_exit(&oplmsu_uinst->l_lock);
1758
1759 oplmsu_link_upath(upath);
1760 return (SUCCESS);
1761 }
1762
1763 /* Setup new upper instance structure */
1764 int
oplmsu_config_new(struct msu_path * mpath)1765 oplmsu_config_new(struct msu_path *mpath)
1766 {
1767 struct msu_dev *mdev;
1768 int i;
1769 int rval = SUCCESS;
1770
1771 DBG_PRINT((CE_NOTE, "oplmsu: conf-new: config_new() called"));
1772 ASSERT(mpath);
1773
1774 if (mpath->num == 0) {
1775 cmn_err(CE_WARN, "oplmsu: conf-new: "
1776 "Number of paths = %d", mpath->num);
1777 return (EINVAL);
1778 }
1779
1780 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1781
1782 mutex_enter(&oplmsu_uinst->l_lock);
1783 rval = oplmsu_check_lpath_usable();
1784 mutex_exit(&oplmsu_uinst->l_lock);
1785
1786 if (rval == BUSY) { /* Check whether Lower path is usable */
1787 rw_exit(&oplmsu_uinst->lock);
1788 cmn_err(CE_WARN, "oplmsu: conf-new: "
1789 "Other processing is using this device");
1790 return (EBUSY);
1791 }
1792
1793 /*
1794 * Because the OPLMSU instance already exists when the upper path
1795 * table exists, the configure_new processing cannot be done.
1796 */
1797
1798 mutex_enter(&oplmsu_uinst->u_lock);
1799
1800 if ((oplmsu_uinst->first_upath != NULL) ||
1801 (oplmsu_uinst->last_upath != NULL)) {
1802 mutex_exit(&oplmsu_uinst->u_lock);
1803 rw_exit(&oplmsu_uinst->lock);
1804 cmn_err(CE_WARN, "oplmsu: conf-new: upath_t already exist");
1805 return (EINVAL);
1806 }
1807
1808 /*
1809 * Because the config_new processing has already been done
1810 * if oplmsu_uinst->path_num isn't -1, this processing cannot be
1811 * continued.
1812 */
1813
1814 if (oplmsu_uinst->path_num != UNDEFINED) {
1815 mutex_exit(&oplmsu_uinst->u_lock);
1816 rw_exit(&oplmsu_uinst->lock);
1817 cmn_err(CE_WARN, "oplmsu: conf-new: "
1818 "conf-new processing has already been completed");
1819 return (EINVAL);
1820 }
1821
1822 /*
1823 * Only the number of specified paths makes the upper path
1824 * information tables.
1825 */
1826
1827 mdev = (struct msu_dev *)(mpath + 1);
1828 for (i = 0; i < mpath->num; i++) {
1829 /*
1830 * Associate upper path information table with lower path
1831 * information table.
1832 *
1833 * If the upper path information table and the lower path
1834 * information table cannot be associated, the link list of
1835 * the upper path information table is released.
1836 */
1837 rval = oplmsu_create_upath(mdev->dip);
1838 if (rval != SUCCESS) {
1839 oplmsu_delete_upath_info();
1840 mutex_exit(&oplmsu_uinst->u_lock);
1841 rw_exit(&oplmsu_uinst->lock);
1842 cmn_err(CE_WARN, "oplmsu: conf-new: "
1843 "Failed to create upath %d", rval);
1844 return (rval);
1845 }
1846
1847 mdev++;
1848 }
1849
1850 /*
1851 * Setup members of uinst_t
1852 */
1853
1854 oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1855 oplmsu_uinst->path_num = mpath->num;
1856 oplmsu_uinst->lower_queue = NULL;
1857 mutex_exit(&oplmsu_uinst->u_lock);
1858 rw_exit(&oplmsu_uinst->lock);
1859 return (SUCCESS);
1860 }
1861
1862 /* Add path information */
1863 int
oplmsu_config_add(dev_info_t * dip)1864 oplmsu_config_add(dev_info_t *dip)
1865 {
1866 upath_t *upath;
1867 int instance;
1868 int rval = SUCCESS;
1869
1870 DBG_PRINT((CE_NOTE, "oplmsu: conf-add: config_add() called"));
1871 ASSERT(dip);
1872
1873 instance = ddi_get_instance(dip);
1874 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1875
1876 if (oplmsu_uinst->path_num == UNDEFINED) {
1877 rw_exit(&oplmsu_uinst->lock);
1878 cmn_err(CE_WARN, "oplmsu: conf-add: "
1879 "conf-new processing has not been completed yet");
1880 return (EINVAL);
1881 }
1882
1883 mutex_enter(&oplmsu_uinst->u_lock);
1884 upath = oplmsu_search_upath_info(instance);
1885 if (upath != NULL) {
1886 mutex_exit(&oplmsu_uinst->u_lock);
1887 rw_exit(&oplmsu_uinst->lock);
1888 cmn_err(CE_WARN, "oplmsu: conf-add: "
1889 "Proper upath_t doesn't find");
1890 return (EINVAL);
1891 }
1892
1893 rval = oplmsu_create_upath(dip);
1894 if (rval != SUCCESS) {
1895 mutex_exit(&oplmsu_uinst->u_lock);
1896 rw_exit(&oplmsu_uinst->lock);
1897 cmn_err(CE_WARN, "oplmsu: conf-add: "
1898 "Failed to create upath %d", rval);
1899 return (rval);
1900 }
1901
1902 oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1903 oplmsu_uinst->path_num = oplmsu_get_pathnum();
1904 mutex_exit(&oplmsu_uinst->u_lock);
1905 rw_exit(&oplmsu_uinst->lock);
1906 return (SUCCESS);
1907 }
1908
1909 /* Delete each path information */
1910 int
oplmsu_config_del(struct msu_path * mpath)1911 oplmsu_config_del(struct msu_path *mpath)
1912 {
1913 struct msu_dev *mdev;
1914 upath_t *upath;
1915 lpath_t *lpath;
1916 int rval = SUCCESS;
1917 int use_flag;
1918 int i;
1919
1920 DBG_PRINT((CE_NOTE, "oplmsu: conf-del: config_del() called"));
1921 ASSERT(mpath);
1922
1923 mdev = (struct msu_dev *)(mpath + 1);
1924
1925 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
1926 mutex_enter(&oplmsu_uinst->u_lock);
1927 for (i = 0; i < mpath->num; i++) {
1928 upath = oplmsu_search_upath_info(ddi_get_instance(mdev->dip));
1929 if (upath == NULL) {
1930 cmn_err(CE_WARN, "oplmsu: conf-del: "
1931 "Proper upath_t doesn't find");
1932 rval = ENODEV;
1933 mdev++;
1934 continue;
1935 }
1936
1937 lpath = upath->lpath;
1938 if (lpath == NULL) {
1939 if ((upath->traditional_status == MSU_WSTP_ACK) ||
1940 (upath->traditional_status == MSU_WSTR_ACK) ||
1941 (upath->traditional_status == MSU_WPTH_CHG) ||
1942 (upath->traditional_status == MSU_WTCS_ACK) ||
1943 (upath->traditional_status == MSU_WTMS_ACK) ||
1944 (upath->traditional_status == MSU_WPPS_ACK) ||
1945 (upath->traditional_status == MSU_WWSZ_ACK) ||
1946 (upath->traditional_status == MSU_WCAR_ACK)) {
1947 cmn_err(CE_WARN, "oplmsu: conf-del: "
1948 "Other processing is using this device");
1949 rval = EBUSY;
1950 mdev++;
1951 continue;
1952 }
1953
1954 if ((upath->status != MSU_PSTAT_DISCON) ||
1955 (upath->traditional_status != MSU_DISCON)) {
1956 cmn_err(CE_WARN, "oplmsu: conf-del: "
1957 "Status of path is improper");
1958 rval = EINVAL;
1959 mdev++;
1960 continue;
1961 }
1962 } else {
1963 mutex_enter(&oplmsu_uinst->l_lock);
1964 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
1965 if (use_flag == BUSY) {
1966 mutex_exit(&oplmsu_uinst->l_lock);
1967 cmn_err(CE_WARN, "oplmsu: conf-del: "
1968 "Other processing is using lower path");
1969 rval = EBUSY;
1970 mdev++;
1971 continue;
1972 }
1973
1974 if (((upath->status != MSU_PSTAT_STOP) ||
1975 (upath->traditional_status != MSU_STOP)) &&
1976 ((upath->status != MSU_PSTAT_FAIL) ||
1977 (upath->traditional_status != MSU_FAIL))) {
1978 oplmsu_clear_ioctl_path(lpath);
1979 mutex_exit(&oplmsu_uinst->l_lock);
1980 cmn_err(CE_WARN, "oplmsu: conf-del: "
1981 "Status of path isn't 'Offline:stop/fail'");
1982 rval = EINVAL;
1983 mdev++;
1984 continue;
1985 }
1986 lpath->src_upath = NULL;
1987 lpath->status = MSU_SETID_NU;
1988 oplmsu_clear_ioctl_path(lpath);
1989 mutex_exit(&oplmsu_uinst->l_lock);
1990 }
1991 oplmsu_unlink_upath(upath); /* Unlink upath_t */
1992 kmem_free(upath, sizeof (upath_t));
1993 mdev++;
1994 }
1995
1996 oplmsu_uinst->inst_status = oplmsu_get_inst_status();
1997 oplmsu_uinst->path_num = oplmsu_get_pathnum();
1998 mutex_exit(&oplmsu_uinst->u_lock);
1999 rw_exit(&oplmsu_uinst->lock);
2000 return (rval);
2001 }
2002
2003 /* Stop to use the path */
2004 int
oplmsu_config_stop(int pathnum)2005 oplmsu_config_stop(int pathnum)
2006 {
2007 upath_t *upath, *altn_upath;
2008 lpath_t *lpath, *altn_lpath;
2009 queue_t *stp_queue = NULL;
2010 queue_t *dst_queue = NULL;
2011 mblk_t *nmp = NULL, *fmp = NULL;
2012 ctrl_t *ctrl;
2013 int term_ioctl, term_stat;
2014 int use_flag;
2015
2016 DBG_PRINT((CE_NOTE,
2017 "oplmsu: conf-stop: config_stop(%d) called", pathnum));
2018
2019 if (pathnum == MSU_PATH_ALL) {
2020 cmn_err(CE_WARN, "oplmsu: conf-stop: "
2021 "All path can't be transferred to the status of "
2022 "'Offline:stop'");
2023 return (EINVAL);
2024 }
2025
2026 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
2027 mutex_enter(&oplmsu_uinst->u_lock);
2028
2029 upath = oplmsu_search_upath_info(pathnum); /* Search upath_t */
2030 if (upath == NULL) {
2031 mutex_exit(&oplmsu_uinst->u_lock);
2032 rw_exit(&oplmsu_uinst->lock);
2033 cmn_err(CE_WARN, "oplmsu: conf-stop: "
2034 "Proper upath_t doesn't find");
2035 return (ENODEV);
2036 }
2037
2038 lpath = upath->lpath;
2039 if (lpath == NULL) {
2040 mutex_exit(&oplmsu_uinst->u_lock);
2041 rw_exit(&oplmsu_uinst->lock);
2042 cmn_err(CE_WARN, "oplmsu: conf-stop: "
2043 "Proper lpath_t doesn't exist");
2044 return (ENODEV);
2045 }
2046
2047 mutex_enter(&oplmsu_uinst->l_lock);
2048
2049 /* Check status of lpath_t */
2050 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
2051 if (use_flag == BUSY) {
2052 mutex_exit(&oplmsu_uinst->l_lock);
2053 mutex_exit(&oplmsu_uinst->u_lock);
2054 rw_exit(&oplmsu_uinst->lock);
2055 cmn_err(CE_WARN, "oplmsu: conf-stop: "
2056 "Other processing is using lower path");
2057 return (EBUSY);
2058 }
2059
2060 if (upath->status == MSU_PSTAT_FAIL) {
2061 oplmsu_clear_ioctl_path(lpath);
2062 mutex_exit(&oplmsu_uinst->l_lock);
2063 mutex_exit(&oplmsu_uinst->u_lock);
2064 rw_exit(&oplmsu_uinst->lock);
2065 return (EIO);
2066 } else if ((upath->status == MSU_PSTAT_STOP) &&
2067 (upath->traditional_status == MSU_STOP)) {
2068 oplmsu_clear_ioctl_path(lpath);
2069 mutex_exit(&oplmsu_uinst->l_lock);
2070 mutex_exit(&oplmsu_uinst->u_lock);
2071 rw_exit(&oplmsu_uinst->lock);
2072 return (SUCCESS);
2073 } else if ((upath->status == MSU_PSTAT_STANDBY) &&
2074 (upath->traditional_status == MSU_STANDBY)) {
2075 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
2076 upath->status, MSU_STOP);
2077 oplmsu_clear_ioctl_path(lpath);
2078 lpath->src_upath = NULL;
2079 lpath->status = MSU_EXT_NOTUSED;
2080
2081 oplmsu_uinst->inst_status = oplmsu_get_inst_status();
2082 mutex_exit(&oplmsu_uinst->l_lock);
2083 mutex_exit(&oplmsu_uinst->u_lock);
2084 rw_exit(&oplmsu_uinst->lock);
2085 return (SUCCESS);
2086 } else if ((upath->status == MSU_PSTAT_ACTIVE) &&
2087 (upath->traditional_status == MSU_ACTIVE)) {
2088 altn_upath = oplmsu_search_standby();
2089 if (altn_upath == NULL) { /* Alternate path doesn't exist */
2090 DBG_PRINT((CE_NOTE, "oplmsu: conf-stop: "
2091 "Alternate upper path doesn't find"));
2092 oplmsu_clear_ioctl_path(lpath);
2093 mutex_exit(&oplmsu_uinst->l_lock);
2094 mutex_exit(&oplmsu_uinst->u_lock);
2095 rw_exit(&oplmsu_uinst->lock);
2096 return (EINVAL);
2097 }
2098
2099 if ((fmp = allocb(sizeof (char), BPRI_LO)) == NULL) {
2100 oplmsu_clear_ioctl_path(lpath);
2101 mutex_exit(&oplmsu_uinst->l_lock);
2102 mutex_exit(&oplmsu_uinst->u_lock);
2103 rw_exit(&oplmsu_uinst->lock);
2104 return (ENOSR);
2105 }
2106
2107 if (oplmsu_stop_prechg(&nmp, &term_ioctl, &term_stat) !=
2108 SUCCESS) {
2109 oplmsu_clear_ioctl_path(lpath);
2110 mutex_exit(&oplmsu_uinst->l_lock);
2111 mutex_exit(&oplmsu_uinst->u_lock);
2112 rw_exit(&oplmsu_uinst->lock);
2113 freeb(fmp);
2114 return (ENOSR);
2115 }
2116
2117 altn_lpath = altn_upath->lpath;
2118 use_flag = oplmsu_set_ioctl_path(altn_lpath, NULL, NULL);
2119 if (use_flag == BUSY) {
2120 oplmsu_clear_ioctl_path(lpath);
2121 mutex_exit(&oplmsu_uinst->l_lock);
2122 mutex_exit(&oplmsu_uinst->u_lock);
2123 rw_exit(&oplmsu_uinst->lock);
2124
2125 cmn_err(CE_WARN, "oplmsu: conf-stop: "
2126 "Other processing is using alternate lower path");
2127 freeb(fmp);
2128 freemsg(nmp);
2129 return (EBUSY);
2130 }
2131
2132 dst_queue = WR(altn_lpath->lower_queue);
2133
2134 /* termios is not held. Change alternate path to MSU_ACTIVE */
2135 if (nmp == NULL) {
2136 altn_upath->traditional_status = term_stat;
2137 altn_lpath->src_upath = upath;
2138 altn_lpath->status = MSU_EXT_VOID;
2139
2140 oplmsu_uinst->lower_queue = NULL;
2141
2142 ctrl = oplmsu_uinst->user_ctrl;
2143 if (ctrl != NULL) {
2144 mutex_enter(&oplmsu_uinst->c_lock);
2145 stp_queue = WR(ctrl->queue);
2146 mutex_exit(&oplmsu_uinst->c_lock);
2147 noenable(stp_queue);
2148 oplmsu_queue_flag = 1;
2149 }
2150
2151 /* Make M_FLUSH and send to alternate path */
2152 oplmsu_cmn_set_mflush(fmp);
2153 (void) putq(dst_queue, fmp);
2154
2155 /* Change status of alternate path */
2156 oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
2157 altn_upath->status, MSU_ACTIVE);
2158
2159 oplmsu_clear_ioctl_path(altn_lpath);
2160 altn_lpath->uinst = oplmsu_uinst;
2161 altn_lpath->src_upath = NULL;
2162 altn_lpath->status = MSU_EXT_NOTUSED;
2163
2164 /* Notify of the active path changing */
2165 (void) prom_opl_switch_console(
2166 altn_upath->ser_devcb.lsb);
2167
2168 /* Send XON to notify active path */
2169 (void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
2170
2171 /* Send XOFF to notify all standby paths */
2172 oplmsu_cmn_putxoff_standby();
2173
2174 oplmsu_uinst->lower_queue = RD(dst_queue);
2175 ctrl = oplmsu_uinst->user_ctrl;
2176
2177 /* Switch active path of oplmsu */
2178 if (ctrl != NULL) {
2179 queue_t *altn_queue;
2180
2181 mutex_enter(&oplmsu_uinst->c_lock);
2182 altn_queue = WR(ctrl->queue);
2183 mutex_exit(&oplmsu_uinst->c_lock);
2184
2185 /* Restart queuing of user access node */
2186 enableok(altn_queue);
2187
2188 oplmsu_queue_flag = 0;
2189 mutex_exit(&oplmsu_uinst->l_lock);
2190 mutex_exit(&oplmsu_uinst->u_lock);
2191 oplmsu_wcmn_high_qenable(altn_queue, RW_WRITER);
2192 mutex_enter(&oplmsu_uinst->u_lock);
2193 mutex_enter(&oplmsu_uinst->l_lock);
2194 }
2195
2196 /* Stop previous active path */
2197 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP,
2198 upath->status, MSU_STOP);
2199
2200 lpath->uinst = NULL;
2201 lpath->src_upath = NULL;
2202 lpath->status = MSU_EXT_NOTUSED;
2203 oplmsu_clear_ioctl_path(lpath);
2204
2205 oplmsu_uinst->inst_status = oplmsu_get_inst_status();
2206 mutex_exit(&oplmsu_uinst->l_lock);
2207 mutex_exit(&oplmsu_uinst->u_lock);
2208 rw_exit(&oplmsu_uinst->lock);
2209 return (SUCCESS);
2210 }
2211
2212 /* Send termios information to alternate path */
2213 if (canput(dst_queue)) {
2214 altn_upath->traditional_status = term_stat;
2215 altn_lpath->src_upath = upath;
2216 altn_lpath->status = MSU_EXT_VOID;
2217
2218 upath->traditional_status = MSU_WSTP_ACK;
2219 lpath->uinst = NULL;
2220
2221 oplmsu_uinst->lower_queue = NULL;
2222
2223 ctrl = oplmsu_uinst->user_ctrl;
2224 if (ctrl != NULL) {
2225 mutex_enter(&oplmsu_uinst->c_lock);
2226 stp_queue = WR(ctrl->queue);
2227 mutex_exit(&oplmsu_uinst->c_lock);
2228 noenable(stp_queue);
2229 oplmsu_queue_flag = 1;
2230 }
2231
2232 mutex_exit(&oplmsu_uinst->l_lock);
2233 mutex_exit(&oplmsu_uinst->u_lock);
2234 rw_exit(&oplmsu_uinst->lock);
2235 oplmsu_cmn_set_mflush(fmp);
2236 (void) putq(dst_queue, fmp);
2237 (void) putq(dst_queue, nmp);
2238
2239 mutex_enter(&oplmsu_uinst->l_lock);
2240 lpath->sw_flag = 1;
2241 while (lpath->sw_flag != 0) {
2242 /* Wait for the completion of path switching */
2243 cv_wait(&lpath->sw_cv, &oplmsu_uinst->l_lock);
2244 }
2245 mutex_exit(&oplmsu_uinst->l_lock);
2246 return (SUCCESS);
2247 } else {
2248 oplmsu_clear_ioctl_path(altn_lpath);
2249 oplmsu_clear_ioctl_path(lpath);
2250 mutex_exit(&oplmsu_uinst->l_lock);
2251 mutex_exit(&oplmsu_uinst->u_lock);
2252 rw_exit(&oplmsu_uinst->lock);
2253 freeb(fmp);
2254 freemsg(nmp);
2255 return (FAILURE);
2256 }
2257 /* NOTREACHED */
2258 } else {
2259 oplmsu_clear_ioctl_path(lpath);
2260 mutex_exit(&oplmsu_uinst->l_lock);
2261 mutex_exit(&oplmsu_uinst->u_lock);
2262 rw_exit(&oplmsu_uinst->lock);
2263
2264 cmn_err(CE_WARN, "oplmsu: conf-stop: "
2265 "Status of path is improper");
2266 return (EINVAL);
2267 }
2268 /* NOTREACHED */
2269 }
2270
2271 /* Start to use path */
2272 int
oplmsu_config_start(int pathnum)2273 oplmsu_config_start(int pathnum)
2274 {
2275 upath_t *upath = NULL;
2276 lpath_t *lpath = NULL;
2277 queue_t *dst_queue, *main_rq = NULL;
2278 int msu_tty_port;
2279
2280 DBG_PRINT((CE_NOTE,
2281 "oplmsu: conf-start: config_start(%d) called", pathnum));
2282
2283 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
2284 mutex_enter(&oplmsu_uinst->u_lock);
2285
2286 if (oplmsu_get_inst_status() == INST_STAT_BUSY) {
2287 mutex_exit(&oplmsu_uinst->u_lock);
2288 rw_exit(&oplmsu_uinst->lock);
2289 return (EBUSY);
2290 }
2291
2292 if (pathnum == MSU_PATH_ALL) {
2293 (void) oplmsu_search_min_stop_path();
2294 }
2295
2296 for (upath = oplmsu_uinst->first_upath; upath; ) {
2297 if ((pathnum != MSU_PATH_ALL) && (upath->path_no != pathnum)) {
2298 upath = upath->u_next;
2299 continue;
2300 }
2301
2302 if (upath->path_no == pathnum) {
2303 lpath = upath->lpath;
2304 if (lpath == NULL) {
2305 mutex_exit(&oplmsu_uinst->u_lock);
2306 rw_exit(&oplmsu_uinst->lock);
2307 cmn_err(CE_WARN, "oplmsu: conf-start: "
2308 "Proper lpath_t doesn't exist");
2309 return (EINVAL);
2310 }
2311
2312 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
2313 upath->status, MSU_STANDBY);
2314
2315 mutex_enter(&oplmsu_uinst->l_lock);
2316 lpath->src_upath = NULL;
2317 lpath->status = MSU_EXT_NOTUSED;
2318 mutex_exit(&oplmsu_uinst->l_lock);
2319 mutex_exit(&oplmsu_uinst->u_lock);
2320 rw_exit(&oplmsu_uinst->lock);
2321 return (SUCCESS);
2322 }
2323
2324 /*
2325 * with PATH_ALL
2326 */
2327 lpath = upath->lpath;
2328 if (lpath == NULL) {
2329 upath = upath->u_next;
2330
2331 DBG_PRINT((CE_WARN, "oplmsu: conf-start: "
2332 "Proper lpath_t doesn't exist"));
2333 continue;
2334 }
2335
2336 msu_tty_port = ddi_prop_get_int(DDI_DEV_T_ANY,
2337 oplmsu_uinst->msu_dip, 0, MSU_TTY_PORT_PROP, -1);
2338
2339 if (upath->ser_devcb.lsb == msu_tty_port) {
2340 /* Notify of the active path changing */
2341 (void) prom_opl_switch_console(upath->ser_devcb.lsb);
2342
2343 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE,
2344 upath->status, MSU_ACTIVE);
2345
2346 mutex_enter(&oplmsu_uinst->l_lock);
2347 main_rq = RD(lpath->lower_queue);
2348 dst_queue = WR(lpath->lower_queue);
2349 lpath->src_upath = NULL;
2350 lpath->status = MSU_EXT_NOTUSED;
2351 lpath->uinst = oplmsu_uinst;
2352 mutex_exit(&oplmsu_uinst->l_lock);
2353
2354 /* Send XON to notify active path */
2355 (void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4);
2356 } else {
2357 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY,
2358 upath->status, MSU_STANDBY);
2359
2360 mutex_enter(&oplmsu_uinst->l_lock);
2361 lpath->src_upath = NULL;
2362 lpath->status = MSU_EXT_NOTUSED;
2363 mutex_exit(&oplmsu_uinst->l_lock);
2364 }
2365 upath = upath->u_next;
2366 }
2367
2368 if (main_rq == NULL) {
2369 upath_t *altn_upath;
2370 lpath_t *altn_lpath;
2371
2372 altn_upath = oplmsu_search_standby();
2373 if (altn_upath) {
2374 oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE,
2375 altn_upath->status, MSU_ACTIVE);
2376
2377 /* Notify of the active path changing */
2378 (void) prom_opl_switch_console(
2379 altn_upath->ser_devcb.lsb);
2380
2381 altn_lpath = altn_upath->lpath;
2382 if (altn_lpath) {
2383 mutex_enter(&oplmsu_uinst->l_lock);
2384 main_rq = RD(altn_lpath->lower_queue);
2385 dst_queue = WR(altn_lpath->lower_queue);
2386 altn_lpath->src_upath = NULL;
2387 altn_lpath->status = MSU_EXT_NOTUSED;
2388 altn_lpath->uinst = oplmsu_uinst;
2389 mutex_exit(&oplmsu_uinst->l_lock);
2390
2391 /* Send XON to notify active path */
2392 (void) oplmsu_cmn_put_xoffxon(dst_queue,
2393 MSU_XON_4);
2394 } else {
2395 cmn_err(CE_WARN, "oplmsu: conf-start: "
2396 "Proper alternate lpath_t doesn't exist");
2397 }
2398 } else {
2399 cmn_err(CE_WARN, "oplmsu: conf-start: "
2400 "Proper alternate upath_t doesn't exist");
2401 }
2402 }
2403
2404 mutex_enter(&oplmsu_uinst->l_lock);
2405
2406 /* Send XOFF to notify all standby paths */
2407 oplmsu_cmn_putxoff_standby();
2408
2409 /* Change active path of oplmsu */
2410 oplmsu_uinst->lower_queue = main_rq;
2411 oplmsu_uinst->inst_status = oplmsu_get_inst_status();
2412 mutex_exit(&oplmsu_uinst->l_lock);
2413 mutex_exit(&oplmsu_uinst->u_lock);
2414 rw_exit(&oplmsu_uinst->lock);
2415 return (SUCCESS);
2416 }
2417
2418 /* Prepare of unlink path */
2419 int
oplmsu_config_disc(int pathnum)2420 oplmsu_config_disc(int pathnum)
2421 {
2422 upath_t *upath;
2423 lpath_t *lpath;
2424 int use_flag;
2425
2426 DBG_PRINT((CE_NOTE,
2427 "oplmsu: conf-disc: config_disc(%d) called", pathnum));
2428
2429 rw_enter(&oplmsu_uinst->lock, RW_READER);
2430 mutex_enter(&oplmsu_uinst->u_lock);
2431
2432 upath = oplmsu_search_upath_info(pathnum);
2433 if (upath == NULL) {
2434 mutex_exit(&oplmsu_uinst->u_lock);
2435 rw_exit(&oplmsu_uinst->lock);
2436 cmn_err(CE_WARN, "oplmsu: conf-disc: "
2437 "Proper upath_t doesn't find");
2438 return (EINVAL);
2439 }
2440
2441 if ((upath->status == MSU_PSTAT_DISCON) ||
2442 (upath->traditional_status == MSU_DISCON)) {
2443 mutex_exit(&oplmsu_uinst->u_lock);
2444 rw_exit(&oplmsu_uinst->lock);
2445 return (SUCCESS);
2446 } else if (((upath->status != MSU_PSTAT_STOP) ||
2447 (upath->traditional_status != MSU_STOP)) &&
2448 ((upath->status != MSU_PSTAT_FAIL) ||
2449 (upath->traditional_status != MSU_FAIL))) {
2450 mutex_exit(&oplmsu_uinst->u_lock);
2451 rw_exit(&oplmsu_uinst->lock);
2452 cmn_err(CE_WARN, "oplmsu: conf-disc: "
2453 "Status of path is improper");
2454 return (EINVAL);
2455 }
2456
2457 lpath = upath->lpath;
2458 if (lpath == NULL) {
2459 mutex_exit(&oplmsu_uinst->u_lock);
2460 rw_exit(&oplmsu_uinst->lock);
2461 cmn_err(CE_WARN, "oplmsu: conf-disc: "
2462 "Proper lpath_t doesn't exist");
2463 return (ENODEV);
2464 }
2465
2466 mutex_enter(&oplmsu_uinst->l_lock);
2467
2468 /* Check lower path status */
2469 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL);
2470 if (use_flag == BUSY) {
2471 mutex_exit(&oplmsu_uinst->l_lock);
2472 mutex_exit(&oplmsu_uinst->u_lock);
2473 rw_exit(&oplmsu_uinst->lock);
2474 cmn_err(CE_WARN, "oplmsu: conf-disc: "
2475 "Other processing is using lower path");
2476 return (EBUSY);
2477 }
2478
2479 upath->status = MSU_PSTAT_STOP;
2480 upath->traditional_status = MSU_SETID;
2481
2482 oplmsu_clear_ioctl_path(lpath);
2483 mutex_exit(&oplmsu_uinst->l_lock);
2484 mutex_exit(&oplmsu_uinst->u_lock);
2485 rw_exit(&oplmsu_uinst->lock);
2486 return (SUCCESS);
2487 }
2488