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