4 * Copyright(c) 2017 Cavium, Inc.. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * * Neither the name of Cavium, Inc. nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 #include "lio_common.h"
39 #include "lio_response_manager.h"
40 #include "lio_device.h"
41 #include "lio_network.h"
43 #include "cn23xx_pf_device.h"
44 #include "lio_image.h"
45 #include "lio_ioctl.h"
49 static int lio_set_rx_csum(struct ifnet *ifp, uint32_t data);
50 static int lio_set_tso4(struct ifnet *ifp);
51 static int lio_set_tso6(struct ifnet *ifp);
52 static int lio_set_lro(struct ifnet *ifp);
53 static int lio_change_mtu(struct ifnet *ifp, int new_mtu);
54 static int lio_set_mcast_list(struct ifnet *ifp);
55 static inline enum lio_ifflags lio_get_new_flags(struct ifnet *ifp);
58 lio_is_valid_ether_addr(const uint8_t *addr)
61 return (!(0x01 & addr[0]) && !((addr[0] + addr[1] + addr[2] + addr[3] +
62 addr[4] + addr[5]) == 0x00));
66 lio_change_dev_flags(struct ifnet *ifp)
68 struct lio_ctrl_pkt nctrl;
69 struct lio *lio = if_getsoftc(ifp);
70 struct octeon_device *oct = lio->oct_dev;
73 bzero(&nctrl, sizeof(struct lio_ctrl_pkt));
75 /* Create a ctrl pkt command to be sent to core app. */
77 nctrl.ncmd.s.cmd = LIO_CMD_CHANGE_DEVFLAGS;
78 nctrl.ncmd.s.param1 = lio_get_new_flags(ifp);
79 nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
81 nctrl.cb_fn = lio_ctrl_cmd_completion;
83 ret = lio_send_ctrl_pkt(oct, &nctrl);
85 lio_dev_err(oct, "Failed to change flags ret %d\n", ret);
91 * lio_ioctl : User calls this routine for configuring
94 * return 0 on success, positive on failure
97 lio_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
99 struct lio *lio = if_getsoftc(ifp);
100 struct ifreq *ifrequest = (struct ifreq *)data;
105 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCSIFADDR\n");
106 if_setflagbits(ifp, IFF_UP, 0);
107 error = ether_ioctl(ifp, cmd, data);
110 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCSIFMTU\n");
111 error = lio_change_mtu(ifp, ifrequest->ifr_mtu);
114 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCSIFFLAGS\n");
115 if (if_getflags(ifp) & IFF_UP) {
116 if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
117 if ((if_getflags(ifp) ^ lio->if_flags) &
118 (IFF_PROMISC | IFF_ALLMULTI))
119 error = lio_change_dev_flags(ifp);
121 if (!(atomic_load_acq_int(&lio->ifstate) &
126 if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
129 lio->if_flags = if_getflags(ifp);
132 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCADDMULTI\n");
133 if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
134 error = lio_set_mcast_list(ifp);
137 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCSIFMULTI\n");
140 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCSIFMEDIA\n");
142 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCGIFMEDIA\n");
144 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCGIFXMEDIA\n");
145 error = ifmedia_ioctl(ifp, ifrequest, &lio->ifmedia, cmd);
149 int features = ifrequest->ifr_reqcap ^
150 if_getcapenable(ifp);
152 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCSIFCAP (Set Capabilities)\n");
157 if (features & IFCAP_TXCSUM) {
158 if_togglecapenable(ifp, IFCAP_TXCSUM);
159 if (if_getcapenable(ifp) & IFCAP_TXCSUM)
160 if_sethwassistbits(ifp, (CSUM_TCP |
164 if_sethwassistbits(ifp, 0,
165 (CSUM_TCP | CSUM_UDP |
168 if (features & IFCAP_TXCSUM_IPV6) {
169 if_togglecapenable(ifp, IFCAP_TXCSUM_IPV6);
170 if (if_getcapenable(ifp) & IFCAP_TXCSUM_IPV6)
171 if_sethwassistbits(ifp, (CSUM_UDP_IPV6 |
174 if_sethwassistbits(ifp, 0,
178 if (features & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6))
179 error |= lio_set_rx_csum(ifp, (features &
181 IFCAP_RXCSUM_IPV6)));
183 if (features & IFCAP_TSO4)
184 error |= lio_set_tso4(ifp);
186 if (features & IFCAP_TSO6)
187 error |= lio_set_tso6(ifp);
189 if (features & IFCAP_LRO)
190 error |= lio_set_lro(ifp);
192 if (features & IFCAP_VLAN_HWTAGGING)
193 if_togglecapenable(ifp, IFCAP_VLAN_HWTAGGING);
195 if (features & IFCAP_VLAN_HWFILTER)
196 if_togglecapenable(ifp, IFCAP_VLAN_HWFILTER);
198 if (features & IFCAP_VLAN_HWTSO)
199 if_togglecapenable(ifp, IFCAP_VLAN_HWTSO);
201 VLAN_CAPABILITIES(ifp);
205 lio_dev_dbg(lio->oct_dev, "ioctl: UNKNOWN (0x%X)\n", (int)cmd);
206 error = ether_ioctl(ifp, cmd, data);
214 lio_set_tso4(struct ifnet *ifp)
216 struct lio *lio = if_getsoftc(ifp);
218 if (if_getcapabilities(ifp) & IFCAP_TSO4) {
219 if_togglecapenable(ifp, IFCAP_TSO4);
220 if (if_getcapenable(ifp) & IFCAP_TSO4)
221 if_sethwassistbits(ifp, CSUM_IP_TSO, 0);
223 if_sethwassistbits(ifp, 0, CSUM_IP_TSO);
225 lio_dev_info(lio->oct_dev, "TSO4 capability not supported\n");
233 lio_set_tso6(struct ifnet *ifp)
235 struct lio *lio = if_getsoftc(ifp);
237 if (if_getcapabilities(ifp) & IFCAP_TSO6) {
238 if_togglecapenable(ifp, IFCAP_TSO6);
239 if (if_getcapenable(ifp) & IFCAP_TSO6)
240 if_sethwassistbits(ifp, CSUM_IP6_TSO, 0);
242 if_sethwassistbits(ifp, 0, CSUM_IP6_TSO);
244 lio_dev_info(lio->oct_dev, "TSO6 capability not supported\n");
252 lio_set_rx_csum(struct ifnet *ifp, uint32_t data)
254 struct lio *lio = if_getsoftc(ifp);
257 if (if_getcapabilities(ifp) & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) {
258 if_togglecapenable(ifp, (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6));
261 /* LRO requires RXCSUM */
262 if ((if_getcapabilities(ifp) & IFCAP_LRO) &&
263 (if_getcapenable(ifp) & IFCAP_LRO)) {
264 ret = lio_set_feature(ifp, LIO_CMD_LRO_DISABLE,
267 if_togglecapenable(ifp, IFCAP_LRO);
271 lio_dev_info(lio->oct_dev, "Rx checksum offload capability not supported\n");
275 return ((ret) ? EINVAL : 0);
279 lio_set_lro(struct ifnet *ifp)
281 struct lio *lio = if_getsoftc(ifp);
284 if (!(if_getcapabilities(ifp) & IFCAP_LRO)) {
285 lio_dev_info(lio->oct_dev, "LRO capability not supported\n");
289 if ((!(if_getcapenable(ifp) & IFCAP_LRO)) &&
290 (if_getcapenable(ifp) & IFCAP_RXCSUM) &&
291 (if_getcapenable(ifp) & IFCAP_RXCSUM_IPV6)) {
292 if_togglecapenable(ifp, IFCAP_LRO);
295 ret = lio_set_feature(ifp, LIO_CMD_LRO_ENABLE, LIO_LROIPV4 |
298 } else if (if_getcapenable(ifp) & IFCAP_LRO) {
299 if_togglecapenable(ifp, IFCAP_LRO);
302 ret = lio_set_feature(ifp, LIO_CMD_LRO_DISABLE, LIO_LROIPV4 |
305 lio_dev_info(lio->oct_dev, "LRO requires RXCSUM");
307 return ((ret) ? EINVAL : 0);
311 lio_mtu_ctl_callback(struct octeon_device *oct, uint32_t status, void *buf)
313 struct lio_soft_command *sc = buf;
314 volatile int *mtu_sc_ctx;
316 mtu_sc_ctx = sc->ctxptr;
319 lio_dev_err(oct, "MTU updation ctl instruction failed. Status: %llx\n",
323 * This barrier is required to be sure that the
324 * response has been written fully.
333 * This barrier is required to be sure that the response has been
339 /* @param ifp is network device */
341 lio_change_mtu(struct ifnet *ifp, int new_mtu)
343 struct lio *lio = if_getsoftc(ifp);
344 struct octeon_device *oct = lio->oct_dev;
345 struct lio_soft_command *sc;
346 union octeon_cmd *ncmd;
347 volatile int *mtu_sc_ctx;
350 if (lio->mtu == new_mtu)
354 * Limit the MTU to make sure the ethernet packets are between
355 * LIO_MIN_MTU_SIZE bytes and LIO_MAX_MTU_SIZE bytes
357 if ((new_mtu < LIO_MIN_MTU_SIZE) || (new_mtu > LIO_MAX_MTU_SIZE)) {
358 lio_dev_err(oct, "Invalid MTU: %d\n", new_mtu);
359 lio_dev_err(oct, "Valid range %d and %d\n",
360 LIO_MIN_MTU_SIZE, LIO_MAX_MTU_SIZE);
364 sc = lio_alloc_soft_command(oct, OCTEON_CMD_SIZE, 16,
365 sizeof(*mtu_sc_ctx));
369 ncmd = (union octeon_cmd *)sc->virtdptr;
370 mtu_sc_ctx = sc->ctxptr;
375 ncmd->s.cmd = LIO_CMD_CHANGE_MTU;
376 ncmd->s.param1 = new_mtu;
378 lio_swap_8B_data((uint64_t *)ncmd, (OCTEON_CMD_SIZE >> 3));
380 sc->iq_no = lio->linfo.txpciq[0].s.q_no;
382 lio_prepare_soft_command(oct, sc, LIO_OPCODE_NIC,
383 LIO_OPCODE_NIC_CMD, 0, 0, 0);
385 sc->callback = lio_mtu_ctl_callback;
386 sc->callback_arg = sc;
387 sc->wait_time = 5000;
389 retval = lio_send_soft_command(oct, sc);
390 if (retval == LIO_IQ_SEND_FAILED) {
392 "Failed to send MTU update Control message\n");
394 goto mtu_updation_failed;
398 * Sleep on a wait queue till the cond flag indicates that the
399 * response arrived or timed-out.
401 lio_sleep_cond(oct, mtu_sc_ctx);
403 if (*mtu_sc_ctx < 0) {
405 goto mtu_updation_failed;
407 lio_dev_info(oct, "MTU Changed from %d to %d\n", if_getmtu(ifp),
409 if_setmtu(ifp, new_mtu);
412 * this updation is make sure that LIO_IQ_SEND_STOP case
417 lio_free_soft_command(oct, sc);
422 /* @param ifp network device */
424 lio_set_mac(struct ifnet *ifp, uint8_t *p)
426 struct lio_ctrl_pkt nctrl;
427 struct lio *lio = if_getsoftc(ifp);
428 struct octeon_device *oct = lio->oct_dev;
431 if (!lio_is_valid_ether_addr(p))
432 return (EADDRNOTAVAIL);
434 bzero(&nctrl, sizeof(struct lio_ctrl_pkt));
436 nctrl.ncmd.cmd64 = 0;
437 nctrl.ncmd.s.cmd = LIO_CMD_CHANGE_MACADDR;
438 nctrl.ncmd.s.param1 = 0;
439 nctrl.ncmd.s.more = 1;
440 nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
442 nctrl.cb_fn = lio_ctrl_cmd_completion;
443 nctrl.wait_time = 100;
446 /* The MAC Address is presented in network byte order. */
447 memcpy((uint8_t *)&nctrl.udd[0] + 2, p, ETHER_HDR_LEN);
449 ret = lio_send_ctrl_pkt(lio->oct_dev, &nctrl);
451 lio_dev_err(oct, "MAC Address change failed\n");
455 memcpy(((uint8_t *)&lio->linfo.hw_addr) + 2, p, ETHER_HDR_LEN);
461 * \brief Converts a mask based on ifp flags
462 * @param ifp network device
464 * This routine generates a lio_ifflags mask from the ifp flags
465 * received from the OS.
467 static inline enum lio_ifflags
468 lio_get_new_flags(struct ifnet *ifp)
470 enum lio_ifflags f = LIO_IFFLAG_UNICAST;
472 if (if_getflags(ifp) & IFF_PROMISC)
473 f |= LIO_IFFLAG_PROMISC;
475 if (if_getflags(ifp) & IFF_ALLMULTI)
476 f |= LIO_IFFLAG_ALLMULTI;
478 if (if_getflags(ifp) & IFF_MULTICAST) {
479 f |= LIO_IFFLAG_MULTICAST;
482 * Accept all multicast addresses if there are more than we
485 if (if_getamcount(ifp) > LIO_MAX_MULTICAST_ADDR)
486 f |= LIO_IFFLAG_ALLMULTI;
488 if (if_getflags(ifp) & IFF_BROADCAST)
489 f |= LIO_IFFLAG_BROADCAST;
494 /* @param ifp network device */
496 lio_set_mcast_list(struct ifnet *ifp)
498 struct lio *lio = if_getsoftc(ifp);
499 struct octeon_device *oct = lio->oct_dev;
500 struct lio_ctrl_pkt nctrl;
501 struct ifmultiaddr *ifma;
506 bzero(&nctrl, sizeof(struct lio_ctrl_pkt));
508 /* Create a ctrl pkt command to be sent to core app. */
509 nctrl.ncmd.cmd64 = 0;
510 nctrl.ncmd.s.cmd = LIO_CMD_SET_MULTI_LIST;
511 nctrl.ncmd.s.param1 = lio_get_new_flags(ifp);
512 nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
514 nctrl.cb_fn = lio_ctrl_cmd_completion;
516 /* copy all the addresses into the udd */
519 /* to protect access to if_multiaddrs */
522 CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
523 if (ifma->ifma_addr->sa_family != AF_LINK)
526 memcpy(((uint8_t *)mc) + 2,
527 LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
529 /* no need to swap bytes */
532 if (++mc > &nctrl.udd[LIO_MAX_MULTICAST_ADDR])
536 if_maddr_runlock(ifp);
539 * Apparently, any activity in this call from the kernel has to
540 * be atomic. So we won't wait for response.
543 nctrl.ncmd.s.param2 = mc_count;
544 nctrl.ncmd.s.more = mc_count;
546 ret = lio_send_ctrl_pkt(lio->oct_dev, &nctrl);
548 lio_dev_err(oct, "DEVFLAGS change failed in core (ret: 0x%x)\n",
552 return ((ret) ? EINVAL : 0);