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 			/* FALLTHROUGH */
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