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