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 #include <sys/errno.h>
27 #include <sys/modctl.h>
28 #include <sys/stat.h>
29 #include <sys/kmem.h>
30 #include <sys/ksynch.h>
31 #include <sys/stream.h>
32 #include <sys/stropts.h>
33 #include <sys/termio.h>
34 #include <sys/ddi.h>
35 #include <sys/file.h>
36 #include <sys/disp.h>
37 #include <sys/sunddi.h>
38 #include <sys/sunldi.h>
39 #include <sys/sunndi.h>
40 #include <sys/oplmsu/oplmsu.h>
41 #include <sys/oplmsu/oplmsu_proto.h>
42
43 /*
44 * UPPER WRITE SERVICE PROCEDURE
45 */
46
47 /* I_PLINK ioctl command received */
48 int
oplmsu_uwioctl_iplink(queue_t * uwq,mblk_t * mp)49 oplmsu_uwioctl_iplink(queue_t *uwq, mblk_t *mp)
50 {
51 struct linkblk *lbp;
52 lpath_t *lpath;
53 int ncode;
54
55 if (mp == NULL) {
56 return (EINVAL);
57 }
58
59 if ((mp->b_cont->b_wptr - mp->b_cont->b_rptr) <
60 sizeof (struct linkblk)) {
61 cmn_err(CE_WARN, "oplmsu: uw-iplink: Invalid data length");
62 oplmsu_iocack(uwq, mp, EINVAL);
63 return (EINVAL);
64 }
65
66 lbp = (struct linkblk *)mp->b_cont->b_rptr;
67 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
68
69 /*
70 * Check whether this is called by super-user privilege.
71 * uwq => Queue of meta control node
72 */
73
74 ncode = oplmsu_wcmn_chknode(uwq, MSU_NODE_META, mp);
75 if (ncode != SUCCESS) {
76 rw_exit(&oplmsu_uinst->lock);
77 oplmsu_iocack(uwq, mp, ncode);
78 return (ncode);
79 }
80
81 /* Allocate kernel memory for lpath_t */
82 lpath = (lpath_t *)kmem_zalloc(sizeof (lpath_t), KM_NOSLEEP);
83 if (lpath == NULL) {
84 rw_exit(&oplmsu_uinst->lock);
85 cmn_err(CE_WARN, "oplmsu: uw-iplink: "
86 "Failed to allocate kernel memory");
87 oplmsu_iocack(uwq, mp, ENOMEM);
88 return (ENOMEM);
89 }
90
91 /*
92 * Initialize members of lpath_t
93 */
94
95 lpath->rbuftbl =
96 (struct buf_tbl *)kmem_zalloc(sizeof (struct buf_tbl), KM_NOSLEEP);
97 if (lpath->rbuftbl == NULL) {
98 rw_exit(&oplmsu_uinst->lock);
99 kmem_free(lpath, sizeof (lpath_t));
100 cmn_err(CE_WARN, "oplmsu: uw-iplink: "
101 "Failed to allocate kernel memory");
102 oplmsu_iocack(uwq, mp, ENOMEM);
103 return (ENOMEM);
104 }
105
106 cv_init(&lpath->sw_cv, "oplmsu lpath condvar", CV_DRIVER, NULL);
107 lpath->src_upath = NULL;
108 lpath->status = MSU_EXT_NOTUSED;
109 lpath->lower_queue = lbp->l_qbot; /* Set lower queue pointer */
110 lpath->link_id = lbp->l_index; /* Set Link-ID */
111 lpath->path_no = UNDEFINED; /* Set initial path number */
112 lpath->abt_char = oplmsu_uinst->abts; /* Set abort character seq */
113
114 WR(lpath->lower_queue)->q_ptr = lpath;
115 RD(lpath->lower_queue)->q_ptr = lpath;
116
117 oplmsu_link_lpath(lpath); /* Link lpath_t */
118 rw_exit(&oplmsu_uinst->lock);
119 oplmsu_iocack(uwq, mp, 0);
120 return (SUCCESS);
121 }
122
123 /* I_PUNLINK ioctl command received */
124 int
oplmsu_uwioctl_ipunlink(queue_t * uwq,mblk_t * mp)125 oplmsu_uwioctl_ipunlink(queue_t *uwq, mblk_t *mp)
126 {
127 struct linkblk *lbp;
128 upath_t *upath;
129 lpath_t *lpath;
130 mblk_t *hmp = NULL, *next_hmp = NULL;
131 bufcall_id_t rbuf_id;
132 timeout_id_t rtout_id;
133 int ncode;
134 int use_flag;
135
136 if (mp == NULL) {
137 return (EINVAL);
138 }
139
140 if ((mp->b_cont->b_wptr - mp->b_cont->b_rptr) <
141 sizeof (struct linkblk)) {
142 cmn_err(CE_WARN, "oplmsu: uw-ipunlink: Invalid data length");
143 oplmsu_iocack(uwq, mp, ENOSR);
144 return (ENOSR);
145 }
146
147 lbp = (struct linkblk *)mp->b_cont->b_rptr;
148 rw_enter(&oplmsu_uinst->lock, RW_WRITER);
149
150 /*
151 * Check whether this is called by super-user privilege.
152 * uwq => Queue of meta control node
153 */
154
155 ncode = oplmsu_wcmn_chknode(uwq, MSU_NODE_META, mp);
156 if (ncode != SUCCESS) {
157 rw_exit(&oplmsu_uinst->lock);
158 oplmsu_iocack(uwq, mp, ncode);
159 return (ncode);
160 }
161
162 mutex_enter(&oplmsu_uinst->u_lock);
163 mutex_enter(&oplmsu_uinst->l_lock);
164
165 /*
166 * Search for a corresponding lower path information table to
167 * lbp->l_qbot from the lower queue address.
168 */
169
170 lpath = oplmsu_uinst->first_lpath;
171 while (lpath) {
172 if ((lpath->lower_queue == RD(lbp->l_qbot)) ||
173 (lpath->lower_queue == WR(lbp->l_qbot))) {
174 break;
175 }
176 lpath = lpath->l_next;
177 }
178
179 if (lpath == NULL) {
180 mutex_exit(&oplmsu_uinst->l_lock);
181 mutex_exit(&oplmsu_uinst->u_lock);
182 rw_exit(&oplmsu_uinst->lock);
183 cmn_err(CE_WARN, "oplmsu: uw-ipunlink: "
184 "Proper lpath_t doesn't find");
185 oplmsu_iocack(uwq, mp, EINVAL);
186 return (EINVAL);
187 }
188
189 /* lpath_t come into the busy status */
190 use_flag = oplmsu_set_ioctl_path(lpath, uwq, NULL);
191 if (use_flag == BUSY) {
192 mutex_exit(&oplmsu_uinst->l_lock);
193 mutex_exit(&oplmsu_uinst->u_lock);
194 rw_exit(&oplmsu_uinst->lock);
195 cmn_err(CE_WARN, "oplmsu: uw-ipunlink: "
196 "Other processing is using lower path");
197 oplmsu_iocack(uwq, mp, EBUSY);
198 return (EBUSY);
199 }
200
201 /* upath_t is retrieved by using the path number */
202 upath = oplmsu_search_upath_info(lpath->path_no);
203 if (upath != NULL) { /* When the upath_t exists */
204 switch (upath->status) {
205 case MSU_PSTAT_STOP : /* FALLTHRU */
206 case MSU_PSTAT_FAIL :
207 /*
208 * When traditional_status is MSU_SETID, the path
209 * status is changed into the state of disconnect.
210 */
211
212 if (upath->traditional_status == MSU_SETID) {
213 oplmsu_cmn_set_upath_sts(upath,
214 MSU_PSTAT_DISCON, upath->status,
215 MSU_DISCON);
216 upath->lpath = NULL;
217 break;
218 }
219
220 /*
221 * When traditional_status isn't MSU_SETID,
222 * the error is reported.
223 */
224
225 default :
226 /*
227 * When upath->status isn't MSU_PSTAT_STOP or
228 * MSU_PSTAT_FAIL, the error is reported.
229 */
230
231 oplmsu_clear_ioctl_path(lpath);
232 mutex_exit(&oplmsu_uinst->l_lock);
233 cmn_err(CE_WARN, "oplmsu: uw-ipunlink: "
234 "trad_status = %lx", upath->traditional_status);
235 cmn_err(CE_WARN, "oplmsu: uw-ipunlink: "
236 "status = %d", upath->status);
237 mutex_exit(&oplmsu_uinst->u_lock);
238 rw_exit(&oplmsu_uinst->lock);
239 oplmsu_iocack(uwq, mp, EINVAL);
240 return (EINVAL);
241 }
242 } else {
243 /*
244 * This pattern is no upper info table before config_add or
245 * after config_del.
246 */
247
248 /*
249 * When the upper path table doesn't exist, path is deleted
250 * with config_del/config_add ioctl processed.
251 */
252
253 if ((lpath->status != MSU_LINK_NU) &&
254 (lpath->status != MSU_SETID_NU)) {
255 oplmsu_clear_ioctl_path(lpath);
256 mutex_exit(&oplmsu_uinst->l_lock);
257 mutex_exit(&oplmsu_uinst->u_lock);
258 rw_exit(&oplmsu_uinst->lock);
259 oplmsu_iocack(uwq, mp, EINVAL);
260 return (EINVAL);
261 }
262 }
263
264 oplmsu_uinst->inst_status = oplmsu_get_inst_status();
265 oplmsu_clear_ioctl_path(lpath);
266
267 /* Free high priority message */
268 if (lpath->first_lpri_hi != NULL) {
269 cmn_err(CE_WARN, "oplmsu: uw-ipunlink: "
270 "Free high-priority message by unlinking lower path");
271
272 for (hmp = lpath->first_lpri_hi; hmp; ) {
273 next_hmp = hmp->b_next;
274 freemsg(hmp);
275 hmp = next_hmp;
276 }
277 lpath->first_lpri_hi = NULL;
278 lpath->last_lpri_hi = NULL;
279 }
280
281 rbuf_id = lpath->rbuf_id;
282 rtout_id = lpath->rtout_id;
283 lpath->rbuf_id = 0;
284 lpath->rtout_id = 0;
285
286 kmem_free(lpath->rbuftbl, sizeof (struct buf_tbl));
287 lpath->rbuftbl = NULL;
288 cv_destroy(&lpath->sw_cv);
289 oplmsu_unlink_lpath(lpath);
290 kmem_free(lpath, sizeof (lpath_t));
291
292 mutex_exit(&oplmsu_uinst->l_lock);
293 mutex_exit(&oplmsu_uinst->u_lock);
294 rw_exit(&oplmsu_uinst->lock);
295
296 if (rbuf_id != 0) {
297 unbufcall(rbuf_id);
298 }
299
300 if (rtout_id != 0) {
301 (void) untimeout(rtout_id);
302 }
303 oplmsu_iocack(uwq, mp, 0);
304 return (SUCCESS);
305 }
306
307 /* termio ioctl command received */
308 int
oplmsu_uwioctl_termios(queue_t * uwq,mblk_t * mp)309 oplmsu_uwioctl_termios(queue_t *uwq, mblk_t *mp)
310 {
311 struct iocblk *iocp = NULL;
312 queue_t *dst_queue;
313 upath_t *upath = NULL;
314 lpath_t *lpath = NULL;
315 mblk_t *nmp = NULL;
316 ctrl_t *ctrl;
317 int term_stat;
318 int use_flag;
319
320 if (mp == NULL) {
321 return (EINVAL);
322 }
323
324 if (mp->b_cont == NULL) {
325 cmn_err(CE_WARN, "oplmsu: uw-termios: "
326 "b_cont data block is NULL");
327 oplmsu_iocack(uwq, mp, EINVAL);
328 return (FAILURE);
329 }
330
331 if (mp->b_cont->b_rptr == NULL) {
332 cmn_err(CE_WARN, "oplmsu: uw-termios: "
333 "b_rptr data pointer is NULL");
334 oplmsu_iocack(uwq, mp, EINVAL);
335 return (EINVAL);
336 }
337
338 iocp = (struct iocblk *)mp->b_rptr;
339 rw_enter(&oplmsu_uinst->lock, RW_READER);
340
341 /*
342 * Check control node type
343 * uwq : Queue of user control node
344 */
345
346 mutex_enter(&oplmsu_uinst->c_lock);
347 ctrl = (ctrl_t *)uwq->q_ptr;
348 if (ctrl != NULL) {
349 if (ctrl->node_type != MSU_NODE_USER) {
350 mutex_exit(&oplmsu_uinst->c_lock);
351 rw_exit(&oplmsu_uinst->lock);
352 cmn_err(CE_WARN, "oplmsu: uw-termios: "
353 "ctrl node type = %d", ctrl->node_type);
354 oplmsu_iocack(uwq, mp, EINVAL);
355 return (EINVAL);
356 }
357 }
358 mutex_exit(&oplmsu_uinst->c_lock);
359
360 switch (iocp->ioc_cmd) {
361 case TCSETS : /* FALLTHRU */
362 case TCSETSW : /* FALLTHRU */
363 case TCSETSF :
364 term_stat = MSU_WTCS_ACK;
365 break;
366
367 case TIOCMSET :
368 term_stat = MSU_WTMS_ACK;
369 break;
370
371 case TIOCSPPS :
372 term_stat = MSU_WPPS_ACK;
373 break;
374
375 case TIOCSWINSZ :
376 term_stat = MSU_WWSZ_ACK;
377 break;
378
379 case TIOCSSOFTCAR :
380 term_stat = MSU_WCAR_ACK;
381 break;
382
383 default :
384 rw_exit(&oplmsu_uinst->lock);
385 cmn_err(CE_WARN, "oplmsu: uw-termios: ioctl mismatch");
386 oplmsu_iocack(uwq, mp, EINVAL);
387 return (EINVAL);
388 }
389
390 if (oplmsu_uinst->lower_queue == NULL) {
391 rw_exit(&oplmsu_uinst->lock);
392 cmn_err(CE_WARN, "!oplmsu: uw-termios: "
393 "Active path doesn't exist");
394 oplmsu_iocack(uwq, mp, ENODEV);
395 return (FAILURE);
396 }
397
398 lpath = oplmsu_uinst->lower_queue->q_ptr;
399 if (lpath == NULL) {
400 rw_exit(&oplmsu_uinst->lock);
401 cmn_err(CE_WARN, "oplmsu: uw-termios: "
402 "Proper lpath_t doesn't exist");
403 oplmsu_iocack(uwq, mp, EINVAL);
404 return (EINVAL);
405 }
406
407 if (oplmsu_cmn_copymb(uwq, mp, &nmp, mp, MSU_WRITE_SIDE) == FAILURE) {
408 rw_exit(&oplmsu_uinst->lock);
409 return (FAILURE);
410 }
411
412 mutex_enter(&oplmsu_uinst->u_lock);
413 mutex_enter(&oplmsu_uinst->l_lock);
414
415 upath = oplmsu_search_upath_info(lpath->path_no);
416 if (upath == NULL) {
417 mutex_exit(&oplmsu_uinst->l_lock);
418 mutex_exit(&oplmsu_uinst->u_lock);
419 rw_exit(&oplmsu_uinst->lock);
420 cmn_err(CE_WARN, "oplmsu: uw-termios: "
421 "Proper upath_t doesn't find");
422 oplmsu_iocack(uwq, mp, EINVAL);
423 return (EINVAL);
424 }
425
426 /* Set ioctl command to lower path info table */
427 use_flag = oplmsu_set_ioctl_path(lpath, uwq, mp);
428 if (use_flag == BUSY) {
429 mutex_exit(&oplmsu_uinst->l_lock);
430 mutex_exit(&oplmsu_uinst->u_lock);
431 freemsg(nmp);
432
433 if (ctrl != NULL) {
434 mutex_enter(&oplmsu_uinst->c_lock);
435 ctrl->wait_queue = uwq;
436 mutex_exit(&oplmsu_uinst->c_lock);
437 rw_exit(&oplmsu_uinst->lock);
438
439 (void) putbq(uwq, mp);
440 return (SUCCESS);
441 } else {
442 rw_exit(&oplmsu_uinst->lock);
443 oplmsu_iocack(uwq, mp, EBUSY);
444 return (EBUSY);
445 }
446 }
447
448 /* Set destination queue (active path) */
449 dst_queue = WR(oplmsu_uinst->lower_queue);
450 if (canput(dst_queue)) {
451 lpath->src_upath = NULL;
452 lpath->status = upath->traditional_status;
453 upath->traditional_status = term_stat;
454 mutex_exit(&oplmsu_uinst->l_lock);
455 mutex_exit(&oplmsu_uinst->u_lock);
456 rw_exit(&oplmsu_uinst->lock);
457
458 (void) putq(dst_queue, nmp);
459 return (SUCCESS);
460 } else {
461 oplmsu_clear_ioctl_path(lpath);
462 mutex_exit(&oplmsu_uinst->l_lock);
463 mutex_exit(&oplmsu_uinst->u_lock);
464
465 freemsg(nmp);
466 oplmsu_wcmn_norm_putbq(WR(uwq), mp, dst_queue);
467 rw_exit(&oplmsu_uinst->lock);
468 return (FAILURE);
469 }
470 }
471