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 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 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 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