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