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