1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015 OmniTI Computer Consulting, Inc. All rights reserved.
14 */
15
16 #include "i40e_sw.h"
17
18 /*
19 * Add a MAC address on an already-general-locked i40e instance.
20 */
21 static int
22 i40e_add_mac_locked(i40e_t *i40e, const uint8_t *mac_addr)
23 {
24 i40e_hw_t *hw = &i40e->i40e_hw_space;
25 int i;
26
27 ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
28
29 if (i40e->i40e_state & I40E_SUSPENDED)
30 return (ECANCELED);
31
32
33 if (i40e->i40e_mac_used == i40e->i40e_mac_total)
34 return (ENOSPC);
35
36 /*
37 * The array of i40e_mac_addrs is ordered from
38 * 0..(i40e_mac_used - 1) to make insertion/deletion rather
39 * simple. WE don't care what order they're in, which costs O(N)
40 * search for both insertion (collision checking) and deletion
41 * (location), but this doesn't happen much. If we eliminate
42 * collision checking (like ixgbe seems to), we go to O(1) for
43 * insertion.
44 */
45
46 for (i = 0; i < i40e->i40e_mac_used; i++) {
47 if (bcmp(mac_addr, i40e->i40e_mac_addrs[i].i40eth_mac,
48 ETHERADDRL) == 0) {
49 /* Ooops, adding an ethernet we already know about. */
50 return (EEXIST);
51 }
52 }
53 ASSERT(i == i40e->i40e_mac_used);
54 ASSERT(!(i40e->i40e_mac_addrs[i].i40eth_used));
55
56 bcopy(mac_addr, i40e->i40e_mac_addrs[i].i40eth_mac, ETHERADDRL);
57 i40e->i40e_mac_used++;
58
59 return (i40e_hwadd_mac(i40e, hw, mac_addr) ? 0 : EIO);
60 }
61
62 /*
63 * Remove a MAC address on an already-general-locked i40e instance.
64 */
65 static int
66 i40e_remove_mac_locked(i40e_t *i40e, const uint8_t *mac_addr)
67 {
68 i40e_hw_t *hw = &i40e->i40e_hw_space;
69 struct i40e_aqc_remove_macvlan_element_data mvlist;
70 int i;
71 i40e_ether_addr_t *nukeme;
72
73 ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
74
75 if (i40e->i40e_state & I40E_SUSPENDED)
76 return (ECANCELED);
77
78 for (i = 0; i < i40e->i40e_mac_used; i++) {
79 if (bcmp(mac_addr, i40e->i40e_mac_addrs[i].i40eth_mac,
80 ETHERADDRL) == 0) {
81 break;
82 }
83 }
84
85 if (i == i40e->i40e_mac_used)
86 return (ENOENT); /* ixgbe uses EINVAL... why? */
87
88 nukeme = &(i40e->i40e_mac_addrs[i]);
89
90 /*
91 * Start by making sure the common-code/HW is okay with it.
92 */
93 bzero(&mvlist, sizeof (mvlist));
94 bcopy(mac_addr, mvlist.mac_addr, ETHERADDRL);
95 mvlist.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH |
96 I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
97 if (i40e_aq_remove_macvlan(hw, i40e->i40e_vsi_id, &mvlist, 1, NULL) !=
98 I40E_SUCCESS) {
99 return (EIO); /* XXX KEBE ASKS - better idea? */
100 }
101
102 /*
103 * Okay! Now that the board thinks it's good, we need to update
104 * ourselves.
105 */
106 i40e->i40e_mac_used--;
107 *nukeme = i40e->i40e_mac_addrs[i40e->i40e_mac_used];
108 i40e->i40e_mac_addrs[i40e->i40e_mac_used].i40eth_used = 0;
109
110 return (0);
111 }
112
113 /*
114 * GLDv3 receive group method: Add a MAC address.
115 */
116 static int
117 i40e_addmac(void *arg, const uint8_t *mac_addr)
118 {
119 i40e_t *i40e = arg;
120 int rc;
121
122 mutex_enter(&i40e->i40e_general_lock);
123 rc = i40e_add_mac_locked(i40e, mac_addr);
124 mutex_exit(&i40e->i40e_general_lock);
125 return (rc);
126 }
127
128 /*
129 * GLDv3 receive group method: Remove a MAC address.
130 */
131 static int
132 i40e_remmac(void *arg, const uint8_t *mac_addr)
133 {
134 i40e_t *i40e = arg;
135 int rc;
136
137 mutex_enter(&i40e->i40e_general_lock);
138 rc = i40e_remove_mac_locked(i40e, mac_addr);
139 mutex_exit(&i40e->i40e_general_lock);
140 return (rc);
141 }
142
143 /*
144 * GLDv3 method: Bring the device out of the reset/quiesced state that it was
145 * in when the interface was registered.
146 */
147 static int
148 i40e_m_start(void *arg)
149 {
150 i40e_t *i40e = arg;
151 int rc = 0;
152
153 mutex_enter(&i40e->i40e_general_lock);
154 if (i40e->i40e_state & I40E_SUSPENDED) {
155 rc = ECANCELED;
156 goto done;
157 }
158
159 if (!i40e_start(i40e, B_TRUE)) {
160 rc = EIO;
161 goto done;
162 }
163
164 atomic_or_32(&i40e->i40e_state, I40E_STARTED);
165
166 done:
167 mutex_exit(&i40e->i40e_general_lock);
168 if (rc == 0)
169 i40e_enable_watchdog_timer(i40e);
170 return (rc);
171 }
172
173 /*
174 * GLDv3 method: Stop the device and put it in a reset/quiesced state such
175 * that the interface can be unregistered.
176 */
177 static void
178 i40e_m_stop(void *arg)
179 {
180 i40e_t *i40e = arg;
181
182 mutex_enter(&i40e->i40e_general_lock);
183
184 if (i40e->i40e_state & I40E_SUSPENDED)
185 goto done;
186
187 atomic_and_32(&i40e->i40e_state, ~I40E_STARTED);
188
189 i40e_stop(i40e, B_TRUE);
190
191 done:
192 mutex_exit(&i40e->i40e_general_lock);
193
194 i40e_disable_watchdog_timer(i40e);
195 }
196
197 /*
198 * GLDv3 method: Enable or disable promiscuous mode.
199 */
200 static int
201 i40e_m_promisc(void *arg, boolean_t on)
202 {
203 i40e_t *i40e = arg;
204 struct i40e_hw *hw = &i40e->i40e_hw_space;
205 int rc = 0;
206
207 mutex_enter(&i40e->i40e_general_lock);
208 if (i40e->i40e_state & I40E_SUSPENDED) {
209 rc = ECANCELED;
210 goto done;
211 }
212
213 /*
214 * Turn on or off promiscuous. The i40e common code has two switches:
215 * unicast & multicast. Turn them BOTH on or off.
216 */
217 rc = i40e_aq_set_vsi_unicast_promiscuous(hw, i40e->i40e_vsi_id,
218 on, NULL);
219 switch (rc) {
220 case I40E_SUCCESS:
221 rc = 0; /* May be redundant... */
222 break;
223 /* XXX KEBE SAYS MORE VALUES to map here... */
224 default:
225 rc = EINVAL;
226 goto done;
227 }
228 rc = i40e_aq_set_vsi_multicast_promiscuous(hw, i40e->i40e_vsi_id,
229 on, NULL);
230 /* NOTE: If this fails, the unicast one has still taken, however. */
231 switch (rc) {
232 case I40E_SUCCESS:
233 rc = 0; /* May be redundant... */
234 break;
235 /* XXX KEBE SAYS MORE VALUES to map here... */
236 default:
237 rc = EINVAL;
238 break;
239 }
240
241 done:
242 mutex_exit(&i40e->i40e_general_lock);
243 return (rc);
244 }
245
246 /*
247 * Support functions for GLDv3 add/delete multicast address method.
248 *
249 * NOTE: It appears that the i40e HW doesn't differentiate between unicast and
250 * multicast in its filters. For now, act as a front-end to i40e_{add,rem}mac.
251 * IF we overrun with multicast, these functions should instead:
252 * 1.) Keep track of multicasts separately and
253 * 2.) Send a all-multicast indicator to i40e_{add,rem}mac() to produce a
254 * single HW filter that accepts ALL multicast traffic (but not promisc. per se
255 * if that's the case) and opens up more filter space.
256 *
257 * For now, however, these are VERY TINY functions. If we need to get smarter
258 * per above, we have the scaffolding in place now.
259 */
260 static int
261 i40e_multicast_add(i40e_t *i40e, const uint8_t *multicast_address)
262 {
263 return (i40e_add_mac_locked(i40e, multicast_address));
264 }
265
266 static int
267 i40e_multicast_remove(i40e_t *i40e, const uint8_t *multicast_address)
268 {
269 return (i40e_remove_mac_locked(i40e, multicast_address));
270 }
271
272 /*
273 * GLDv3 method: Add or delete a multicast address.
274 */
275 static int
276 i40e_m_multicast(void *arg, boolean_t add, const uint8_t *multicast_address)
277 {
278 i40e_t *i40e = arg;
279 int rc;
280
281 mutex_enter(&i40e->i40e_general_lock);
282
283 /*
284 * NOTE: This is redundant unless the above tiny support functions
285 * grow more sophisticated.
286 */
287 if (i40e->i40e_state & I40E_SUSPENDED) {
288 rc = ECANCELED;
289 goto done;
290 }
291
292 rc = (add) ? i40e_multicast_add(i40e, multicast_address) :
293 i40e_multicast_remove(i40e, multicast_address);
294
295 done:
296 mutex_exit(&i40e->i40e_general_lock);
297 return (rc);
298 }
299
300 /*
301 * GLDv3 method: Pass on M_IOCTL messages passed to the DLD, and support
302 * private IOCTLs for debugging and ndd. Yes this looks a little STREAMS-ish
303 * with its queue_t and mblk_t arguments.
304 */
305 static void
306 i40e_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
307 {
308 i40e_t *i40e = arg;
309 struct iocblk *iocp;
310 boolean_t send_nak;
311
312 iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
313 iocp->ioc_error = 0;
314
315 mutex_enter(&i40e->i40e_general_lock);
316 if (i40e->i40e_state & I40E_SUSPENDED) {
317 mutex_exit(&i40e->i40e_general_lock);
318 /*
319 * ixgbe returns EINVAL here, but other SUSPENDED cases
320 * in other methods return ECANCELED. Why different here?
321 * Because of STREAMS?
322 */
323 miocnak(q, mp, 0, EINVAL);
324 return;
325 }
326 mutex_exit(&i40e->i40e_general_lock);
327
328 switch (iocp->ioc_cmd) {
329 case LB_GET_INFO_SIZE:
330 case LB_GET_INFO:
331 case LB_GET_MODE:
332 case LB_SET_MODE:
333 /* XXX KEBE SAYS FILL ME IN with loopback ioctl handling. */
334 iocp->ioc_error = ENOTSUP;
335 send_nak = B_TRUE; /* send_nak = i40e_loopback_ioctl(...) */
336 break;
337
338 default:
339 send_nak = B_TRUE;
340 break;
341 }
342
343 /* Using STREAMS status, figure out what next. */
344 if (send_nak) {
345 /* NAK with a specified error or EINVAL. */
346 miocnak(q, mp, 0,
347 iocp->ioc_error == 0 ? EINVAL : iocp->ioc_error);
348 } else {
349 /* Callee prepared reply as ACK or NAK). */
350 mp->b_datap->db_type =
351 iocp->ioc_error == 0 ? M_IOCACK : M_IOCNAK;
352 qreply(q, mp);
353 }
354 }
355
356 /*
357 * Support functions for GLDv3 capabilities (ring fill-in functions).
358 */
359
360 /* Enable a ring interrupt. */
361 /* ARGSUSED */
362 int
363 i40e_rx_ring_intr_enable(mac_intr_handle_t intrh)
364 {
365 return (EINVAL);
366 }
367
368 /* Disable a ring interrupt. */
369 /* ARGSUSED */
370 int
371 i40e_rx_ring_intr_disable(mac_intr_handle_t intrh)
372 {
373 return (EINVAL);
374 }
375
376 /* Fill in transmit ring. */
377 static void
378 i40e_fill_tx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
379 const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
380 {
381 i40e_t *i40e = arg;
382 mac_intr_t *mintr = &infop->mri_intr;
383 i40e_trqpair_t *itrq = &(i40e->i40e_trqpairs[ring_index]);
384
385 ASSERT(group_index == -1);
386 ASSERT(ring_index < i40e->i40e_num_trqpairs);
387
388 /*
389 * Basically, we want to map the ring_index & ring handle handed to us
390 * from GLDv3 and bidirectionally map it to an i40e_tx_ring_t. We put
391 * "rh" into the txr_ring_handle (one direction) and put the
392 * i40e_tx_ring_t into the infop->mri_driver (other direction).
393 */
394
395 /* Map the appropriate itrq. */
396 itrq->itrq_mactxring = rh;
397
398 infop->mri_driver = (mac_ring_driver_t)itrq;
399 infop->mri_start = NULL;
400 infop->mri_stop = NULL;
401 infop->mri_tx = i40e_ring_tx;
402 infop->mri_stat = i40e_tx_ring_stat;
403
404 /*
405 * Fill in the one lone mintr field, only if MSI or MSIX, the
406 * mi_ddi_handle.
407 */
408 if (i40e->i40e_intr_type & (DDI_INTR_TYPE_MSIX | DDI_INTR_TYPE_MSI)) {
409 mintr->mi_ddi_handle =
410 i40e->i40e_intr_handles[itrq->itrq_tx_intrvec];
411 }
412 }
413
414 /*
415 * Receive ring start function for GLDv3
416 */
417 static int
418 i40e_ring_start(mac_ring_driver_t rh, uint64_t gen_num)
419 {
420 i40e_trqpair_t *itrq = (i40e_trqpair_t *)rh;
421
422 /*
423 * GLDv3 requires we keep track of a generation number, as it uses
424 * that number to keep track of whether or not a ring is active.
425 */
426 mutex_enter(&itrq->itrq_rx_lock);
427 itrq->itrq_rxgen = gen_num;
428 mutex_exit(&itrq->itrq_rx_lock);
429 return (0);
430 }
431
432 /* Fill in receive ring. */
433 static void
434 i40e_fill_rx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
435 const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
436 {
437 i40e_t *i40e = arg;
438 mac_intr_t *mintr = &infop->mri_intr;
439 i40e_trqpair_t *itrq = &(i40e->i40e_trqpairs[ring_index]);
440
441 /*
442 * Basically, we want to map the ring_index & ring handle handed to us
443 * from GLDv3 and bidirectionally map it to an i40e qpair. We put
444 * "rh" into the macrxring (one direction) and put the i40e_trqpair_t
445 * into the infop->mri_driver (other direction).
446 */
447 ASSERT3S(group_index, ==, 0);
448 ASSERT3S(ring_index, <, i40e->i40e_num_trqpairs);
449
450 /* Map the appropriate rxr, use ring_index as an index for now... */
451 itrq->itrq_macrxring = rh;
452
453 infop->mri_driver = (mac_ring_driver_t)itrq;
454 /* XXX KEBE SAYS FILL IN THESE WITH REAL FUNCTIONS... */
455 infop->mri_start = i40e_ring_start;
456 infop->mri_stop = NULL;
457 infop->mri_poll = i40e_ring_rx_poll;
458 infop->mri_stat = i40e_rx_ring_stat;
459
460 mintr->mi_handle = (mac_intr_handle_t)itrq;
461 mintr->mi_enable = i40e_rx_ring_intr_enable;
462 mintr->mi_disable = i40e_rx_ring_intr_disable;
463 /*
464 * Fill in the one lone mintr field, only if MSI or MSIX, the
465 * mi_ddi_handle.
466 */
467 if (i40e->i40e_intr_type & (DDI_INTR_TYPE_MSIX | DDI_INTR_TYPE_MSI)) {
468 mintr->mi_ddi_handle =
469 i40e->i40e_intr_handles[itrq->itrq_rx_intrvec];
470 }
471 }
472
473 /* Fill in receive group. */
474 void
475 i40e_fill_rx_group(void *arg, mac_ring_type_t rtype, const int index,
476 mac_group_info_t *infop, mac_group_handle_t gh)
477 {
478 i40e_t *i40e = arg;
479
480 /* It *IS* called i40e_fill_*RX*_group().... */
481 if (rtype != MAC_RING_TYPE_RX)
482 return;
483
484 i40e->i40e_rx_group_handle = gh;
485
486 /*
487 * NOTE: If we map rx groups to VSIs OR trqpairs, the following may
488 * need to change.
489 */
490 infop->mgi_driver = (mac_group_driver_t)i40e;
491 infop->mgi_start = NULL;
492 infop->mgi_stop = NULL;
493 infop->mgi_addmac = i40e_addmac;
494 infop->mgi_remmac = i40e_remmac;
495
496 ASSERT(i40e->i40e_num_rx_groups == 1); /* XXX KEBE SAYS FOR NOW... */
497 infop->mgi_count = i40e->i40e_num_trqpairs;
498 }
499
500 /*
501 * GLDv3 method: Express as GLDv3 capabilities what this driver can do.
502 */
503 static boolean_t
504 i40e_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
505 {
506 i40e_t *i40e = arg;
507 mac_capab_rings_t *cap_rings;
508
509 switch (cap) {
510 case MAC_CAPAB_HCKSUM:
511 /* Hardware checksumming info. */
512 if (!i40e->i40e_tx_hcksum_enable)
513 return (B_FALSE);
514 /* cap_data points to a uint32_t, needing flags filled in. */
515 *((uint32_t *)cap_data) =
516 HCKSUM_INET_PARTIAL | HCKSUM_IPHDRCKSUM;
517 break;
518 case MAC_CAPAB_LSO:
519 /* Large-segment offload (LSO) info. */
520 if (i40e->i40e_lso_enable) {
521 mac_capab_lso_t *cap_lso = cap_data;
522
523 /*
524 * Table 1-5 says LSO works for both IPv4 and IPv6,
525 * BUT IPv6 LSO isn't supported in GLDv3/MAC yet.
526 */
527 cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4;
528 cap_lso->lso_basic_tcp_ipv4.lso_max = I40E_LSO_MAXLEN;
529 } else {
530 return (B_FALSE);
531 }
532 break;
533 case MAC_CAPAB_RINGS:
534 /* XXX KEBE SAYS FILL ME IN with ring info, incl. functions. */
535 cap_rings = cap_data;
536 /* Anything common across both TX and RX? Fill that in here. */
537 cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
538 switch (cap_rings->mr_type) {
539 case MAC_RING_TYPE_TX:
540 /* Fill in transmit ring info. */
541 cap_rings->mr_rnum = i40e->i40e_num_trqpairs;
542 cap_rings->mr_gnum = 0;
543 cap_rings->mr_rget = i40e_fill_tx_ring;
544 cap_rings->mr_gget = NULL;
545 /* XXX KEBE ASKS -> make these functions?!? */
546 cap_rings->mr_gaddring = NULL;
547 cap_rings->mr_gremring = NULL;
548 break;
549 case MAC_RING_TYPE_RX:
550 /* Fill in receive ring info. */
551 cap_rings->mr_rnum = i40e->i40e_num_trqpairs;
552 cap_rings->mr_rget = i40e_fill_rx_ring;
553 /* XXX KEBE SAYS FIX FOR rx groups... */
554 cap_rings->mr_gnum = 1;
555 cap_rings->mr_gget = i40e_fill_rx_group;
556 /* XXX KEBE ASKS -> make these functions?!? */
557 cap_rings->mr_gaddring = NULL;
558 cap_rings->mr_gremring = NULL;
559 break;
560 default:
561 /* XXX KEBE ASKS - ixgbe does nothing here, why? */
562 return (B_FALSE);
563 }
564 break;
565 default:
566 return (B_FALSE);
567 }
568
569 return (B_TRUE);
570 }
571
572 /*
573 * GLDv3 method: Set the NIC's properties.
574 */
575 static int
576 i40e_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
577 uint_t pr_valsize, const void *pr_val)
578 {
579 i40e_t *i40e = arg;
580 int err = 0;
581
582 mutex_enter(&i40e->i40e_general_lock);
583 if (i40e->i40e_state & I40E_SUSPENDED) {
584 err = ECANCELED;
585 goto done;
586 }
587
588 /* XXX KEBE SAYS FILL ME IN */
589 err = ENOTSUP;
590
591 done:
592 mutex_exit(&i40e->i40e_general_lock);
593 return (err);
594 }
595
596 /*
597 * GLDv3 method: Get the NIC's properties.
598 */
599 static int
600 i40e_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
601 uint_t pr_valsize, void *pr_val)
602 {
603 /* i40e_t *i40e = arg; */
604 int err = 0;
605
606 switch (pr_num) {
607 case MAC_PROP_PRIVATE:
608 /* XXX KEBE SAYS FILL ME IN */
609 break;
610 default:
611 err = EINVAL;
612 break;
613 }
614
615 return (err);
616 }
617
618 /*
619 * GLDv3 method: Get the read/write data about a given property.
620 */
621 static void
622 i40e_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
623 mac_prop_info_handle_t prh)
624 {
625 /* XXX KEBE SAYS FILL ME IN */
626 }
627
628 #define I40E_M_CALLBACK_FLAGS \
629 (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO)
630
631 static mac_callbacks_t i40e_m_callbacks = {
632 I40E_M_CALLBACK_FLAGS,
633 i40e_m_stat,
634 i40e_m_start,
635 i40e_m_stop,
636 i40e_m_promisc,
637 i40e_m_multicast,
638 NULL,
639 NULL,
640 NULL,
641 i40e_m_ioctl,
642 i40e_m_getcapab,
643 NULL,
644 NULL,
645 i40e_m_setprop,
646 i40e_m_getprop,
647 i40e_m_propinfo
648 };
649
650 boolean_t
651 i40e_register_mac(i40e_t *i40e)
652 {
653 struct i40e_hw *hw = &i40e->i40e_hw_space;
654 int status;
655 mac_register_t *mac = mac_alloc(MAC_VERSION);
656
657 if (mac == NULL)
658 return (B_FALSE);
659
660 mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
661 mac->m_driver = i40e;
662 mac->m_dip = i40e->i40e_dip;
663 mac->m_src_addr = hw->mac.addr;
664 mac->m_callbacks = &i40e_m_callbacks;
665 mac->m_min_sdu = 0;
666 mac->m_max_sdu = i40e->i40e_default_mtu;
667 mac->m_margin = VLAN_TAGSZ;
668 mac->m_priv_props = NULL; /* XXX KEBE SAYS FIX ME */
669 mac->m_v12n = MAC_VIRT_LEVEL1;
670
671 status = mac_register(mac, &i40e->i40e_mac_hdl);
672 if (status != 0)
673 i40e_error(i40e, "mac_register() returned %d", status);
674 mac_free(mac);
675
676 return (status == 0);
677 }