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 = NULL; 58 59 static int aggr_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 60 static int aggr_attach(dev_info_t *, ddi_attach_cmd_t); 61 static int aggr_detach(dev_info_t *, ddi_detach_cmd_t); 62 static int aggr_open(queue_t *, dev_t *, int, int, cred_t *); 63 static int aggr_close(queue_t *); 64 static void aggr_wput(queue_t *, mblk_t *); 65 66 /* 67 * mi_hiwat is set to 1 because of the flow control mechanism implemented 68 * in dld. refer to the comments in dld_str.c for details. 69 */ 70 static struct module_info aggr_module_info = { 71 0, 72 AGGR_DRIVER_NAME, 73 0, 74 INFPSZ, 75 1, 76 0 77 }; 78 79 static struct qinit aggr_r_qinit = { /* read queues */ 80 NULL, 81 NULL, 82 aggr_open, 83 aggr_close, 84 NULL, 85 &aggr_module_info 86 }; 87 88 static struct qinit aggr_w_qinit = { /* write queues */ 89 (pfi_t)dld_wput, 90 (pfi_t)dld_wsrv, 91 NULL, 92 NULL, 93 NULL, 94 &aggr_module_info 95 }; 96 97 /* 98 * Entry points for aggr control node 99 */ 100 static struct qinit aggr_w_ctl_qinit = { 101 (pfi_t)aggr_wput, 102 NULL, 103 NULL, 104 NULL, 105 NULL, 106 &aggr_module_info 107 }; 108 109 static struct streamtab aggr_streamtab = { 110 &aggr_r_qinit, 111 &aggr_w_qinit 112 }; 113 114 DDI_DEFINE_STREAM_OPS(aggr_dev_ops, nulldev, nulldev, aggr_attach, aggr_detach, 115 nodev, aggr_getinfo, D_MP, &aggr_streamtab); 116 117 static struct modldrv aggr_modldrv = { 118 &mod_driverops, /* Type of module. This one is a driver */ 119 AGGR_LINKINFO, /* short description */ 120 &aggr_dev_ops /* driver specific ops */ 121 }; 122 123 static struct modlinkage modlinkage = { 124 MODREV_1, 125 &aggr_modldrv, 126 NULL 127 }; 128 129 130 int 131 _init(void) 132 { 133 return (mod_install(&modlinkage)); 134 } 135 136 int 137 _fini(void) 138 { 139 return (mod_remove(&modlinkage)); 140 } 141 142 int 143 _info(struct modinfo *modinfop) 144 { 145 return (mod_info(&modlinkage, modinfop)); 146 } 147 148 static int 149 aggr_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) 150 { 151 if (q->q_ptr != NULL) 152 return (EBUSY); 153 154 if (getminor(*devp) == AGGR_MINOR_CTL) { 155 dld_str_t *dsp; 156 157 dsp = dld_str_create(q, DLD_CONTROL, getmajor(*devp), 158 DL_STYLE1); 159 if (dsp == NULL) 160 return (ENOSR); 161 162 /* 163 * The aggr control node uses its own set of entry points. 164 */ 165 WR(q)->q_qinfo = &aggr_w_ctl_qinit; 166 *devp = makedevice(getmajor(*devp), dsp->ds_minor); 167 qprocson(q); 168 return (0); 169 } 170 return (dld_open(q, devp, flag, sflag, credp)); 171 } 172 173 static int 174 aggr_close(queue_t *q) 175 { 176 dld_str_t *dsp = q->q_ptr; 177 178 if (dsp->ds_type == DLD_CONTROL) { 179 qprocsoff(q); 180 dld_str_destroy(dsp); 181 return (0); 182 } 183 return (dld_close(q)); 184 } 185 186 static void 187 aggr_wput(queue_t *q, mblk_t *mp) 188 { 189 if (DB_TYPE(mp) == M_IOCTL) 190 aggr_ioctl(q, mp); 191 else 192 freemsg(mp); 193 } 194 195 /*ARGSUSED*/ 196 static int 197 aggr_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 198 void **result) 199 { 200 switch (infocmd) { 201 case DDI_INFO_DEVT2DEVINFO: 202 *result = aggr_dip; 203 return (DDI_SUCCESS); 204 case DDI_INFO_DEVT2INSTANCE: 205 *result = NULL; 206 return (DDI_SUCCESS); 207 } 208 return (DDI_FAILURE); 209 } 210 211 static int 212 aggr_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 213 { 214 switch (cmd) { 215 case DDI_ATTACH: 216 if (ddi_get_instance(dip) != 0) { 217 /* we only allow instance 0 to attach */ 218 return (DDI_FAILURE); 219 } 220 221 /* create minor node for control interface */ 222 if (ddi_create_minor_node(dip, AGGR_DEVNAME_CTL, S_IFCHR, 223 AGGR_MINOR_CTL, DDI_PSEUDO, 0) != DDI_SUCCESS) { 224 return (DDI_FAILURE); 225 } 226 227 aggr_dip = dip; 228 aggr_port_init(); 229 aggr_grp_init(); 230 aggr_lacp_init(); 231 return (DDI_SUCCESS); 232 233 case DDI_RESUME: 234 return (DDI_SUCCESS); 235 236 default: 237 return (DDI_FAILURE); 238 } 239 } 240 241 /*ARGSUSED*/ 242 static int 243 aggr_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 244 { 245 switch (cmd) { 246 case DDI_DETACH: 247 if (aggr_grp_count() > 0) 248 return (DDI_FAILURE); 249 250 aggr_dip = NULL; 251 ddi_remove_minor_node(dip, AGGR_DEVNAME_CTL); 252 aggr_port_fini(); 253 aggr_grp_fini(); 254 aggr_lacp_fini(); 255 return (DDI_SUCCESS); 256 257 case DDI_SUSPEND: 258 return (DDI_SUCCESS); 259 260 default: 261 return (DDI_FAILURE); 262 } 263 } 264