xref: /illumos-gate/usr/src/uts/common/io/aggr/aggr_dev.c (revision 15d9d0b5)
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 2008 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 ioctl handling callback to process control ioctl
164 		 * messages; see comments above dld_ioctl() for details.
165 		 */
166 		dsp->ds_ioctl = aggr_ioctl;
167 
168 		/*
169 		 * The aggr control node uses its own set of entry points.
170 		 */
171 		WR(q)->q_qinfo = &aggr_w_ctl_qinit;
172 		*devp = makedevice(getmajor(*devp), dsp->ds_minor);
173 		qprocson(q);
174 		return (0);
175 	}
176 	return (dld_open(q, devp, flag, sflag, credp));
177 }
178 
179 static int
180 aggr_close(queue_t *q)
181 {
182 	dld_str_t	*dsp = q->q_ptr;
183 
184 	if (dsp->ds_type == DLD_CONTROL) {
185 		qprocsoff(q);
186 		dld_finish_pending_task(dsp);
187 		dsp->ds_ioctl = NULL;
188 		dld_str_destroy(dsp);
189 		return (0);
190 	}
191 	return (dld_close(q));
192 }
193 
194 static void
195 aggr_wput(queue_t *q, mblk_t *mp)
196 {
197 	if (DB_TYPE(mp) == M_IOCTL)
198 		dld_ioctl(q, mp);
199 	else
200 		freemsg(mp);
201 }
202 
203 /*ARGSUSED*/
204 static int
205 aggr_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
206     void **result)
207 {
208 	switch (infocmd) {
209 	case DDI_INFO_DEVT2DEVINFO:
210 		*result = aggr_dip;
211 		return (DDI_SUCCESS);
212 	case DDI_INFO_DEVT2INSTANCE:
213 		*result = NULL;
214 		return (DDI_SUCCESS);
215 	}
216 	return (DDI_FAILURE);
217 }
218 
219 static int
220 aggr_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
221 {
222 	switch (cmd) {
223 	case DDI_ATTACH:
224 		if (ddi_get_instance(dip) != 0) {
225 			/* we only allow instance 0 to attach */
226 			return (DDI_FAILURE);
227 		}
228 
229 		/* create minor node for control interface */
230 		if (ddi_create_minor_node(dip, AGGR_DEVNAME_CTL, S_IFCHR,
231 		    AGGR_MINOR_CTL, DDI_PSEUDO, 0) != DDI_SUCCESS) {
232 			return (DDI_FAILURE);
233 		}
234 
235 		aggr_dip = dip;
236 		aggr_port_init();
237 		aggr_grp_init();
238 		aggr_lacp_init();
239 		return (DDI_SUCCESS);
240 
241 	case DDI_RESUME:
242 		return (DDI_SUCCESS);
243 
244 	default:
245 		return (DDI_FAILURE);
246 	}
247 }
248 
249 /*ARGSUSED*/
250 static int
251 aggr_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
252 {
253 	switch (cmd) {
254 	case DDI_DETACH:
255 		if (aggr_grp_count() > 0)
256 			return (DDI_FAILURE);
257 
258 		aggr_dip = NULL;
259 		ddi_remove_minor_node(dip, AGGR_DEVNAME_CTL);
260 		aggr_port_fini();
261 		aggr_grp_fini();
262 		aggr_lacp_fini();
263 		return (DDI_SUCCESS);
264 
265 	case DDI_SUSPEND:
266 		return (DDI_SUCCESS);
267 
268 	default:
269 		return (DDI_FAILURE);
270 	}
271 }
272