]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/liquidio/lio_ioctl.c
MFV r368464:
[FreeBSD/FreeBSD.git] / sys / dev / liquidio / lio_ioctl.c
1 /*
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2017 Cavium, Inc.. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
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
16  *       distribution.
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.
20  *
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.
32  */
33 /*$FreeBSD$*/
34
35 #include "lio_bsd.h"
36 #include "lio_common.h"
37 #include "lio_droq.h"
38 #include "lio_iq.h"
39 #include "lio_response_manager.h"
40 #include "lio_device.h"
41 #include "lio_network.h"
42 #include "lio_ctrl.h"
43 #include "cn23xx_pf_device.h"
44 #include "lio_image.h"
45 #include "lio_ioctl.h"
46 #include "lio_main.h"
47 #include "lio_rxtx.h"
48
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);
56
57 static inline bool
58 lio_is_valid_ether_addr(const uint8_t *addr)
59 {
60
61         return (!(0x01 & addr[0]) && !((addr[0] + addr[1] + addr[2] + addr[3] +
62                                         addr[4] + addr[5]) == 0x00));
63 }
64
65 static int
66 lio_change_dev_flags(struct ifnet *ifp)
67 {
68         struct lio_ctrl_pkt     nctrl;
69         struct lio              *lio = if_getsoftc(ifp);
70         struct octeon_device    *oct = lio->oct_dev;
71         int ret = 0;
72
73         bzero(&nctrl, sizeof(struct lio_ctrl_pkt));
74
75         /* Create a ctrl pkt command to be sent to core app. */
76         nctrl.ncmd.cmd64 = 0;
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;
80         nctrl.lio = lio;
81         nctrl.cb_fn = lio_ctrl_cmd_completion;
82
83         ret = lio_send_ctrl_pkt(oct, &nctrl);
84         if (ret)
85                 lio_dev_err(oct, "Failed to change flags ret %d\n", ret);
86
87         return (ret);
88 }
89
90 /*
91  * lio_ioctl : User calls this routine for configuring
92  * the interface.
93  *
94  * return 0 on success, positive on failure
95  */
96 int
97 lio_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
98 {
99         struct lio      *lio = if_getsoftc(ifp);
100         struct ifreq    *ifrequest = (struct ifreq *)data;
101         int     error = 0;
102
103         switch (cmd) {
104         case SIOCSIFADDR:
105                 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCSIFADDR\n");
106                 if_setflagbits(ifp, IFF_UP, 0);
107                 error = ether_ioctl(ifp, cmd, data);
108                 break;
109         case SIOCSIFMTU:
110                 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCSIFMTU\n");
111                 error = lio_change_mtu(ifp, ifrequest->ifr_mtu);
112                 break;
113         case SIOCSIFFLAGS:
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);
120                         } else {
121                                 if (!(atomic_load_acq_int(&lio->ifstate) &
122                                       LIO_IFSTATE_DETACH))
123                                         lio_open(lio);
124                         }
125                 } else {
126                         if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
127                                 lio_stop(ifp);
128                 }
129                 lio->if_flags = if_getflags(ifp);
130                 break;
131         case SIOCADDMULTI:
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);
135                 break;
136         case SIOCDELMULTI:
137                 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCSIFMULTI\n");
138                 break;
139         case SIOCSIFMEDIA:
140                 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCSIFMEDIA\n");
141         case SIOCGIFMEDIA:
142                 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCGIFMEDIA\n");
143         case SIOCGIFXMEDIA:
144                 lio_dev_dbg(lio->oct_dev, "ioctl: SIOCGIFXMEDIA\n");
145                 error = ifmedia_ioctl(ifp, ifrequest, &lio->ifmedia, cmd);
146                 break;
147         case SIOCSIFCAP:
148                 {
149                         int     features = ifrequest->ifr_reqcap ^
150                                         if_getcapenable(ifp);
151
152                         lio_dev_dbg(lio->oct_dev, "ioctl: SIOCSIFCAP (Set Capabilities)\n");
153
154                         if (!features)
155                                 break;
156
157                         if (features & IFCAP_TXCSUM) {
158                                 if_togglecapenable(ifp, IFCAP_TXCSUM);
159                                 if (if_getcapenable(ifp) & IFCAP_TXCSUM)
160                                         if_sethwassistbits(ifp, (CSUM_TCP |
161                                                                  CSUM_UDP |
162                                                                  CSUM_IP), 0);
163                                 else
164                                         if_sethwassistbits(ifp, 0,
165                                                         (CSUM_TCP | CSUM_UDP |
166                                                          CSUM_IP));
167                         }
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 |
172                                                            CSUM_TCP_IPV6), 0);
173                                 else
174                                         if_sethwassistbits(ifp, 0,
175                                                            (CSUM_UDP_IPV6 |
176                                                             CSUM_TCP_IPV6));
177                         }
178                         if (features & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6))
179                                 error |= lio_set_rx_csum(ifp, (features &
180                                                                (IFCAP_RXCSUM |
181                                                          IFCAP_RXCSUM_IPV6)));
182
183                         if (features & IFCAP_TSO4)
184                                 error |= lio_set_tso4(ifp);
185
186                         if (features & IFCAP_TSO6)
187                                 error |= lio_set_tso6(ifp);
188
189                         if (features & IFCAP_LRO)
190                                 error |= lio_set_lro(ifp);
191
192                         if (features & IFCAP_VLAN_HWTAGGING)
193                                 if_togglecapenable(ifp, IFCAP_VLAN_HWTAGGING);
194
195                         if (features & IFCAP_VLAN_HWFILTER)
196                                 if_togglecapenable(ifp, IFCAP_VLAN_HWFILTER);
197
198                         if (features & IFCAP_VLAN_HWTSO)
199                                 if_togglecapenable(ifp, IFCAP_VLAN_HWTSO);
200
201                         VLAN_CAPABILITIES(ifp);
202                         break;
203                 }
204         default:
205                 lio_dev_dbg(lio->oct_dev, "ioctl: UNKNOWN (0x%X)\n", (int)cmd);
206                 error = ether_ioctl(ifp, cmd, data);
207                 break;
208         }
209
210         return (error);
211 }
212
213 static int
214 lio_set_tso4(struct ifnet *ifp)
215 {
216         struct lio      *lio = if_getsoftc(ifp);
217
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);
222                 else
223                         if_sethwassistbits(ifp, 0, CSUM_IP_TSO);
224         } else {
225                 lio_dev_info(lio->oct_dev, "TSO4 capability not supported\n");
226                 return (EINVAL);
227         }
228
229         return (0);
230 }
231
232 static int
233 lio_set_tso6(struct ifnet *ifp)
234 {
235         struct lio      *lio = if_getsoftc(ifp);
236
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);
241                 else
242                         if_sethwassistbits(ifp, 0, CSUM_IP6_TSO);
243         } else {
244                 lio_dev_info(lio->oct_dev, "TSO6 capability not supported\n");
245                 return (EINVAL);
246         }
247
248         return (0);
249 }
250
251 static int
252 lio_set_rx_csum(struct ifnet *ifp, uint32_t data)
253 {
254         struct lio      *lio = if_getsoftc(ifp);
255         int     ret = 0;
256
257         if (if_getcapabilities(ifp) & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) {
258                 if_togglecapenable(ifp, (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6));
259
260                 if (data) {
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,
265                                                       LIO_LROIPV4 |
266                                                       LIO_LROIPV6);
267                                 if_togglecapenable(ifp, IFCAP_LRO);
268                         }
269                 }
270         } else {
271                 lio_dev_info(lio->oct_dev, "Rx checksum offload capability not supported\n");
272                 return (ENODEV);
273         }
274
275         return ((ret) ? EINVAL : 0);
276 }
277
278 static int
279 lio_set_lro(struct ifnet *ifp)
280 {
281         struct lio      *lio = if_getsoftc(ifp);
282         int     ret = 0;
283
284         if (!(if_getcapabilities(ifp) & IFCAP_LRO)) {
285                 lio_dev_info(lio->oct_dev, "LRO capability not supported\n");
286                 return (ENODEV);
287         }
288
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);
293
294                 if (lio_hwlro)
295                         ret = lio_set_feature(ifp, LIO_CMD_LRO_ENABLE, LIO_LROIPV4 |
296                                               LIO_LROIPV6);
297
298         } else if (if_getcapenable(ifp) & IFCAP_LRO) {
299                 if_togglecapenable(ifp, IFCAP_LRO);
300
301                 if (lio_hwlro)
302                         ret = lio_set_feature(ifp, LIO_CMD_LRO_DISABLE, LIO_LROIPV4 |
303                                               LIO_LROIPV6);
304         } else
305                 lio_dev_info(lio->oct_dev, "LRO requires RXCSUM");
306
307         return ((ret) ? EINVAL : 0);
308 }
309
310 static void
311 lio_mtu_ctl_callback(struct octeon_device *oct, uint32_t status, void *buf)
312 {
313         struct lio_soft_command *sc = buf;
314         volatile int            *mtu_sc_ctx;
315
316         mtu_sc_ctx = sc->ctxptr;
317
318         if (status) {
319                 lio_dev_err(oct, "MTU updation ctl instruction failed. Status: %llx\n",
320                             LIO_CAST64(status));
321                 *mtu_sc_ctx = -1;
322                 /*
323                  * This barrier is required to be sure that the
324                  * response has been written fully.
325                  */
326                 wmb();
327                 return;
328         }
329
330         *mtu_sc_ctx = 1;
331
332         /*
333          * This barrier is required to be sure that the response has been
334          * written fully.
335          */
336         wmb();
337 }
338
339 /* @param ifp is network device */
340 static int
341 lio_change_mtu(struct ifnet *ifp, int new_mtu)
342 {
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;
348         int     retval = 0;
349
350         if (lio->mtu == new_mtu)
351                 return (0);
352
353         /*
354          * Limit the MTU to make sure the ethernet packets are between
355          * LIO_MIN_MTU_SIZE bytes and LIO_MAX_MTU_SIZE bytes
356          */
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);
361                 return (EINVAL);
362         }
363
364         sc = lio_alloc_soft_command(oct, OCTEON_CMD_SIZE, 16,
365                                     sizeof(*mtu_sc_ctx));
366         if (sc == NULL)
367                 return (ENOMEM);
368
369         ncmd = (union octeon_cmd *)sc->virtdptr;
370         mtu_sc_ctx = sc->ctxptr;
371
372         *mtu_sc_ctx = 0;
373
374         ncmd->cmd64 = 0;
375         ncmd->s.cmd = LIO_CMD_CHANGE_MTU;
376         ncmd->s.param1 = new_mtu;
377
378         lio_swap_8B_data((uint64_t *)ncmd, (OCTEON_CMD_SIZE >> 3));
379
380         sc->iq_no = lio->linfo.txpciq[0].s.q_no;
381
382         lio_prepare_soft_command(oct, sc, LIO_OPCODE_NIC,
383                                  LIO_OPCODE_NIC_CMD, 0, 0, 0);
384
385         sc->callback = lio_mtu_ctl_callback;
386         sc->callback_arg = sc;
387         sc->wait_time = 5000;
388
389         retval = lio_send_soft_command(oct, sc);
390         if (retval == LIO_IQ_SEND_FAILED) {
391                 lio_dev_info(oct,
392                              "Failed to send MTU update Control message\n");
393                 retval = EBUSY;
394                 goto mtu_updation_failed;
395         }
396
397         /*
398          * Sleep on a wait queue till the cond flag indicates that the
399          * response arrived or timed-out.
400          */
401         lio_sleep_cond(oct, mtu_sc_ctx);
402
403         if (*mtu_sc_ctx < 0) {
404                 retval = EBUSY;
405                 goto mtu_updation_failed;
406         }
407         lio_dev_info(oct, "MTU Changed from %d to %d\n", if_getmtu(ifp),
408                      new_mtu);
409         if_setmtu(ifp, new_mtu);
410         lio->mtu = new_mtu;
411         retval = 0;                     /*
412                                          * this updation is make sure that LIO_IQ_SEND_STOP case
413                                          * also success
414                                          */
415
416 mtu_updation_failed:
417         lio_free_soft_command(oct, sc);
418
419         return (retval);
420 }
421
422 /* @param ifp network device */
423 int
424 lio_set_mac(struct ifnet *ifp, uint8_t *p)
425 {
426         struct lio_ctrl_pkt     nctrl;
427         struct lio              *lio = if_getsoftc(ifp);
428         struct octeon_device    *oct = lio->oct_dev;
429         int     ret = 0;
430
431         if (!lio_is_valid_ether_addr(p))
432                 return (EADDRNOTAVAIL);
433
434         bzero(&nctrl, sizeof(struct lio_ctrl_pkt));
435
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;
441         nctrl.lio = lio;
442         nctrl.cb_fn = lio_ctrl_cmd_completion;
443         nctrl.wait_time = 100;
444
445         nctrl.udd[0] = 0;
446         /* The MAC Address is presented in network byte order. */
447         memcpy((uint8_t *)&nctrl.udd[0] + 2, p, ETHER_HDR_LEN);
448
449         ret = lio_send_ctrl_pkt(lio->oct_dev, &nctrl);
450         if (ret < 0) {
451                 lio_dev_err(oct, "MAC Address change failed\n");
452                 return (ENOMEM);
453         }
454
455         memcpy(((uint8_t *)&lio->linfo.hw_addr) + 2, p, ETHER_HDR_LEN);
456
457         return (0);
458 }
459
460 /*
461  * \brief Converts a mask based on ifp flags
462  * @param ifp network device
463  *
464  * This routine generates a lio_ifflags mask from the ifp flags
465  * received from the OS.
466  */
467 static inline enum lio_ifflags
468 lio_get_new_flags(struct ifnet *ifp)
469 {
470         enum lio_ifflags f = LIO_IFFLAG_UNICAST;
471
472         if (if_getflags(ifp) & IFF_PROMISC)
473                 f |= LIO_IFFLAG_PROMISC;
474
475         if (if_getflags(ifp) & IFF_ALLMULTI)
476                 f |= LIO_IFFLAG_ALLMULTI;
477
478         if (if_getflags(ifp) & IFF_MULTICAST) {
479                 f |= LIO_IFFLAG_MULTICAST;
480
481                 /*
482                  * Accept all multicast addresses if there are more than we
483                  * can handle
484                  */
485                 if (if_getamcount(ifp) > LIO_MAX_MULTICAST_ADDR)
486                         f |= LIO_IFFLAG_ALLMULTI;
487         }
488         if (if_getflags(ifp) & IFF_BROADCAST)
489                 f |= LIO_IFFLAG_BROADCAST;
490
491         return (f);
492 }
493
494 static u_int
495 lio_copy_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
496 {
497         uint64_t *mc = arg;
498
499         if (cnt == LIO_MAX_MULTICAST_ADDR)
500                 return (0);
501
502         mc += cnt;
503         *mc = 0;
504         memcpy(((uint8_t *)mc) + 2, LLADDR(sdl), ETHER_ADDR_LEN);
505         /* no need to swap bytes */
506
507         return (1);
508 }
509
510 /* @param ifp network device */
511 static int
512 lio_set_mcast_list(struct ifnet *ifp)
513 {
514         struct lio              *lio = if_getsoftc(ifp);
515         struct octeon_device    *oct = lio->oct_dev;
516         struct lio_ctrl_pkt     nctrl;
517         int     mc_count;
518         int     ret;
519
520         bzero(&nctrl, sizeof(struct lio_ctrl_pkt));
521
522         /* Create a ctrl pkt command to be sent to core app. */
523         nctrl.ncmd.cmd64 = 0;
524         nctrl.ncmd.s.cmd = LIO_CMD_SET_MULTI_LIST;
525         nctrl.ncmd.s.param1 = lio_get_new_flags(ifp);
526         nctrl.iq_no = lio->linfo.txpciq[0].s.q_no;
527         nctrl.lio = lio;
528         nctrl.cb_fn = lio_ctrl_cmd_completion;
529
530         /* copy all the addresses into the udd */
531         mc_count = if_foreach_llmaddr(ifp, lio_copy_maddr, &nctrl.udd[0]);
532
533         /*
534          * Apparently, any activity in this call from the kernel has to
535          * be atomic. So we won't wait for response.
536          */
537         nctrl.wait_time = 0;
538         nctrl.ncmd.s.param2 = mc_count;
539         nctrl.ncmd.s.more = mc_count;
540
541         ret = lio_send_ctrl_pkt(lio->oct_dev, &nctrl);
542         if (ret < 0) {
543                 lio_dev_err(oct, "DEVFLAGS change failed in core (ret: 0x%x)\n",
544                             ret);
545         }
546
547         return ((ret) ? EINVAL : 0);
548 }