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 * IEEE 802.3ad Link Aggregation. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/sysmacros.h> 35 #include <sys/conf.h> 36 #include <sys/cmn_err.h> 37 #include <sys/list.h> 38 #include <sys/ksynch.h> 39 #include <sys/kmem.h> 40 #include <sys/stream.h> 41 #include <sys/strsun.h> 42 #include <sys/modctl.h> 43 #include <sys/ddi.h> 44 #include <sys/sunddi.h> 45 #include <sys/atomic.h> 46 #include <sys/stat.h> 47 48 #include <sys/dld_impl.h> 49 #include <sys/aggr.h> 50 #include <sys/aggr_impl.h> 51 #include <inet/common.h> 52 53 /* module description */ 54 #define AGGR_LINKINFO "Link Aggregation MAC" 55 #define AGGR_DRIVER_NAME "aggr" 56 57 /* device info ptr, only one for instance 0 */ 58 dev_info_t *aggr_dip; 59 60 static void aggr_dev_init(void); 61 static int aggr_dev_fini(void); 62 static int aggr_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 63 static int aggr_attach(dev_info_t *, ddi_attach_cmd_t); 64 static int aggr_detach(dev_info_t *, ddi_detach_cmd_t); 65 static int aggr_open(queue_t *, dev_t *, int, int, cred_t *); 66 static int aggr_close(queue_t *); 67 static void aggr_wput(queue_t *, mblk_t *); 68 69 /* 70 * mi_hiwat is set to 1 because of the flow control mechanism implemented 71 * in dld. refer to the comments in dld_str.c for details. 72 */ 73 static struct module_info aggr_module_info = { 74 0, 75 AGGR_DRIVER_NAME, 76 0, 77 INFPSZ, 78 1, 79 0 80 }; 81 82 static struct qinit aggr_r_qinit = { /* read queues */ 83 NULL, 84 NULL, 85 aggr_open, 86 aggr_close, 87 NULL, 88 &aggr_module_info 89 }; 90 91 static struct qinit aggr_w_qinit = { /* write queues */ 92 (pfi_t)dld_wput, 93 (pfi_t)dld_wsrv, 94 NULL, 95 NULL, 96 NULL, 97 &aggr_module_info 98 }; 99 100 /* 101 * Entry points for aggr control node 102 */ 103 static struct qinit aggr_w_ctl_qinit = { 104 (pfi_t)aggr_wput, 105 NULL, 106 NULL, 107 NULL, 108 NULL, 109 &aggr_module_info 110 }; 111 112 static struct streamtab aggr_streamtab = { 113 &aggr_r_qinit, 114 &aggr_w_qinit 115 }; 116 117 DDI_DEFINE_STREAM_OPS(aggr_dev_ops, nulldev, nulldev, aggr_attach, aggr_detach, 118 nodev, aggr_getinfo, D_MP, &aggr_streamtab); 119 120 static struct modldrv aggr_modldrv = { 121 &mod_driverops, /* Type of module. This one is a driver */ 122 AGGR_LINKINFO, /* short description */ 123 &aggr_dev_ops /* driver specific ops */ 124 }; 125 126 static struct modlinkage modlinkage = { 127 MODREV_1, 128 &aggr_modldrv, 129 NULL 130 }; 131 132 133 int 134 _init(void) 135 { 136 int err; 137 138 aggr_dev_init(); 139 140 if ((err = mod_install(&modlinkage)) != 0) { 141 (void) aggr_dev_fini(); 142 return (err); 143 } 144 145 aggr_dip = NULL; 146 return (0); 147 } 148 149 int 150 _fini(void) 151 { 152 int err; 153 154 if ((err = aggr_dev_fini()) != 0) 155 return (err); 156 157 if ((err = mod_remove(&modlinkage)) != 0) { 158 aggr_dev_init(); 159 return (err); 160 } 161 162 return (0); 163 } 164 165 int 166 _info(struct modinfo *modinfop) 167 { 168 return (mod_info(&modlinkage, modinfop)); 169 } 170 171 static int 172 aggr_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) 173 { 174 if (q->q_ptr != NULL) 175 return (EBUSY); 176 177 if (getminor(*devp) == AGGR_MINOR_CTL) { 178 dld_str_t *dsp; 179 180 dsp = dld_str_create(q, DLD_CONTROL, getmajor(*devp), 181 DL_STYLE1); 182 if (dsp == NULL) 183 return (ENOSR); 184 185 /* 186 * The aggr control node uses its own set of entry points. 187 */ 188 WR(q)->q_qinfo = &aggr_w_ctl_qinit; 189 *devp = makedevice(getmajor(*devp), dsp->ds_minor); 190 qprocson(q); 191 return (0); 192 } 193 return (dld_open(q, devp, flag, sflag, credp)); 194 } 195 196 static int 197 aggr_close(queue_t *q) 198 { 199 dld_str_t *dsp = q->q_ptr; 200 201 if (dsp->ds_type == DLD_CONTROL) { 202 qprocsoff(q); 203 dld_str_destroy(dsp); 204 return (0); 205 } 206 return (dld_close(q)); 207 } 208 209 static void 210 aggr_wput(queue_t *q, mblk_t *mp) 211 { 212 if (DB_TYPE(mp) == M_IOCTL) 213 aggr_ioctl(q, mp); 214 else 215 freemsg(mp); 216 } 217 218 static void 219 aggr_dev_init(void) 220 { 221 aggr_port_init(); 222 aggr_grp_init(); 223 } 224 225 static int 226 aggr_dev_fini(void) 227 { 228 int err; 229 230 if ((err = aggr_grp_fini()) != 0) 231 return (err); 232 if ((err = aggr_port_fini()) != 0) { 233 /* 234 * re-initialize the groups to keep a consistent 235 * state. 236 */ 237 aggr_grp_init(); 238 } 239 240 return (err); 241 } 242 243 /*ARGSUSED*/ 244 static int 245 aggr_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 246 void **result) 247 { 248 switch (infocmd) { 249 case DDI_INFO_DEVT2DEVINFO: 250 *result = aggr_dip; 251 return (DDI_SUCCESS); 252 case DDI_INFO_DEVT2INSTANCE: 253 *result = NULL; 254 return (DDI_SUCCESS); 255 } 256 return (DDI_FAILURE); 257 } 258 259 static int 260 aggr_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 261 { 262 switch (cmd) { 263 case DDI_ATTACH: 264 if (ddi_get_instance(dip) != 0) { 265 /* we only allow instance 0 to attach */ 266 return (DDI_FAILURE); 267 } 268 269 /* create minor node for control interface */ 270 if (ddi_create_minor_node(dip, AGGR_DEVNAME_CTL, S_IFCHR, 271 AGGR_MINOR_CTL, DDI_PSEUDO, 0) != DDI_SUCCESS) { 272 return (DDI_FAILURE); 273 } 274 275 aggr_dip = dip; 276 return (DDI_SUCCESS); 277 278 case DDI_RESUME: 279 return (DDI_SUCCESS); 280 281 default: 282 return (DDI_FAILURE); 283 } 284 } 285 286 /*ARGSUSED*/ 287 static int 288 aggr_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 289 { 290 switch (cmd) { 291 case DDI_DETACH: 292 if (aggr_grp_count() > 0) 293 return (DDI_FAILURE); 294 295 aggr_dip = NULL; 296 ddi_remove_minor_node(dip, AGGR_DEVNAME_CTL); 297 298 return (DDI_SUCCESS); 299 300 case DDI_SUSPEND: 301 return (DDI_SUCCESS); 302 303 default: 304 return (DDI_FAILURE); 305 } 306 } 307