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 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Data-Link Driver 30 */ 31 32 #include <sys/conf.h> 33 #include <sys/mkdev.h> 34 #include <sys/modctl.h> 35 #include <sys/stat.h> 36 #include <sys/strsun.h> 37 #include <sys/dld.h> 38 #include <sys/dld_impl.h> 39 #include <sys/dls_impl.h> 40 #include <inet/common.h> 41 42 /* 43 * dld control node state, one per open control node session. 44 */ 45 typedef struct dld_ctl_str_s { 46 minor_t cs_minor; 47 queue_t *cs_wq; 48 } dld_ctl_str_t; 49 50 static void drv_init(void); 51 static int drv_fini(void); 52 53 static int drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 54 static int drv_attach(dev_info_t *, ddi_attach_cmd_t); 55 static int drv_detach(dev_info_t *, ddi_detach_cmd_t); 56 57 /* 58 * The following entry points are private to dld and are used for control 59 * operations only. The entry points exported to mac drivers are defined 60 * in dld_str.c. Refer to the comment on top of dld_str.c for details. 61 */ 62 static int drv_open(queue_t *, dev_t *, int, int, cred_t *); 63 static int drv_close(queue_t *); 64 65 static void drv_uw_put(queue_t *, mblk_t *); 66 static void drv_uw_srv(queue_t *); 67 68 dev_info_t *dld_dip; /* dev_info_t for the driver */ 69 uint32_t dld_opt = 0; /* Global options */ 70 static vmem_t *dld_ctl_vmem; /* for control minor numbers */ 71 72 static struct module_info drv_info = { 73 0, /* mi_idnum */ 74 DLD_DRIVER_NAME, /* mi_idname */ 75 0, /* mi_minpsz */ 76 (64 * 1024), /* mi_maxpsz */ 77 1, /* mi_hiwat */ 78 0 /* mi_lowat */ 79 }; 80 81 static struct qinit drv_ur_init = { 82 NULL, /* qi_putp */ 83 NULL, /* qi_srvp */ 84 drv_open, /* qi_qopen */ 85 drv_close, /* qi_qclose */ 86 NULL, /* qi_qadmin */ 87 &drv_info, /* qi_minfo */ 88 NULL /* qi_mstat */ 89 }; 90 91 static struct qinit drv_uw_init = { 92 (pfi_t)drv_uw_put, /* qi_putp */ 93 (pfi_t)drv_uw_srv, /* qi_srvp */ 94 NULL, /* qi_qopen */ 95 NULL, /* qi_qclose */ 96 NULL, /* qi_qadmin */ 97 &drv_info, /* qi_minfo */ 98 NULL /* qi_mstat */ 99 }; 100 101 static struct streamtab drv_stream = { 102 &drv_ur_init, /* st_rdinit */ 103 &drv_uw_init, /* st_wrinit */ 104 NULL, /* st_muxrinit */ 105 NULL /* st_muxwinit */ 106 }; 107 108 DDI_DEFINE_STREAM_OPS(drv_ops, nulldev, nulldev, drv_attach, drv_detach, 109 nodev, drv_getinfo, D_MP, &drv_stream); 110 111 /* 112 * Module linkage information for the kernel. 113 */ 114 115 extern struct mod_ops mod_driverops; 116 117 static struct modldrv drv_modldrv = { 118 &mod_driverops, 119 DLD_INFO, 120 &drv_ops 121 }; 122 123 static struct modlinkage drv_modlinkage = { 124 MODREV_1, 125 &drv_modldrv, 126 NULL 127 }; 128 129 int 130 _init(void) 131 { 132 int err; 133 134 drv_init(); 135 136 if ((err = mod_install(&drv_modlinkage)) != 0) 137 return (err); 138 139 return (0); 140 } 141 142 int 143 _fini(void) 144 { 145 int err; 146 147 if ((err = mod_remove(&drv_modlinkage)) != 0) 148 return (err); 149 150 if (drv_fini() != 0) { 151 (void) mod_install(&drv_modlinkage); 152 return (DDI_FAILURE); 153 } 154 155 return (err); 156 } 157 158 int 159 _info(struct modinfo *modinfop) 160 { 161 return (mod_info(&drv_modlinkage, modinfop)); 162 } 163 164 165 /* 166 * Initialize component modules. 167 */ 168 static void 169 drv_init(void) 170 { 171 dld_ctl_vmem = vmem_create("dld_ctl", (void *)1, MAXMIN, 1, 172 NULL, NULL, NULL, 1, VM_SLEEP | VMC_IDENTIFIER); 173 dld_str_init(); 174 } 175 176 static int 177 drv_fini(void) 178 { 179 int err; 180 181 if ((err = dld_str_fini()) != 0) 182 return (err); 183 184 vmem_destroy(dld_ctl_vmem); 185 return (0); 186 } 187 188 /* 189 * devo_getinfo: getinfo(9e) 190 */ 191 /*ARGSUSED*/ 192 static int 193 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp) 194 { 195 if (dld_dip == NULL) 196 return (DDI_FAILURE); 197 198 switch (cmd) { 199 case DDI_INFO_DEVT2INSTANCE: 200 *resp = (void *)0; 201 break; 202 case DDI_INFO_DEVT2DEVINFO: 203 *resp = (void *)dld_dip; 204 break; 205 default: 206 return (DDI_FAILURE); 207 } 208 209 return (DDI_SUCCESS); 210 } 211 212 /* 213 * Check properties to set options. (See dld.h for property definitions). 214 */ 215 static void 216 drv_set_opt(dev_info_t *dip) 217 { 218 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 219 DLD_PROP_NO_FASTPATH, 0) != 0) { 220 dld_opt |= DLD_OPT_NO_FASTPATH; 221 } 222 223 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 224 DLD_PROP_NO_POLL, 0) != 0) { 225 dld_opt |= DLD_OPT_NO_POLL; 226 } 227 228 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 229 DLD_PROP_NO_ZEROCOPY, 0) != 0) { 230 dld_opt |= DLD_OPT_NO_ZEROCOPY; 231 } 232 } 233 234 /* 235 * devo_attach: attach(9e) 236 */ 237 static int 238 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 239 { 240 if (cmd != DDI_ATTACH) 241 return (DDI_FAILURE); 242 243 ASSERT(ddi_get_instance(dip) == 0); 244 245 drv_set_opt(dip); 246 247 /* 248 * Create control node. DLPI provider nodes will be created on demand. 249 */ 250 if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR, 251 DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) 252 return (DDI_FAILURE); 253 254 dld_dip = dip; 255 256 /* 257 * Log the fact that the driver is now attached. 258 */ 259 ddi_report_dev(dip); 260 return (DDI_SUCCESS); 261 } 262 263 /* 264 * devo_detach: detach(9e) 265 */ 266 static int 267 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 268 { 269 if (cmd != DDI_DETACH) 270 return (DDI_FAILURE); 271 272 ASSERT(dld_dip == dip); 273 274 /* 275 * Remove the control node. 276 */ 277 ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME); 278 dld_dip = NULL; 279 280 return (DDI_SUCCESS); 281 } 282 283 /* 284 * dld control node open procedure. 285 */ 286 /*ARGSUSED*/ 287 static int 288 drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp) 289 { 290 dld_ctl_str_t *ctls; 291 minor_t minor; 292 queue_t *oq = OTHERQ(rq); 293 294 if (sflag == MODOPEN) 295 return (ENOTSUP); 296 297 /* 298 * This is a cloning driver and therefore each queue should only 299 * ever get opened once. 300 */ 301 if (rq->q_ptr != NULL) 302 return (EBUSY); 303 304 minor = (minor_t)(uintptr_t)vmem_alloc(dld_ctl_vmem, 1, VM_NOSLEEP); 305 if (minor == 0) 306 return (ENOMEM); 307 308 ctls = kmem_zalloc(sizeof (dld_ctl_str_t), KM_NOSLEEP); 309 if (ctls == NULL) { 310 vmem_free(dld_ctl_vmem, (void *)(uintptr_t)minor, 1); 311 return (ENOMEM); 312 } 313 314 ctls->cs_minor = minor; 315 ctls->cs_wq = WR(rq); 316 317 rq->q_ptr = ctls; 318 oq->q_ptr = ctls; 319 320 /* 321 * Enable the queue srv(9e) routine. 322 */ 323 qprocson(rq); 324 325 /* 326 * Construct a cloned dev_t to hand back. 327 */ 328 *devp = makedevice(getmajor(*devp), ctls->cs_minor); 329 return (0); 330 } 331 332 /* 333 * dld control node close procedure. 334 */ 335 static int 336 drv_close(queue_t *rq) 337 { 338 dld_ctl_str_t *ctls; 339 340 ctls = rq->q_ptr; 341 ASSERT(ctls != NULL); 342 343 /* 344 * Disable the queue srv(9e) routine. 345 */ 346 qprocsoff(rq); 347 348 vmem_free(dld_ctl_vmem, (void *)(uintptr_t)ctls->cs_minor, 1); 349 350 kmem_free(ctls, sizeof (dld_ctl_str_t)); 351 352 return (0); 353 } 354 355 /* 356 * DLDIOCATTR 357 */ 358 static void 359 drv_ioc_attr(dld_ctl_str_t *ctls, mblk_t *mp) 360 { 361 dld_ioc_attr_t *diap; 362 dls_vlan_t *dvp = NULL; 363 dls_link_t *dlp = NULL; 364 int err; 365 queue_t *q = ctls->cs_wq; 366 367 if ((err = miocpullup(mp, sizeof (dld_ioc_attr_t))) != 0) 368 goto failed; 369 370 diap = (dld_ioc_attr_t *)mp->b_cont->b_rptr; 371 diap->dia_name[IFNAMSIZ - 1] = '\0'; 372 373 if (dls_vlan_hold(diap->dia_name, &dvp, B_FALSE) != 0) { 374 err = ENOENT; 375 goto failed; 376 } 377 378 dlp = dvp->dv_dlp; 379 (void) strlcpy(diap->dia_dev, dlp->dl_name, sizeof (diap->dia_dev)); 380 diap->dia_vid = dvp->dv_id; 381 diap->dia_max_sdu = dlp->dl_mip->mi_sdu_max; 382 383 dls_vlan_rele(dvp); 384 miocack(q, mp, sizeof (dld_ioc_attr_t), 0); 385 return; 386 387 failed: 388 ASSERT(err != 0); 389 if (err == ENOENT) { 390 char devname[MAXNAMELEN]; 391 uint_t instance; 392 major_t major; 393 394 /* 395 * Try to detect if the specified device is gldv3 396 * and return ENODEV if it is not. 397 */ 398 if (ddi_parse(diap->dia_name, devname, &instance) == 0 && 399 (major = ddi_name_to_major(devname)) != (major_t)-1 && 400 !GLDV3_DRV(major)) 401 err = ENODEV; 402 } 403 miocnak(q, mp, 0, err); 404 } 405 406 407 /* 408 * DLDIOCVLAN 409 */ 410 typedef struct dld_ioc_vlan_state { 411 uint_t bytes_left; 412 dld_ioc_vlan_t *divp; 413 dld_vlan_info_t *vlanp; 414 } dld_ioc_vlan_state_t; 415 416 static int 417 drv_ioc_vlan_info(dls_vlan_t *dvp, void *arg) 418 { 419 dld_ioc_vlan_state_t *statep = arg; 420 421 /* 422 * passed buffer space is limited to 65536 bytes. So 423 * copy only the vlans associated with the passed link. 424 */ 425 if (strcmp(dvp->dv_dlp->dl_name, statep->divp->div_name) == 0 && 426 dvp->dv_id != 0) { 427 if (statep->bytes_left < sizeof (dld_vlan_info_t)) 428 return (ENOSPC); 429 430 (void) strlcpy(statep->vlanp->dvi_name, 431 dvp->dv_name, IFNAMSIZ); 432 statep->divp->div_count++; 433 statep->bytes_left -= sizeof (dld_vlan_info_t); 434 statep->vlanp += 1; 435 } 436 return (0); 437 } 438 439 static void 440 drv_ioc_vlan(dld_ctl_str_t *ctls, mblk_t *mp) 441 { 442 dld_ioc_vlan_t *divp; 443 dld_ioc_vlan_state_t state; 444 int err = EINVAL; 445 queue_t *q = ctls->cs_wq; 446 mblk_t *bp; 447 448 if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_t))) != 0) 449 goto failed; 450 451 if ((bp = msgpullup(mp->b_cont, -1)) == NULL) 452 goto failed; 453 454 freemsg(mp->b_cont); 455 mp->b_cont = bp; 456 divp = (dld_ioc_vlan_t *)bp->b_rptr; 457 divp->div_count = 0; 458 state.bytes_left = MBLKL(bp) - sizeof (dld_ioc_vlan_t); 459 state.divp = divp; 460 state.vlanp = (dld_vlan_info_t *)(divp + 1); 461 462 err = dls_vlan_walk(drv_ioc_vlan_info, &state); 463 if (err != 0) 464 goto failed; 465 466 miocack(q, mp, sizeof (dld_ioc_vlan_t) + 467 state.divp->div_count * sizeof (dld_vlan_info_t), 0); 468 return; 469 470 failed: 471 ASSERT(err != 0); 472 miocnak(q, mp, 0, err); 473 } 474 475 476 /* 477 * Process an IOCTL message received by the control node. 478 */ 479 static void 480 drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp) 481 { 482 uint_t cmd; 483 484 cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd; 485 switch (cmd) { 486 case DLDIOCATTR: 487 drv_ioc_attr(ctls, mp); 488 return; 489 case DLDIOCVLAN: 490 drv_ioc_vlan(ctls, mp); 491 return; 492 default: 493 miocnak(ctls->cs_wq, mp, 0, ENOTSUP); 494 return; 495 } 496 } 497 498 /* 499 * Write side put routine of the dld control node. 500 */ 501 static void 502 drv_uw_put(queue_t *q, mblk_t *mp) 503 { 504 dld_ctl_str_t *ctls = q->q_ptr; 505 506 switch (mp->b_datap->db_type) { 507 case M_IOCTL: 508 drv_ioc(ctls, mp); 509 break; 510 default: 511 freemsg(mp); 512 break; 513 } 514 } 515 516 /* 517 * Write-side service procedure. 518 */ 519 void 520 drv_uw_srv(queue_t *q) 521 { 522 mblk_t *mp; 523 524 while (mp = getq(q)) 525 drv_uw_put(q, mp); 526 } 527