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