Print this page
OS-1573 vnic_dev.c insufficiently explicit
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/io/vnic/vnic_dev.c
+++ new/usr/src/uts/common/io/vnic/vnic_dev.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 23 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
24 24 */
25 25
26 26 #include <sys/types.h>
27 27 #include <sys/cred.h>
28 28 #include <sys/sysmacros.h>
29 29 #include <sys/conf.h>
30 30 #include <sys/cmn_err.h>
31 31 #include <sys/list.h>
32 32 #include <sys/ksynch.h>
33 33 #include <sys/kmem.h>
34 34 #include <sys/stream.h>
35 35 #include <sys/modctl.h>
36 36 #include <sys/ddi.h>
37 37 #include <sys/sunddi.h>
38 38 #include <sys/atomic.h>
39 39 #include <sys/stat.h>
40 40 #include <sys/modhash.h>
41 41 #include <sys/strsubr.h>
42 42 #include <sys/strsun.h>
43 43 #include <sys/dlpi.h>
44 44 #include <sys/mac.h>
↓ open down ↓ |
44 lines elided |
↑ open up ↑ |
45 45 #include <sys/mac_provider.h>
46 46 #include <sys/mac_client.h>
47 47 #include <sys/mac_client_priv.h>
48 48 #include <sys/mac_ether.h>
49 49 #include <sys/dls.h>
50 50 #include <sys/pattr.h>
51 51 #include <sys/time.h>
52 52 #include <sys/vlan.h>
53 53 #include <sys/vnic.h>
54 54 #include <sys/vnic_impl.h>
55 +#include <sys/mac_impl.h>
55 56 #include <sys/mac_flow_impl.h>
56 57 #include <inet/ip_impl.h>
57 58
58 59 /*
59 60 * Note that for best performance, the VNIC is a passthrough design.
60 61 * For each VNIC corresponds a MAC client of the underlying MAC (lower MAC).
61 62 * This MAC client is opened by the VNIC driver at VNIC creation,
62 63 * and closed when the VNIC is deleted.
63 64 * When a MAC client of the VNIC itself opens a VNIC, the MAC layer
64 65 * (upper MAC) detects that the MAC being opened is a VNIC. Instead
65 66 * of allocating a new MAC client, it asks the VNIC driver to return
66 67 * the lower MAC client handle associated with the VNIC, and that handle
67 68 * is returned to the upper MAC client directly. This allows access
68 69 * by upper MAC clients of the VNIC to have direct access to the lower
69 70 * MAC client for the control path and data path.
70 71 *
71 72 * Due to this passthrough, some of the entry points exported by the
72 73 * VNIC driver are never directly invoked. These entry points include
73 74 * vnic_m_start, vnic_m_stop, vnic_m_promisc, vnic_m_multicst, etc.
74 75 */
75 76
76 77 static int vnic_m_start(void *);
77 78 static void vnic_m_stop(void *);
78 79 static int vnic_m_promisc(void *, boolean_t);
79 80 static int vnic_m_multicst(void *, boolean_t, const uint8_t *);
80 81 static int vnic_m_unicst(void *, const uint8_t *);
81 82 static int vnic_m_stat(void *, uint_t, uint64_t *);
82 83 static void vnic_m_ioctl(void *, queue_t *, mblk_t *);
83 84 static int vnic_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
84 85 const void *);
85 86 static int vnic_m_getprop(void *, const char *, mac_prop_id_t, uint_t, void *);
86 87 static void vnic_m_propinfo(void *, const char *, mac_prop_id_t,
87 88 mac_prop_info_handle_t);
88 89 static mblk_t *vnic_m_tx(void *, mblk_t *);
89 90 static boolean_t vnic_m_capab_get(void *, mac_capab_t, void *);
90 91 static void vnic_notify_cb(void *, mac_notify_type_t);
91 92
92 93 static kmem_cache_t *vnic_cache;
93 94 static krwlock_t vnic_lock;
94 95 static uint_t vnic_count;
95 96
96 97 #define ANCHOR_VNIC_MIN_MTU 576
97 98 #define ANCHOR_VNIC_MAX_MTU 9000
98 99
99 100 /* hash of VNICs (vnic_t's), keyed by VNIC id */
100 101 static mod_hash_t *vnic_hash;
101 102 #define VNIC_HASHSZ 64
102 103 #define VNIC_HASH_KEY(vnic_id) ((mod_hash_key_t)(uintptr_t)vnic_id)
103 104
104 105 #define VNIC_M_CALLBACK_FLAGS \
105 106 (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO)
106 107
107 108 static mac_callbacks_t vnic_m_callbacks = {
108 109 VNIC_M_CALLBACK_FLAGS,
109 110 vnic_m_stat,
110 111 vnic_m_start,
111 112 vnic_m_stop,
112 113 vnic_m_promisc,
113 114 vnic_m_multicst,
114 115 vnic_m_unicst,
115 116 vnic_m_tx,
116 117 NULL,
117 118 vnic_m_ioctl,
118 119 vnic_m_capab_get,
119 120 NULL,
120 121 NULL,
121 122 vnic_m_setprop,
122 123 vnic_m_getprop,
123 124 vnic_m_propinfo
124 125 };
125 126
126 127 void
127 128 vnic_dev_init(void)
128 129 {
129 130 vnic_cache = kmem_cache_create("vnic_cache",
130 131 sizeof (vnic_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
131 132
132 133 vnic_hash = mod_hash_create_idhash("vnic_hash",
133 134 VNIC_HASHSZ, mod_hash_null_valdtor);
134 135
135 136 rw_init(&vnic_lock, NULL, RW_DEFAULT, NULL);
136 137
137 138 vnic_count = 0;
138 139 }
139 140
140 141 void
141 142 vnic_dev_fini(void)
142 143 {
143 144 ASSERT(vnic_count == 0);
144 145
145 146 rw_destroy(&vnic_lock);
146 147 mod_hash_destroy_idhash(vnic_hash);
147 148 kmem_cache_destroy(vnic_cache);
148 149 }
149 150
150 151 uint_t
151 152 vnic_dev_count(void)
152 153 {
153 154 return (vnic_count);
154 155 }
155 156
156 157 static vnic_ioc_diag_t
157 158 vnic_mac2vnic_diag(mac_diag_t diag)
158 159 {
159 160 switch (diag) {
160 161 case MAC_DIAG_MACADDR_NIC:
161 162 return (VNIC_IOC_DIAG_MACADDR_NIC);
162 163 case MAC_DIAG_MACADDR_INUSE:
163 164 return (VNIC_IOC_DIAG_MACADDR_INUSE);
164 165 case MAC_DIAG_MACADDR_INVALID:
165 166 return (VNIC_IOC_DIAG_MACADDR_INVALID);
166 167 case MAC_DIAG_MACADDRLEN_INVALID:
167 168 return (VNIC_IOC_DIAG_MACADDRLEN_INVALID);
168 169 case MAC_DIAG_MACFACTORYSLOTINVALID:
169 170 return (VNIC_IOC_DIAG_MACFACTORYSLOTINVALID);
170 171 case MAC_DIAG_MACFACTORYSLOTUSED:
171 172 return (VNIC_IOC_DIAG_MACFACTORYSLOTUSED);
172 173 case MAC_DIAG_MACFACTORYSLOTALLUSED:
173 174 return (VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED);
174 175 case MAC_DIAG_MACFACTORYNOTSUP:
175 176 return (VNIC_IOC_DIAG_MACFACTORYNOTSUP);
176 177 case MAC_DIAG_MACPREFIX_INVALID:
177 178 return (VNIC_IOC_DIAG_MACPREFIX_INVALID);
178 179 case MAC_DIAG_MACPREFIXLEN_INVALID:
179 180 return (VNIC_IOC_DIAG_MACPREFIXLEN_INVALID);
180 181 case MAC_DIAG_MACNO_HWRINGS:
181 182 return (VNIC_IOC_DIAG_NO_HWRINGS);
182 183 default:
183 184 return (VNIC_IOC_DIAG_NONE);
184 185 }
185 186 }
186 187
187 188 static int
188 189 vnic_unicast_add(vnic_t *vnic, vnic_mac_addr_type_t vnic_addr_type,
189 190 int *addr_slot, uint_t prefix_len, int *addr_len_ptr_arg,
190 191 uint8_t *mac_addr_arg, uint16_t flags, vnic_ioc_diag_t *diag,
191 192 uint16_t vid, boolean_t req_hwgrp_flag)
192 193 {
193 194 mac_diag_t mac_diag;
194 195 uint16_t mac_flags = 0;
195 196 int err;
196 197 uint_t addr_len;
197 198
198 199 if (flags & VNIC_IOC_CREATE_NODUPCHECK)
199 200 mac_flags |= MAC_UNICAST_NODUPCHECK;
200 201
201 202 switch (vnic_addr_type) {
202 203 case VNIC_MAC_ADDR_TYPE_FIXED:
203 204 case VNIC_MAC_ADDR_TYPE_VRID:
204 205 /*
205 206 * The MAC address value to assign to the VNIC
206 207 * is already provided in mac_addr_arg. addr_len_ptr_arg
207 208 * already contains the MAC address length.
208 209 */
209 210 break;
210 211
211 212 case VNIC_MAC_ADDR_TYPE_RANDOM:
212 213 /*
213 214 * Random MAC address. There are two sub-cases:
214 215 *
215 216 * 1 - If mac_len == 0, a new MAC address is generated.
216 217 * The length of the MAC address to generated depends
217 218 * on the type of MAC used. The prefix to use for the MAC
218 219 * address is stored in the most significant bytes
219 220 * of the mac_addr argument, and its length is specified
220 221 * by the mac_prefix_len argument. This prefix can
221 222 * correspond to a IEEE OUI in the case of Ethernet,
222 223 * for example.
223 224 *
224 225 * 2 - If mac_len > 0, the address was already picked
225 226 * randomly, and is now passed back during VNIC
226 227 * re-creation. The mac_addr argument contains the MAC
227 228 * address that was generated. We distinguish this
228 229 * case from the fixed MAC address case, since we
229 230 * want the user consumers to know, when they query
230 231 * the list of VNICs, that a VNIC was assigned a
231 232 * random MAC address vs assigned a fixed address
232 233 * specified by the user.
233 234 */
234 235
235 236 /*
236 237 * If it's a pre-generated address, we're done. mac_addr_arg
237 238 * and addr_len_ptr_arg already contain the MAC address
238 239 * value and length.
239 240 */
240 241 if (*addr_len_ptr_arg > 0)
241 242 break;
242 243
243 244 /* generate a new random MAC address */
244 245 if ((err = mac_addr_random(vnic->vn_mch,
245 246 prefix_len, mac_addr_arg, &mac_diag)) != 0) {
246 247 *diag = vnic_mac2vnic_diag(mac_diag);
247 248 return (err);
248 249 }
249 250 *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
250 251 break;
251 252
252 253 case VNIC_MAC_ADDR_TYPE_FACTORY:
253 254 err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
254 255 if (err != 0) {
255 256 if (err == EINVAL)
256 257 *diag = VNIC_IOC_DIAG_MACFACTORYSLOTINVALID;
257 258 if (err == EBUSY)
258 259 *diag = VNIC_IOC_DIAG_MACFACTORYSLOTUSED;
259 260 if (err == ENOSPC)
260 261 *diag = VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED;
261 262 return (err);
262 263 }
263 264
264 265 mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
265 266 mac_addr_arg, &addr_len, NULL, NULL);
266 267 *addr_len_ptr_arg = addr_len;
267 268 break;
268 269
269 270 case VNIC_MAC_ADDR_TYPE_AUTO:
270 271 /* first try to allocate a factory MAC address */
271 272 err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
272 273 if (err == 0) {
273 274 mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
274 275 mac_addr_arg, &addr_len, NULL, NULL);
275 276 vnic_addr_type = VNIC_MAC_ADDR_TYPE_FACTORY;
276 277 *addr_len_ptr_arg = addr_len;
277 278 break;
278 279 }
279 280
280 281 /*
281 282 * Allocating a factory MAC address failed, generate a
282 283 * random MAC address instead.
283 284 */
284 285 if ((err = mac_addr_random(vnic->vn_mch,
285 286 prefix_len, mac_addr_arg, &mac_diag)) != 0) {
286 287 *diag = vnic_mac2vnic_diag(mac_diag);
287 288 return (err);
288 289 }
289 290 *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
290 291 vnic_addr_type = VNIC_MAC_ADDR_TYPE_RANDOM;
291 292 break;
292 293 case VNIC_MAC_ADDR_TYPE_PRIMARY:
293 294 /*
294 295 * We get the address here since we copy it in the
295 296 * vnic's vn_addr.
296 297 * We can't ask for hardware resources since we
297 298 * don't currently support hardware classification
298 299 * for these MAC clients.
299 300 */
300 301 if (req_hwgrp_flag) {
301 302 *diag = VNIC_IOC_DIAG_NO_HWRINGS;
302 303 return (ENOTSUP);
303 304 }
304 305 mac_unicast_primary_get(vnic->vn_lower_mh, mac_addr_arg);
305 306 *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
306 307 mac_flags |= MAC_UNICAST_VNIC_PRIMARY;
307 308 break;
308 309 }
309 310
310 311 vnic->vn_addr_type = vnic_addr_type;
311 312
312 313 err = mac_unicast_add(vnic->vn_mch, mac_addr_arg, mac_flags,
313 314 &vnic->vn_muh, vid, &mac_diag);
314 315 if (err != 0) {
315 316 if (vnic_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
316 317 /* release factory MAC address */
317 318 mac_addr_factory_release(vnic->vn_mch, *addr_slot);
318 319 }
319 320 *diag = vnic_mac2vnic_diag(mac_diag);
320 321 }
321 322
322 323 return (err);
323 324 }
324 325
325 326 /*
326 327 * Create a new VNIC upon request from administrator.
327 328 * Returns 0 on success, an errno on failure.
328 329 */
329 330 /* ARGSUSED */
330 331 int
331 332 vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid,
332 333 vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr,
333 334 int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid,
334 335 int af, mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag,
335 336 cred_t *credp)
336 337 {
337 338 vnic_t *vnic;
338 339 mac_register_t *mac;
339 340 int err;
340 341 boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0);
341 342 char vnic_name[MAXNAMELEN];
342 343 const mac_info_t *minfop;
343 344 uint32_t req_hwgrp_flag = B_FALSE;
344 345
345 346 *diag = VNIC_IOC_DIAG_NONE;
346 347
347 348 rw_enter(&vnic_lock, RW_WRITER);
348 349
349 350 /* does a VNIC with the same id already exist? */
350 351 err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
351 352 (mod_hash_val_t *)&vnic);
352 353 if (err == 0) {
353 354 rw_exit(&vnic_lock);
354 355 return (EEXIST);
355 356 }
356 357
357 358 vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP);
358 359 if (vnic == NULL) {
359 360 rw_exit(&vnic_lock);
360 361 return (ENOMEM);
361 362 }
362 363
363 364 bzero(vnic, sizeof (*vnic));
364 365
365 366 vnic->vn_id = vnic_id;
366 367 vnic->vn_link_id = linkid;
367 368 vnic->vn_vrid = vrid;
368 369 vnic->vn_af = af;
369 370
370 371 if (!is_anchor) {
371 372 if (linkid == DATALINK_INVALID_LINKID) {
372 373 err = EINVAL;
373 374 goto bail;
374 375 }
375 376
376 377 /*
377 378 * Open the lower MAC and assign its initial bandwidth and
378 379 * MAC address. We do this here during VNIC creation and
379 380 * do not wait until the upper MAC client open so that we
380 381 * can validate the VNIC creation parameters (bandwidth,
381 382 * MAC address, etc) and reserve a factory MAC address if
382 383 * one was requested.
383 384 */
384 385 err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh);
385 386 if (err != 0)
386 387 goto bail;
387 388
388 389 /*
389 390 * VNIC(vlan) over VNICs(vlans) is not supported.
390 391 */
391 392 if (mac_is_vnic(vnic->vn_lower_mh)) {
392 393 err = EINVAL;
393 394 goto bail;
394 395 }
395 396
396 397 /* only ethernet support for now */
397 398 minfop = mac_info(vnic->vn_lower_mh);
398 399 if (minfop->mi_nativemedia != DL_ETHER) {
399 400 err = ENOTSUP;
400 401 goto bail;
401 402 }
402 403
403 404 (void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL,
404 405 NULL);
405 406 err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch,
406 407 vnic_name, MAC_OPEN_FLAGS_IS_VNIC);
407 408 if (err != 0)
408 409 goto bail;
409 410
410 411 if (mrp != NULL) {
411 412 if ((mrp->mrp_mask & MRP_RX_RINGS) != 0 ||
412 413 (mrp->mrp_mask & MRP_TX_RINGS) != 0) {
413 414 req_hwgrp_flag = B_TRUE;
414 415 }
415 416 err = mac_client_set_resources(vnic->vn_mch, mrp);
416 417 if (err != 0)
417 418 goto bail;
418 419 }
419 420 /* assign a MAC address to the VNIC */
420 421
421 422 err = vnic_unicast_add(vnic, *vnic_addr_type, mac_slot,
422 423 mac_prefix_len, mac_len, mac_addr, flags, diag, vid,
423 424 req_hwgrp_flag);
424 425 if (err != 0) {
425 426 vnic->vn_muh = NULL;
426 427 if (diag != NULL && req_hwgrp_flag)
427 428 *diag = VNIC_IOC_DIAG_NO_HWRINGS;
428 429 goto bail;
429 430 }
430 431
431 432 /* register to receive notification from underlying MAC */
432 433 vnic->vn_mnh = mac_notify_add(vnic->vn_lower_mh, vnic_notify_cb,
433 434 vnic);
434 435
435 436 *vnic_addr_type = vnic->vn_addr_type;
436 437 vnic->vn_addr_len = *mac_len;
437 438 vnic->vn_vid = vid;
438 439
439 440 bcopy(mac_addr, vnic->vn_addr, vnic->vn_addr_len);
440 441
441 442 if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY)
442 443 vnic->vn_slot_id = *mac_slot;
443 444
444 445 /*
445 446 * Set the initial VNIC capabilities. If the VNIC is created
446 447 * over MACs which does not support nactive vlan, disable
447 448 * VNIC's hardware checksum capability if its VID is not 0,
448 449 * since the underlying MAC would get the hardware checksum
449 450 * offset wrong in case of VLAN packets.
450 451 */
451 452 if (vid == 0 || !mac_capab_get(vnic->vn_lower_mh,
452 453 MAC_CAPAB_NO_NATIVEVLAN, NULL)) {
453 454 if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_HCKSUM,
454 455 &vnic->vn_hcksum_txflags))
455 456 vnic->vn_hcksum_txflags = 0;
456 457 } else {
457 458 vnic->vn_hcksum_txflags = 0;
458 459 }
459 460 }
460 461
461 462 /* register with the MAC module */
462 463 if ((mac = mac_alloc(MAC_VERSION)) == NULL)
463 464 goto bail;
464 465
465 466 mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
466 467 mac->m_driver = vnic;
467 468 mac->m_dip = vnic_get_dip();
468 469 mac->m_instance = (uint_t)-1;
469 470 mac->m_src_addr = vnic->vn_addr;
470 471 mac->m_callbacks = &vnic_m_callbacks;
471 472
472 473 if (!is_anchor) {
473 474 /*
474 475 * If this is a VNIC based VLAN, then we check for the
475 476 * margin unless it has been created with the force
476 477 * flag. If we are configuring a VLAN over an etherstub,
477 478 * we don't check the margin even if force is not set.
478 479 */
479 480 if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) {
480 481 if (vid != VLAN_ID_NONE)
481 482 vnic->vn_force = B_TRUE;
482 483 /*
483 484 * As the current margin size of the underlying mac is
484 485 * used to determine the margin size of the VNIC
485 486 * itself, request the underlying mac not to change
486 487 * to a smaller margin size.
487 488 */
488 489 err = mac_margin_add(vnic->vn_lower_mh,
489 490 &vnic->vn_margin, B_TRUE);
490 491 ASSERT(err == 0);
491 492 } else {
492 493 vnic->vn_margin = VLAN_TAGSZ;
493 494 err = mac_margin_add(vnic->vn_lower_mh,
494 495 &vnic->vn_margin, B_FALSE);
495 496 if (err != 0) {
496 497 mac_free(mac);
497 498 if (diag != NULL)
498 499 *diag = VNIC_IOC_DIAG_MACMARGIN_INVALID;
499 500 goto bail;
500 501 }
501 502 }
502 503
503 504 mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu,
504 505 &mac->m_max_sdu);
505 506 } else {
506 507 vnic->vn_margin = VLAN_TAGSZ;
507 508 mac->m_min_sdu = ANCHOR_VNIC_MIN_MTU;
508 509 mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU;
509 510 }
510 511
511 512 mac->m_margin = vnic->vn_margin;
512 513
513 514 err = mac_register(mac, &vnic->vn_mh);
514 515 mac_free(mac);
515 516 if (err != 0) {
516 517 VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
517 518 vnic->vn_margin) == 0);
518 519 goto bail;
519 520 }
520 521
521 522 /* Set the VNIC's MAC in the client */
522 523 if (!is_anchor)
523 524 mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh, mrp);
524 525
525 526 err = dls_devnet_create(vnic->vn_mh, vnic->vn_id, crgetzoneid(credp));
526 527 if (err != 0) {
527 528 VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
528 529 vnic->vn_margin) == 0);
529 530 (void) mac_unregister(vnic->vn_mh);
530 531 goto bail;
531 532 }
532 533
533 534 /* add new VNIC to hash table */
534 535 err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id),
535 536 (mod_hash_val_t)vnic);
536 537 ASSERT(err == 0);
537 538 vnic_count++;
538 539
539 540 vnic->vn_enabled = B_TRUE;
540 541 rw_exit(&vnic_lock);
541 542
542 543 return (0);
543 544
544 545 bail:
545 546 rw_exit(&vnic_lock);
546 547 if (!is_anchor) {
547 548 if (vnic->vn_mnh != NULL)
548 549 (void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
549 550 if (vnic->vn_muh != NULL)
550 551 (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
551 552 if (vnic->vn_mch != NULL)
552 553 mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
553 554 if (vnic->vn_lower_mh != NULL)
554 555 mac_close(vnic->vn_lower_mh);
555 556 }
556 557
557 558 kmem_cache_free(vnic_cache, vnic);
558 559 return (err);
559 560 }
560 561
561 562 /*
562 563 * Modify the properties of an existing VNIC.
563 564 */
564 565 /* ARGSUSED */
565 566 int
566 567 vnic_dev_modify(datalink_id_t vnic_id, uint_t modify_mask,
567 568 vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr,
568 569 uint_t mac_slot, mac_resource_props_t *mrp)
569 570 {
570 571 vnic_t *vnic = NULL;
571 572
572 573 rw_enter(&vnic_lock, RW_WRITER);
573 574
574 575 if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
575 576 (mod_hash_val_t *)&vnic) != 0) {
576 577 rw_exit(&vnic_lock);
577 578 return (ENOENT);
578 579 }
579 580
580 581 rw_exit(&vnic_lock);
581 582
582 583 return (0);
583 584 }
584 585
585 586 /* ARGSUSED */
586 587 int
587 588 vnic_dev_delete(datalink_id_t vnic_id, uint32_t flags, cred_t *credp)
588 589 {
589 590 vnic_t *vnic = NULL;
590 591 mod_hash_val_t val;
591 592 datalink_id_t tmpid;
592 593 int rc;
593 594
594 595 rw_enter(&vnic_lock, RW_WRITER);
595 596
596 597 if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
597 598 (mod_hash_val_t *)&vnic) != 0) {
598 599 rw_exit(&vnic_lock);
599 600 return (ENOENT);
600 601 }
601 602
602 603 if ((rc = dls_devnet_destroy(vnic->vn_mh, &tmpid, B_TRUE)) != 0) {
603 604 rw_exit(&vnic_lock);
604 605 return (rc);
605 606 }
606 607
607 608 ASSERT(vnic_id == tmpid);
608 609
609 610 /*
610 611 * We cannot unregister the MAC yet. Unregistering would
611 612 * free up mac_impl_t which should not happen at this time.
612 613 * So disable mac_impl_t by calling mac_disable(). This will prevent
613 614 * any new claims on mac_impl_t.
614 615 */
615 616 if ((rc = mac_disable(vnic->vn_mh)) != 0) {
616 617 (void) dls_devnet_create(vnic->vn_mh, vnic_id,
617 618 crgetzoneid(credp));
618 619 rw_exit(&vnic_lock);
619 620 return (rc);
620 621 }
621 622
622 623 vnic->vn_enabled = B_FALSE;
623 624 (void) mod_hash_remove(vnic_hash, VNIC_HASH_KEY(vnic_id), &val);
624 625 ASSERT(vnic == (vnic_t *)val);
625 626 vnic_count--;
626 627 rw_exit(&vnic_lock);
627 628
628 629 /*
629 630 * XXX-nicolas shouldn't have a void cast here, if it's
630 631 * expected that the function will never fail, then we should
631 632 * have an ASSERT().
632 633 */
633 634 (void) mac_unregister(vnic->vn_mh);
634 635
635 636 if (vnic->vn_lower_mh != NULL) {
636 637 /*
637 638 * Check if MAC address for the vnic was obtained from the
638 639 * factory MAC addresses. If yes, release it.
639 640 */
640 641 if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
641 642 (void) mac_addr_factory_release(vnic->vn_mch,
642 643 vnic->vn_slot_id);
643 644 }
644 645 (void) mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin);
645 646 (void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
646 647 (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
647 648 mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
648 649 mac_close(vnic->vn_lower_mh);
649 650 }
650 651
651 652 kmem_cache_free(vnic_cache, vnic);
652 653 return (0);
653 654 }
654 655
655 656 /* ARGSUSED */
656 657 mblk_t *
657 658 vnic_m_tx(void *arg, mblk_t *mp_chain)
658 659 {
659 660 /*
660 661 * This function could be invoked for an anchor VNIC when sending
661 662 * broadcast and multicast packets, and unicast packets which did
662 663 * not match any local known destination.
663 664 */
664 665 freemsgchain(mp_chain);
665 666 return (NULL);
666 667 }
667 668
668 669 /*ARGSUSED*/
669 670 static void
670 671 vnic_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
671 672 {
672 673 miocnak(q, mp, 0, ENOTSUP);
673 674 }
674 675
675 676 /*
676 677 * This entry point cannot be passed-through, since it is invoked
677 678 * for the per-VNIC kstats which must be exported independently
678 679 * of the existence of VNIC MAC clients.
679 680 */
680 681 static int
681 682 vnic_m_stat(void *arg, uint_t stat, uint64_t *val)
682 683 {
683 684 vnic_t *vnic = arg;
684 685 int rval = 0;
685 686
686 687 if (vnic->vn_lower_mh == NULL) {
687 688 /*
688 689 * It's an anchor VNIC, which does not have any
689 690 * statistics in itself.
690 691 */
691 692 return (ENOTSUP);
692 693 }
693 694
694 695 /*
695 696 * ENOTSUP must be reported for unsupported stats, the VNIC
696 697 * driver reports a subset of the stats that would
697 698 * be returned by a real piece of hardware.
698 699 */
699 700
700 701 switch (stat) {
701 702 case MAC_STAT_LINK_STATE:
702 703 case MAC_STAT_LINK_UP:
703 704 case MAC_STAT_PROMISC:
704 705 case MAC_STAT_IFSPEED:
705 706 case MAC_STAT_MULTIRCV:
706 707 case MAC_STAT_MULTIXMT:
707 708 case MAC_STAT_BRDCSTRCV:
708 709 case MAC_STAT_BRDCSTXMT:
709 710 case MAC_STAT_OPACKETS:
710 711 case MAC_STAT_OBYTES:
711 712 case MAC_STAT_IERRORS:
712 713 case MAC_STAT_OERRORS:
713 714 case MAC_STAT_RBYTES:
714 715 case MAC_STAT_IPACKETS:
715 716 *val = mac_client_stat_get(vnic->vn_mch, stat);
716 717 break;
717 718 default:
718 719 rval = ENOTSUP;
719 720 }
720 721
721 722 return (rval);
722 723 }
723 724
724 725 /*
725 726 * Invoked by the upper MAC to retrieve the lower MAC client handle
726 727 * corresponding to a VNIC. A pointer to this function is obtained
727 728 * by the upper MAC via capability query.
728 729 *
729 730 * XXX-nicolas Note: this currently causes all VNIC MAC clients to
730 731 * receive the same MAC client handle for the same VNIC. This is ok
731 732 * as long as we have only one VNIC MAC client which sends and
732 733 * receives data, but we don't currently enforce this at the MAC layer.
733 734 */
734 735 static void *
735 736 vnic_mac_client_handle(void *vnic_arg)
736 737 {
737 738 vnic_t *vnic = vnic_arg;
738 739
739 740 return (vnic->vn_mch);
740 741 }
741 742
742 743
743 744 /*
744 745 * Return information about the specified capability.
745 746 */
746 747 /* ARGSUSED */
747 748 static boolean_t
748 749 vnic_m_capab_get(void *arg, mac_capab_t cap, void *cap_data)
749 750 {
750 751 vnic_t *vnic = arg;
751 752
752 753 switch (cap) {
753 754 case MAC_CAPAB_HCKSUM: {
754 755 uint32_t *hcksum_txflags = cap_data;
755 756
756 757 *hcksum_txflags = vnic->vn_hcksum_txflags &
757 758 (HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM |
758 759 HCKSUM_INET_PARTIAL);
759 760 break;
760 761 }
761 762 case MAC_CAPAB_VNIC: {
762 763 mac_capab_vnic_t *vnic_capab = cap_data;
763 764
764 765 if (vnic->vn_lower_mh == NULL) {
765 766 /*
766 767 * It's an anchor VNIC, we don't have an underlying
767 768 * NIC and MAC client handle.
768 769 */
769 770 return (B_FALSE);
770 771 }
771 772
772 773 if (vnic_capab != NULL) {
773 774 vnic_capab->mcv_arg = vnic;
774 775 vnic_capab->mcv_mac_client_handle =
775 776 vnic_mac_client_handle;
776 777 }
777 778 break;
778 779 }
779 780 case MAC_CAPAB_ANCHOR_VNIC: {
780 781 /* since it's an anchor VNIC we don't have lower mac handle */
781 782 if (vnic->vn_lower_mh == NULL) {
782 783 ASSERT(vnic->vn_link_id == 0);
783 784 return (B_TRUE);
784 785 }
785 786 return (B_FALSE);
786 787 }
787 788 case MAC_CAPAB_NO_NATIVEVLAN:
788 789 return (B_FALSE);
789 790 case MAC_CAPAB_NO_ZCOPY:
790 791 return (B_TRUE);
791 792 case MAC_CAPAB_VRRP: {
792 793 mac_capab_vrrp_t *vrrp_capab = cap_data;
793 794
794 795 if (vnic->vn_vrid != 0) {
795 796 if (vrrp_capab != NULL)
796 797 vrrp_capab->mcv_af = vnic->vn_af;
797 798 return (B_TRUE);
798 799 }
799 800 return (B_FALSE);
800 801 }
801 802 default:
802 803 return (B_FALSE);
803 804 }
804 805 return (B_TRUE);
805 806 }
806 807
807 808 /* ARGSUSED */
808 809 static int
809 810 vnic_m_start(void *arg)
810 811 {
811 812 return (0);
812 813 }
813 814
814 815 /* ARGSUSED */
815 816 static void
816 817 vnic_m_stop(void *arg)
817 818 {
818 819 }
819 820
820 821 /* ARGSUSED */
821 822 static int
822 823 vnic_m_promisc(void *arg, boolean_t on)
823 824 {
824 825 return (0);
825 826 }
826 827
827 828 /* ARGSUSED */
828 829 static int
829 830 vnic_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
830 831 {
831 832 return (0);
832 833 }
833 834
834 835 static int
835 836 vnic_m_unicst(void *arg, const uint8_t *macaddr)
836 837 {
837 838 vnic_t *vnic = arg;
838 839
839 840 return (mac_vnic_unicast_set(vnic->vn_mch, macaddr));
↓ open down ↓ |
775 lines elided |
↑ open up ↑ |
840 841 }
841 842
842 843 /*
843 844 * Callback functions for set/get of properties
844 845 */
845 846 /*ARGSUSED*/
846 847 static int
847 848 vnic_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
848 849 uint_t pr_valsize, const void *pr_val)
849 850 {
850 - int err = ENOTSUP;
851 + int err = 0;
851 852 vnic_t *vn = m_driver;
852 853
853 854 switch (pr_num) {
854 855 case MAC_PROP_MTU: {
855 856 uint32_t mtu;
856 857
857 858 /* allow setting MTU only on an etherstub */
858 - if (vn->vn_link_id != DATALINK_INVALID_LINKID)
859 - return (err);
859 + if (vn->vn_link_id != DATALINK_INVALID_LINKID) {
860 + err = ENOTSUP;
861 + break;
862 + }
860 863
861 864 if (pr_valsize < sizeof (mtu)) {
862 865 err = EINVAL;
863 866 break;
864 867 }
865 868 bcopy(pr_val, &mtu, sizeof (mtu));
866 869 if (mtu < ANCHOR_VNIC_MIN_MTU || mtu > ANCHOR_VNIC_MAX_MTU) {
867 870 err = EINVAL;
868 871 break;
869 872 }
870 873 err = mac_maxsdu_update(vn->vn_mh, mtu);
871 874 break;
↓ open down ↓ |
2 lines elided |
↑ open up ↑ |
872 875 }
873 876 case MAC_PROP_VN_PROMISC_FILTERED: {
874 877 boolean_t filtered;
875 878
876 879 if (pr_valsize < sizeof (filtered)) {
877 880 err = EINVAL;
878 881 break;
879 882 }
880 883
881 884 bcopy(pr_val, &filtered, sizeof (filtered));
882 - err = mac_set_promisc_filtered(vn->vn_mch, filtered);
885 + mac_set_promisc_filtered(vn->vn_mch, filtered);
883 886 }
884 887 default:
888 + err = ENOTSUP;
885 889 break;
886 890 }
887 891 return (err);
888 892 }
889 893
890 894 static int
891 895 vnic_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
892 896 uint_t pr_valsize, void *pr_val)
893 897 {
894 898 vnic_t *vn = arg;
895 899 int ret = 0;
896 900 boolean_t out;
897 901
898 902 switch (pr_num) {
899 903 case MAC_PROP_VN_PROMISC_FILTERED:
900 904 out = mac_get_promisc_filtered(vn->vn_mch);
901 905 ASSERT(pr_valsize >= sizeof (boolean_t));
902 906 bcopy(&out, pr_val, sizeof (boolean_t));
903 907 break;
904 908 default:
905 909 ret = EINVAL;
906 910 break;
907 911 }
908 912
909 913 return (ret);
910 914 }
911 915
912 916 /* ARGSUSED */
913 917 static void vnic_m_propinfo(void *m_driver, const char *pr_name,
914 918 mac_prop_id_t pr_num, mac_prop_info_handle_t prh)
915 919 {
916 920 vnic_t *vn = m_driver;
917 921
918 922 /* MTU setting allowed only on an etherstub */
919 923 if (vn->vn_link_id != DATALINK_INVALID_LINKID)
920 924 return;
921 925
922 926 switch (pr_num) {
923 927 case MAC_PROP_MTU:
924 928 mac_prop_info_set_range_uint32(prh,
925 929 ANCHOR_VNIC_MIN_MTU, ANCHOR_VNIC_MAX_MTU);
926 930 break;
927 931 }
928 932 }
929 933
930 934
931 935 int
932 936 vnic_info(vnic_info_t *info, cred_t *credp)
933 937 {
934 938 vnic_t *vnic;
935 939 int err;
936 940
937 941 /* Make sure that the VNIC link is visible from the caller's zone. */
938 942 if (!dls_devnet_islinkvisible(info->vn_vnic_id, crgetzoneid(credp)))
939 943 return (ENOENT);
940 944
941 945 rw_enter(&vnic_lock, RW_WRITER);
942 946
943 947 err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(info->vn_vnic_id),
944 948 (mod_hash_val_t *)&vnic);
945 949 if (err != 0) {
946 950 rw_exit(&vnic_lock);
947 951 return (ENOENT);
948 952 }
949 953
950 954 info->vn_link_id = vnic->vn_link_id;
951 955 info->vn_mac_addr_type = vnic->vn_addr_type;
952 956 info->vn_mac_len = vnic->vn_addr_len;
953 957 bcopy(vnic->vn_addr, info->vn_mac_addr, MAXMACADDRLEN);
954 958 info->vn_mac_slot = vnic->vn_slot_id;
955 959 info->vn_mac_prefix_len = 0;
956 960 info->vn_vid = vnic->vn_vid;
957 961 info->vn_force = vnic->vn_force;
958 962 info->vn_vrid = vnic->vn_vrid;
959 963 info->vn_af = vnic->vn_af;
960 964
961 965 bzero(&info->vn_resource_props, sizeof (mac_resource_props_t));
962 966 if (vnic->vn_mch != NULL)
963 967 mac_resource_ctl_get(vnic->vn_mch, &info->vn_resource_props);
964 968
965 969 rw_exit(&vnic_lock);
966 970 return (0);
967 971 }
968 972
969 973 static void
970 974 vnic_notify_cb(void *arg, mac_notify_type_t type)
971 975 {
972 976 vnic_t *vnic = arg;
973 977
974 978 /*
975 979 * Do not deliver notifications if the vnic is not fully initialized
976 980 * or is in process of being torn down.
977 981 */
978 982 if (!vnic->vn_enabled)
979 983 return;
980 984
981 985 switch (type) {
982 986 case MAC_NOTE_UNICST:
983 987 /*
984 988 * Only the VLAN VNIC needs to be notified with primary MAC
985 989 * address change.
986 990 */
987 991 if (vnic->vn_addr_type != VNIC_MAC_ADDR_TYPE_PRIMARY)
988 992 return;
989 993
990 994 /* the unicast MAC address value */
991 995 mac_unicast_primary_get(vnic->vn_lower_mh, vnic->vn_addr);
992 996
993 997 /* notify its upper layer MAC about MAC address change */
994 998 mac_unicst_update(vnic->vn_mh, (const uint8_t *)vnic->vn_addr);
995 999 break;
996 1000
997 1001 case MAC_NOTE_LINK:
998 1002 mac_link_update(vnic->vn_mh,
999 1003 mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE));
1000 1004 break;
1001 1005
1002 1006 default:
1003 1007 break;
1004 1008 }
1005 1009 }
↓ open down ↓ |
111 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX