* Re: [PATCH] igb: Fix format with line continuation whitespace
From: Vinicius Costa Gomes @ 2018-11-15 1:04 UTC (permalink / raw)
To: Joe Perches, Jeff Kirsher
Cc: David S. Miller, intel-wired-lan, netdev, linux-kernel
In-Reply-To: <1b31c663afa1aa7b3d231917f00f58b9cb604231.camel@perches.com>
Joe Perches <joe@perches.com> writes:
> The line continuation unintentionally adds whitespace so
> instead use a coalesced format to remove the whitespace.
>
> Miscellanea:
>
> o Use a more typical style for ternaries and arguments
> for this logging message
>
> Signed-off-by: Joe Perches <joe@perches.com>
Acked-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
Cheers,
--
Vinicius
^ permalink raw reply
* Re: [PATCH net] ipvs: call ip_vs_dst_notifier before ipv6_dev_notf
From: Xin Long @ 2018-11-14 15:08 UTC (permalink / raw)
To: kbuild test robot
Cc: kbuild-all, network dev, netfilter-devel, davem,
Pablo Neira Ayuso, Hans Schillstrom
In-Reply-To: <201811141232.H4SG8SzP%fengguang.wu@intel.com>
On Wed, Nov 14, 2018 at 1:38 PM kbuild test robot <lkp@intel.com> wrote:
>
> Hi Xin,
>
> Thank you for the patch! Yet something to improve:
>
> [auto build test ERROR on net/master]
>
> url: https://github.com/0day-ci/linux/commits/Xin-Long/ipvs-call-ip_vs_dst_notifier-before-ipv6_dev_notf/20181114-055727
> config: i386-randconfig-x005-11131818 (attached as .config)
> compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
> reproduce:
> # save the attached .config to linux build tree
> make ARCH=i386
>
> All errors (new ones prefixed by >>):
>
> >> net/netfilter/ipvs/ip_vs_ctl.c:3983:14: error: 'ADDRCONF_NOTIFY_PRIORITY' undeclared here (not in a function); did you mean 'DEVCONF_NDISC_NOTIFY'?
> .priority = ADDRCONF_NOTIFY_PRIORITY + 5,
> ^~~~~~~~~~~~~~~~~~~~~~~~
> DEVCONF_NDISC_NOTIFY
>
> vim +3983 net/netfilter/ipvs/ip_vs_ctl.c
>
> 3980
> 3981 static struct notifier_block ip_vs_dst_notifier = {
> 3982 .notifier_call = ip_vs_dst_event,
> > 3983 .priority = ADDRCONF_NOTIFY_PRIORITY + 5,
> 3984 };
> 3985
This issue only exist on ipv6, I will fix it by simply using
#ifdef CONFIG_IP_VS_IPV6
>
> ---
> 0-DAY kernel test infrastructure Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply
* Re: [PATCHv2 net-next 1/4] sctp: define subscribe in sctp_sock as __u16
From: Xin Long @ 2018-11-14 15:11 UTC (permalink / raw)
To: Neil Horman; +Cc: network dev, linux-sctp, Marcelo Ricardo Leitner, davem
In-Reply-To: <20181113171521.GB7568@neilslaptop.think-freely.org>
On Wed, Nov 14, 2018 at 2:16 AM Neil Horman <nhorman@tuxdriver.com> wrote:
>
> On Tue, Nov 13, 2018 at 02:24:53PM +0800, Xin Long wrote:
> >
> > /* Default Peer Address Parameters. These defaults can
> > * be modified via SCTP_PEER_ADDR_PARAMS
> > @@ -5267,14 +5274,24 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
> > static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
> > int __user *optlen)
> > {
> > + struct sctp_event_subscribe subscribe;
> > + __u8 *sn_type = (__u8 *)&subscribe;
> > + int i;
> > +
> > if (len == 0)
> > return -EINVAL;
> > if (len > sizeof(struct sctp_event_subscribe))
> > len = sizeof(struct sctp_event_subscribe);
> > if (put_user(len, optlen))
> > return -EFAULT;
> > - if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
> > +
> > + for (i = 0; i <= len; i++)
> > + sn_type[i] = sctp_ulpevent_type_enabled(sctp_sk(sk)->subscribe,
> > + SCTP_SN_TYPE_BASE + i);
> > +
> This seems like an off by one error. sctp_event_subscribe has N bytes in it (1
> byte for each event), meaning that that events 0-(N-1) are subscribable.
> Iterating this loop imples that you are going to check N events, overrunning the
> sctp_event_subscribe struct.
you're right, thanks.
>
> Neil
>
> >
^ permalink raw reply
* [PATCH V3 6/7] net: maclorawan: Implement maclorawan class module
From: Jian-Hong Pan @ 2018-11-14 16:01 UTC (permalink / raw)
To: Andreas Färber, David S . Miller
Cc: netdev, linux-arm-kernel, linux-kernel, Marcel Holtmann,
Dollar Chen, Ken Yu, linux-wpan, Stefan Schmidt, Jian-Hong Pan
In-Reply-To: <20181105.101610.1437737564548154497.davem@davemloft.net>
LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa devices.
This patch implements part of Class A end-devices SoftMAC defined in
LoRaWAN(TM) Specification Ver. 1.0.2:
1. End-device receive slot timing
2. Only single channel and single data rate for now
3. Unconfirmed data up/down message types
On the other side, it defines the basic interface and operation
functions for compatible LoRa device drivers.
Signed-off-by: Jian-Hong Pan <starnight@g.ncu.edu.tw>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Modify for Big/Little-Endian
- Use SPDX license identifiers
V3:
- Remove the decoration word - inline of the functions
- Order local variables from longest to shortest line in the functions
- Change the calling mac_cb function to lrw_get_mac_cb macro
net/maclorawan/Kconfig | 14 +
net/maclorawan/Makefile | 2 +
net/maclorawan/mac.c | 520 ++++++++++++++++++++++++++++++++++
net/maclorawan/main.c | 605 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 1141 insertions(+)
create mode 100644 net/maclorawan/Kconfig
create mode 100644 net/maclorawan/Makefile
create mode 100644 net/maclorawan/mac.c
create mode 100644 net/maclorawan/main.c
diff --git a/net/maclorawan/Kconfig b/net/maclorawan/Kconfig
new file mode 100644
index 000000000000..177537d5f59f
--- /dev/null
+++ b/net/maclorawan/Kconfig
@@ -0,0 +1,14 @@
+config MACLORAWAN
+ tristate "Generic LoRaWAN Soft Networking Stack (maclorawan)"
+ depends on LORAWAN
+ select CRYPTO
+ select CRYPTO_CMAC
+ select CRYPTO_CBC
+ select CRYPTO_AES
+ ---help---
+ This option enables the hardware independent LoRaWAN
+ networking stack for SoftMAC devices (the ones implementing
+ only PHY level of LoRa standard).
+
+ If you plan to use HardMAC LoRaWAN devices, you can say N
+ here. Alternatively you can say M to compile it as a module.
diff --git a/net/maclorawan/Makefile b/net/maclorawan/Makefile
new file mode 100644
index 000000000000..562831e66c82
--- /dev/null
+++ b/net/maclorawan/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MACLORAWAN) += maclorawan.o
+maclorawan-objs := main.o mac.o crypto.o
diff --git a/net/maclorawan/mac.c b/net/maclorawan/mac.c
new file mode 100644
index 000000000000..2d9f322c364e
--- /dev/null
+++ b/net/maclorawan/mac.c
@@ -0,0 +1,520 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <starnight@g.ncu.edu.tw>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/poll.h>
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/lora/lorawan.h>
+#include <linux/lora/lorawan_netdev.h>
+
+#include "maclorawan.h"
+#include "crypto.h"
+
+static void rx_timeout_work(struct work_struct *work);
+
+struct lrw_session *
+lrw_alloc_ss(struct lrw_struct *lrw_st)
+{
+ struct lrw_session *ss;
+
+ ss = kzalloc(sizeof(struct lrw_session), GFP_KERNEL);
+ if (!ss)
+ goto lrw_alloc_ss_end;
+
+ ss->lrw_st = lrw_st;
+ ss->devaddr = lrw_st->devaddr;
+ INIT_LIST_HEAD(&ss->entry);
+
+ ss->tx_should_ack = false;
+ ss->retry = 3;
+ spin_lock_init(&ss->state_lock);
+ INIT_WORK(&ss->timeout_work, rx_timeout_work);
+
+lrw_alloc_ss_end:
+ return ss;
+}
+
+void
+lrw_free_ss(struct lrw_session *ss)
+{
+ netdev_dbg(ss->lrw_st->ndev, "%s\n", __func__);
+ if (ss->tx_skb)
+ consume_skb(ss->tx_skb);
+ netdev_dbg(ss->lrw_st->ndev, "%s: free rx skb\n", __func__);
+ if (ss->rx_skb)
+ consume_skb(ss->rx_skb);
+
+ netdev_dbg(ss->lrw_st->ndev, "%s: free ss\n", __func__);
+ kfree(ss);
+}
+
+void
+lrw_del_ss(struct lrw_session *ss)
+{
+ netdev_dbg(ss->lrw_st->ndev, "%s\n", __func__);
+ list_del(&ss->entry);
+ lrw_free_ss(ss);
+}
+
+void
+lrw_del_all_ss(struct lrw_struct *lrw_st)
+{
+ struct lrw_session *ss, *tmp;
+
+ mutex_lock(&lrw_st->ss_list_lock);
+ lrw_st->_cur_ss = NULL;
+ list_for_each_entry_safe(ss, tmp, &lrw_st->ss_list, entry) {
+ del_timer(&ss->timer);
+ lrw_del_ss(ss);
+ }
+ mutex_unlock(&lrw_st->ss_list_lock);
+}
+
+void
+lrw_ready_hw(struct lrw_struct *lrw_st)
+{
+ lrw_st->state = LRW_STATE_IDLE;
+}
+
+int
+lrw_start_hw(struct lrw_struct *lrw_st)
+{
+ int ret = 0;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+ lrw_st->nwks_shash_tfm = lrw_mic_key_setup(lrw_st->nwkskey,
+ LRW_KEY_LEN);
+ lrw_st->nwks_skc_tfm = lrw_encrypt_key_setup(lrw_st->nwkskey,
+ LRW_KEY_LEN);
+ lrw_st->apps_skc_tfm = lrw_encrypt_key_setup(lrw_st->appskey,
+ LRW_KEY_LEN);
+ lrw_st->state = LRW_START;
+ ret = lrw_st->ops->start(&lrw_st->hw);
+ if (!ret)
+ lrw_ready_hw(lrw_st);
+
+ return ret;
+}
+
+void
+lrw_stop_hw(struct lrw_struct *lrw_st)
+{
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+ lrw_st->state = LRW_STOP;
+ netdev_dbg(lrw_st->ndev, "%s: going to stop hardware\n", __func__);
+ lrw_st->ops->stop(&lrw_st->hw);
+
+ netdev_dbg(lrw_st->ndev, "%s: going to kill tasks & flush works", __func__);
+ tasklet_kill(&lrw_st->xmit_task);
+ flush_work(&lrw_st->rx_work);
+
+ netdev_dbg(lrw_st->ndev, "%s: going to delete all session\n", __func__);
+ lrw_del_all_ss(lrw_st);
+
+ netdev_dbg(lrw_st->ndev, "%s: going to free mic tfm\n", __func__);
+ lrw_mic_key_free(lrw_st->nwks_shash_tfm);
+ netdev_dbg(lrw_st->ndev, "%s: going to free nwks tfm\n", __func__);
+ lrw_encrypt_key_free(lrw_st->nwks_skc_tfm);
+ netdev_dbg(lrw_st->ndev, "%s: going to free apps tfm\n", __func__);
+ lrw_encrypt_key_free(lrw_st->apps_skc_tfm);
+}
+
+void
+lrw_prepare_tx_frame(struct lrw_session *ss)
+{
+ struct lrw_struct *lrw_st = ss->lrw_st;
+ struct sk_buff *skb = ss->tx_skb;
+ u8 mhdr, fctrl, fport, mic[4];
+ __le32 le_devaddr;
+ __le16 le_fcnt_up;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ /* Encrypt the plain buffer content */
+ lrw_encrypt_buf(lrw_st->apps_skc_tfm, LRW_UPLINK,
+ ss->devaddr, ss->fcnt_up, skb->data, skb->len);
+
+ /* Push FPort */
+ if (skb->len) {
+ fport = ss->fport;
+ memcpy(skb_push(skb, LRW_FPORT_LEN), &fport, LRW_FPORT_LEN);
+ }
+
+ /* Push FCnt_Up */
+ le_fcnt_up = cpu_to_le16(ss->fcnt_up);
+ memcpy(skb_push(skb, 2), &le_fcnt_up, 2);
+
+ /* Push FCtrl */
+ fctrl = 0;
+ if (lrw_st->rx_should_ack) {
+ fctrl |= 0x20;
+ lrw_st->rx_should_ack = false;
+ }
+ memcpy(skb_push(skb, 1), &fctrl, 1);
+
+ /* Push DevAddr */
+ le_devaddr = cpu_to_le32(ss->devaddr);
+ memcpy(skb_push(skb, LRW_DEVADDR_LEN), &le_devaddr, LRW_DEVADDR_LEN);
+
+ /* Push MHDR */
+ mhdr = LRW_UNCONFIRMED_DATA_UP << 5;
+ if ((mhdr & (0x6 << 5)) == (0x4 << 5))
+ ss->tx_should_ack = true;
+ memcpy(skb_push(skb, LRW_MHDR_LEN), &mhdr, LRW_MHDR_LEN);
+
+ /* Put MIC */
+ lrw_calc_mic(lrw_st->nwks_shash_tfm, LRW_UPLINK,
+ ss->devaddr, ss->fcnt_up, skb->data, skb->len, mic);
+ memcpy(skb_put(skb, LRW_MIC_LEN), mic, LRW_MIC_LEN);
+}
+
+void
+lrw_xmit(unsigned long data)
+{
+ struct lrw_struct *lrw_st = (struct lrw_struct *) data;
+ struct lrw_session *ss = lrw_st->_cur_ss;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+ ss->state = LRW_XMITTING_SS;
+ lrw_st->ops->xmit_async(&lrw_st->hw, ss->tx_skb);
+}
+
+void
+lrw_parse_frame(struct lrw_session *ss, struct sk_buff *skb)
+{
+ struct lrw_fhdr *fhdr = &ss->rx_fhdr;
+ __le16 *p_fcnt;
+
+ pr_debug("%s: %s\n", LORAWAN_MODULE_NAME, __func__);
+
+ /* Get message type */
+ fhdr->mtype = skb->data[0];
+ skb_pull(skb, LRW_MHDR_LEN);
+
+ /* Trim Device Address */
+ skb_pull(skb, 4);
+
+ /* Get frame control */
+ fhdr->fctrl = skb->data[0];
+ skb_pull(skb, 1);
+
+ /* Ack the original TX frame if it should be acked */
+ if (ss->tx_should_ack && (fhdr->fctrl & 0x20))
+ ss->tx_should_ack = false;
+
+ /* Get frame count */
+ p_fcnt = (__le16 *)skb->data;
+ fhdr->fcnt = le16_to_cpu(*p_fcnt);
+ skb_pull(skb, 2);
+
+ /* Get frame options */
+ fhdr->fopts_len = fhdr->fctrl & 0xF;
+ if (fhdr->fopts_len > 0) {
+ memcpy(fhdr->fopts, skb->data, fhdr->fopts_len);
+ skb_pull(skb, fhdr->fopts_len);
+ }
+
+ /* TODO: Parse frame options */
+
+ /* Remove message integrity code */
+ skb_trim(skb, skb->len - LRW_MIC_LEN);
+}
+
+struct lrw_session *
+lrw_rx_skb_2_session(struct lrw_struct *lrw_st, struct sk_buff *rx_skb)
+{
+ struct lrw_session *ss;
+ __le16 *p_fcnt;
+ u16 fcnt;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ p_fcnt = (__le16 *)(rx_skb->data + 6);
+ fcnt = le16_to_cpu(*p_fcnt);
+
+ /* Find the corresponding session */
+ ss = lrw_st->_cur_ss;
+
+ /* Frame count downlink check */
+ if (fcnt >= (ss->fcnt_down & 0xFFFF))
+ ss->rx_skb = rx_skb;
+ else
+ ss = NULL;
+
+ return ss;
+}
+
+void
+lrw_rx_work(struct work_struct *work)
+{
+ struct lrw_struct *lrw_st;
+ struct lrw_session *ss;
+ struct sk_buff *skb;
+
+ lrw_st = container_of(work, struct lrw_struct, rx_work);
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ skb = lrw_st->rx_skb_list.next;
+ skb_dequeue(&lrw_st->rx_skb_list);
+
+ /* Check and parse the RX frame */
+ ss = lrw_rx_skb_2_session(lrw_st, skb);
+ if (!ss)
+ goto lrw_rx_work_not_new_frame;
+
+ lrw_parse_frame(ss, skb);
+
+ /* Check the TX frame is acked or not */
+ if (ss->tx_should_ack) {
+ ss->rx_skb = NULL;
+ goto lrw_rx_work_not_new_frame;
+ }
+
+ /* The TX frame is acked or no need to be acked */
+ del_timer(&ss->timer);
+ lrw_st->rx_should_ack = (ss->rx_fhdr.mtype & 0xC0) == 0x40;
+
+ lrw_st->ndev->stats.rx_packets++;
+ lrw_st->ndev->stats.rx_bytes += ss->rx_skb->len;
+
+ if (ss->rx_skb->len > 0) {
+ spin_lock_bh(&ss->state_lock);
+ ss->state = LRW_RXRECEIVED_SS;
+ spin_unlock_bh(&ss->state_lock);
+
+ lrw_get_mac_cb(skb)->devaddr = lrw_st->devaddr;
+ netif_receive_skb(skb);
+
+ ss->rx_skb = NULL;
+ }
+
+ mutex_lock(&lrw_st->ss_list_lock);
+ lrw_st->fcnt_down = ss->rx_fhdr.fcnt;
+ lrw_st->_cur_ss = NULL;
+ lrw_del_ss(ss);
+ lrw_st->state = LRW_STATE_IDLE;
+ mutex_unlock(&lrw_st->ss_list_lock);
+
+ return;
+
+lrw_rx_work_not_new_frame:
+ /* Drop the RX frame if checked failed */
+ kfree_skb(skb);
+}
+
+int
+lrw_check_mic(struct crypto_shash *tfm, struct sk_buff *skb)
+{
+ u32 devaddr;
+ size_t len;
+ u8 cks[4];
+ u16 fcnt;
+ u8 *buf;
+ u8 *mic;
+
+ buf = skb->data;
+ devaddr = le32_to_cpu(*((__le32 *)(buf + LRW_MHDR_LEN)));
+ fcnt = le16_to_cpu(*(__le16 *)(buf + 6));
+ len = skb->len - 4;
+ mic = skb->data + len;
+
+ lrw_calc_mic(tfm, LRW_DOWNLINK, devaddr, fcnt, buf, len, cks);
+
+ return (!memcmp(cks, mic, 4));
+}
+
+/**
+ * lrw_rx_irqsave - Tell LoRaWAN module that there is new received frame
+ * @hw: the LoRa device
+ * @skb: the new received frame
+ */
+void
+lrw_rx_irqsave(struct lrw_hw *hw, struct sk_buff *skb)
+{
+ struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+ u32 devaddr;
+ u8 mtype;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ mtype = skb->data[0] >> 5;
+ devaddr = le32_to_cpu(*(__le32 *)(skb->data + LRW_MHDR_LEN));
+
+ /* Check the frame is the downlink frame */
+ if (((mtype == LRW_UNCONFIRMED_DATA_DOWN)
+ || (mtype == LRW_CONFIRMED_DATA_DOWN))
+ && (devaddr == lrw_st->devaddr)
+ && lrw_check_mic(lrw_st->nwks_shash_tfm, skb)) {
+ skb_queue_tail(&lrw_st->rx_skb_list, skb);
+ schedule_work(&lrw_st->rx_work);
+ }
+ else {
+ kfree_skb(skb);
+ }
+}
+EXPORT_SYMBOL(lrw_rx_irqsave);
+
+static void
+lrw_rexmit(struct timer_list *timer)
+{
+ struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+ struct lrw_struct *lrw_st = ss->lrw_st;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ lrw_st->state = LRW_STATE_TX;
+ lrw_xmit((unsigned long) lrw_st);
+}
+
+static void
+rx_timeout_work(struct work_struct *work)
+{
+ struct lrw_struct *lrw_st;
+ struct lrw_session *ss;
+
+ ss = container_of(work, struct lrw_session, timeout_work);
+ lrw_st = ss->lrw_st;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+ mutex_lock(&lrw_st->ss_list_lock);
+ lrw_st->_cur_ss = NULL;
+ lrw_st->state = LRW_STATE_IDLE;
+ lrw_del_ss(ss);
+ mutex_unlock(&lrw_st->ss_list_lock);
+}
+
+static void
+rx2_timeout_isr(struct timer_list *timer)
+{
+ struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+ struct lrw_struct *lrw_st = ss->lrw_st;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ /* Check TX is acked or not */
+ if (!ss->tx_should_ack) {
+ spin_lock_bh(&ss->state_lock);
+ if (ss->state != LRW_RXRECEIVED_SS)
+ ss->state = LRW_RXTIMEOUT_SS;
+ spin_unlock_bh(&ss->state_lock);
+
+ if (ss->state == LRW_RXTIMEOUT_SS) {
+ netdev_dbg(lrw_st->ndev, "%s: rx time out\n", __func__);
+ goto rx2_timeout_isr_no_retry_rx_frame;
+ }
+ else {
+ return;
+ }
+ }
+
+ /* Check the session need to be retransmitted or not */
+ if (ss->retry > 0) {
+ ss->state = LRW_RETRANSMIT_SS;
+ ss->retry--;
+
+ /* Start timer for ack timeout and retransmit */
+ ss->timer.function = lrw_rexmit;
+ ss->timer.expires = jiffies_64 + ss->ack_timeout * HZ;
+ add_timer(&ss->timer);
+ }
+ else {
+ /* Retry failed */
+rx2_timeout_isr_no_retry_rx_frame:
+ schedule_work(&ss->timeout_work);
+ }
+}
+
+static void
+rx2_delay_isr(struct timer_list *timer)
+{
+ struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+ struct lrw_struct *lrw_st = ss->lrw_st;
+ unsigned long delay;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ /* Start timer for RX2 window */
+ ss->timer.function = rx2_timeout_isr;
+ delay = jiffies_64 + (ss->rx2_window + 20) * HZ / 1000 + HZ;
+ ss->timer.expires = delay;
+ add_timer(&ss->timer);
+
+ /* Start LoRa hardware to RX2 window */
+ ss->state = LRW_RX2_SS;
+ lrw_st->ops->start_rx_window(&lrw_st->hw, ss->rx2_window + 20);
+}
+
+static void
+rx1_delay_isr(struct timer_list *timer)
+{
+ struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+ struct lrw_struct *lrw_st = ss->lrw_st;
+ unsigned long delay;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ /* Start timer for RX_Delay2 - RX_Delay2 */
+ ss->timer.function = rx2_delay_isr;
+ delay = jiffies_64 + (ss->rx_delay2 - ss->rx_delay1) * HZ - 20 * HZ / 1000;
+ ss->timer.expires = delay;
+ add_timer(&ss->timer);
+
+ /* Start LoRa hardware to RX1 window */
+ ss->state = LRW_RX1_SS;
+ lrw_st->ops->start_rx_window(&lrw_st->hw, ss->rx1_window + 20);
+}
+
+void
+lrw_sent_tx_work(struct lrw_struct *lrw_st, struct sk_buff *skb)
+{
+ struct lrw_session *ss = lrw_st->_cur_ss;
+ struct net_device *ndev;
+ unsigned long delay;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ ss->state = LRW_XMITTED;
+
+ /* Start session timer for RX_Delay1 */
+ timer_setup(&ss->timer, rx1_delay_isr, 0);
+ delay = jiffies_64 + ss->rx_delay1 * HZ - 20 * HZ / 1000;
+ ss->timer.expires = delay;
+ add_timer(&ss->timer);
+
+ ndev = skb->dev;
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+ dev_consume_skb_any(skb);
+ ss->tx_skb = NULL;
+}
+
+/**
+ * lrw_xmit_complete - Tell LoRaWAN module that the frame is xmitted completely
+ * @hw: the LoRa device
+ * @skb: the xmitted frame
+ */
+void
+lrw_xmit_complete(struct lrw_hw *hw, struct sk_buff *skb)
+{
+ struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ lrw_sent_tx_work(lrw_st, skb);
+ lrw_st->state = LRW_STATE_RX;
+}
+EXPORT_SYMBOL(lrw_xmit_complete);
diff --git a/net/maclorawan/main.c b/net/maclorawan/main.c
new file mode 100644
index 000000000000..9ca778706880
--- /dev/null
+++ b/net/maclorawan/main.c
@@ -0,0 +1,605 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <starnight@g.ncu.edu.tw>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+
+#include <linux/lora/lorawan.h>
+#include <linux/lora/lorawan_netdev.h>
+#include "maclorawan.h"
+
+#define PHY_NAME "lora"
+
+/* Need to find a way to define or assign */
+#define LORAWAN_MTU 20
+
+static struct class *lrw_sys_class;
+
+static void
+lrw_if_setup(struct net_device *ndev)
+{
+ ndev->addr_len = LRW_DEVADDR_LEN;
+ memset(ndev->broadcast, 0xFF, ndev->addr_len);
+ ndev->type = ARPHRD_LORAWAN;
+
+ ndev->hard_header_len = LRW_MHDR_LEN + LRW_FHDR_MAX_LEN + LRW_FPORT_LEN;
+ ndev->needed_tailroom = LRW_MIC_LEN;
+
+ /**
+ * TODO: M should be a dynamic value defined by Regional Parameters,
+ * Being fixed for now. Going to be changed.
+ */
+ ndev->mtu = LORAWAN_MTU;
+}
+
+/**
+ * lrw_alloc_hw - Allocate a memory space for the LoRa device
+ * @priv_data_len: the private data size
+ * @lrw_operations: the implemented operations of the LoRa device
+ *
+ * Return: address of the LoRa device or NULL for failed
+ */
+struct lrw_hw *
+lrw_alloc_hw(size_t priv_data_len, struct lrw_operations *ops)
+{
+ struct lrw_struct *lrw_st;
+ struct net_device *ndev;
+ int ret;
+
+ if (WARN_ON(!ops || !ops->start || !ops->stop || !ops->xmit_async ||
+ !ops->set_txpower || !ops->set_dr ||
+ !ops->start_rx_window || !ops->set_state))
+ return NULL;
+
+ /* In memory it'll be like this:
+ *
+ * +-----------------------+
+ * | struct net_device |
+ * +-----------------------+
+ * | struct lrw_struct |
+ * +-----------------------+
+ * | driver's private data |
+ * +-----------------------+
+ */
+ ndev = alloc_netdev(sizeof(struct lrw_struct) + priv_data_len,
+ PHY_NAME"%d", NET_NAME_ENUM, lrw_if_setup);
+ if (!ndev)
+ return ERR_PTR(-ENOMEM);
+ ret = dev_alloc_name(ndev, ndev->name);
+ if (ret < 0)
+ goto lrw_alloc_hw_err;
+
+ lrw_st = (struct lrw_struct *)netdev_priv(ndev);
+ lrw_st->ndev = ndev;
+
+ lrw_st->state = LRW_STOP;
+ lrw_st->ops = ops;
+ lrw_st->hw.priv = (void *) lrw_st + sizeof(struct lrw_struct);
+
+ ndev->flags |= IFF_NOARP;
+ ndev->features |= NETIF_F_HW_CSUM;
+
+ return &lrw_st->hw;
+
+lrw_alloc_hw_err:
+ free_netdev(ndev);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(lrw_alloc_hw);
+
+/**
+ * lrw_free_hw - Free the LoRa device's memory resource
+ * @hw: the LoRa device going to be freed
+ */
+void
+lrw_free_hw(struct lrw_hw *hw)
+{
+ struct lrw_struct *lrw_st;
+
+ lrw_st = container_of(hw, struct lrw_struct, hw);
+ free_netdev(lrw_st->ndev);
+}
+EXPORT_SYMBOL(lrw_free_hw);
+
+/**
+ * lrw_set_deveui - Set the LoRa device's DevEUI
+ * @hw: the LoRa device going to be set
+ * @eui: the global end-device ID in IEEE EUI64 address space
+ */
+void
+lrw_set_deveui(struct lrw_hw *hw, u64 eui)
+{
+ struct lrw_struct *lrw_st;
+
+ lrw_st = container_of(hw, struct lrw_struct, hw);
+ lrw_st->dev_eui = eui;
+}
+EXPORT_SYMBOL(lrw_set_deveui);
+
+/**
+ * lrw_get_deveui - Get the LoRa device's DevEUI
+ * @hw: the LoRa device going to be got from
+ *
+ * Return: the device's DevEUI in IEEE EUI64 address space
+ */
+u64
+lrw_get_deveui(struct lrw_hw *hw)
+{
+ struct lrw_struct *lrw_st;
+
+ lrw_st = container_of(hw, struct lrw_struct, hw);
+ return lrw_st->dev_eui;
+}
+EXPORT_SYMBOL(lrw_get_deveui);
+
+/**
+ * lrw_set_appeui - Set the LoRa device's AppEUI
+ * @hw: the LoRa device going to be set
+ * @eui: the global end-device ID in IEEE EUI64 address space
+ */
+void
+lrw_set_appeui(struct lrw_hw *hw, u64 eui)
+{
+ struct lrw_struct *lrw_st;
+
+ lrw_st = container_of(hw, struct lrw_struct, hw);
+ lrw_st->app_eui = eui;
+}
+EXPORT_SYMBOL(lrw_set_appeui);
+
+/**
+ * lrw_get_appeui - Get the LoRa device's AppEUI
+ * @hw: the LoRa device going to be got from
+ *
+ * Return: the device's AppEUI in IEEE EUI64 address space
+ */
+u64
+lrw_get_appeui(struct lrw_hw *hw)
+{
+ struct lrw_struct *lrw_st;
+
+ lrw_st = container_of(hw, struct lrw_struct, hw);
+ return lrw_st->app_eui;
+}
+EXPORT_SYMBOL(lrw_get_appeui);
+
+/**
+ * lrw_set_devaddr - Set the LoRa device's address
+ * @hw: the LoRa device going to be set
+ * @devaddr: the device address
+ */
+void
+lrw_set_devaddr(struct lrw_hw *hw, u32 devaddr)
+{
+ struct lrw_struct *lrw_st;
+
+ lrw_st = container_of(hw, struct lrw_struct, hw);
+ lrw_st->devaddr = devaddr;
+}
+EXPORT_SYMBOL(lrw_set_devaddr);
+
+/**
+ * lrw_get_devaddr - Get the LoRa device's address
+ * @hw: the LoRa device going to be got from
+ *
+ * Return: the device address
+ */
+u32
+lrw_get_devaddr(struct lrw_hw *hw)
+{
+ struct lrw_struct *lrw_st;
+
+ lrw_st = container_of(hw, struct lrw_struct, hw);
+ return lrw_st->devaddr;
+}
+EXPORT_SYMBOL(lrw_get_devaddr);
+
+/**
+ * lrw_add_hw - Add a LoRaWAN hardware as a network device
+ * @lrw_st: the LoRa device going to be added
+ *
+ * Return: 0 / other number for success / failed
+ */
+static int
+lrw_add_hw(struct lrw_struct *lrw_st)
+{
+ struct net_device *ndev = lrw_st->ndev;
+ __be32 be_addr;
+ int ret;
+
+ lrw_st->fcnt_up = 0;
+ lrw_st->fcnt_down = 0;
+ lrw_st->_cur_ss = NULL;
+
+ mutex_init(&lrw_st->ss_list_lock);
+ INIT_LIST_HEAD(&lrw_st->ss_list);
+
+ tasklet_init(&lrw_st->xmit_task, lrw_xmit, (unsigned long) lrw_st);
+ INIT_WORK(&lrw_st->rx_work, lrw_rx_work);
+
+ be_addr = cpu_to_be32(lrw_st->devaddr);
+ memcpy(ndev->perm_addr, &be_addr, ndev->addr_len);
+ memcpy(ndev->dev_addr, ndev->perm_addr, ndev->addr_len);
+
+ write_pnet(&lrw_st->_net, &init_net);
+ ret = register_netdev(ndev);
+
+ return ret;
+}
+
+/**
+ * lrw_remove_hw - Remove a LoRaWAN hardware from a network device
+ * @lrw_st: the LoRa device going to be removed
+ */
+static void
+lrw_remove_hw(struct lrw_struct *lrw_st)
+{
+ unregister_netdev(lrw_st->ndev);
+ tasklet_kill(&lrw_st->xmit_task);
+}
+
+bool
+ready2write(struct lrw_struct *lrw_st)
+{
+ bool status = false;
+
+ if ((!lrw_st->_cur_ss) && (lrw_st->state == LRW_STATE_IDLE))
+ status = true;
+
+ return status;
+}
+
+bool
+ready2read(struct lrw_struct *lrw_st)
+{
+ struct lrw_session *ss;
+ bool status = false;
+
+ if (!list_empty(&lrw_st->ss_list) && (lrw_st->state != LRW_STOP)) {
+ ss = list_first_entry(&lrw_st->ss_list,
+ struct lrw_session,
+ entry);
+ if (ss->state == LRW_RXRECEIVED_SS)
+ status = true;
+ }
+
+ return status;
+}
+
+static int
+lrw_if_up(struct net_device *ndev)
+{
+ struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+ int ret = -EBUSY;
+
+ if (lrw_st->state == LRW_STOP) {
+ ret = lrw_start_hw(lrw_st);
+ netif_start_queue(ndev);
+ }
+
+ return ret;
+}
+
+static int
+lrw_if_down(struct net_device *ndev)
+{
+ struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+
+ if (lrw_st->state != LRW_STOP) {
+ netif_stop_queue(ndev);
+ lrw_stop_hw(lrw_st);
+ }
+
+ return 0;
+}
+
+static netdev_tx_t
+lrw_if_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+ struct lrw_session *ss;
+ netdev_tx_t ret;
+
+ ret = NETDEV_TX_OK;
+
+ ss = lrw_alloc_ss(lrw_st);
+ if (!ss)
+ return NETDEV_TX_BUSY;
+
+ mutex_lock(&lrw_st->ss_list_lock);
+ if (ready2write(lrw_st)) {
+ list_add_tail(&ss->entry, &lrw_st->ss_list);
+ lrw_st->state = LRW_STATE_TX;
+ lrw_st->_cur_ss = ss;
+ ss->fcnt_up = lrw_st->fcnt_up;
+ ss->fcnt_down = lrw_st->fcnt_down;
+ /* TODO: RX delay #1/#2 should be set by regional parameters */
+ ss->rx_delay1 = 1;
+ ss->rx_delay2 = 2;
+ ss->rx1_window = 500;
+ ss->rx2_window = 500;
+ }
+ else
+ ret = NETDEV_TX_BUSY;
+ mutex_unlock(&lrw_st->ss_list_lock);
+
+ if (ret == NETDEV_TX_OK) {
+ ss->state = LRW_INIT_SS;
+ ss->tx_skb = skb;
+ lrw_prepare_tx_frame(ss);
+ tasklet_schedule(&lrw_st->xmit_task);
+ }
+ else
+ lrw_free_ss(ss);
+
+ return ret;
+}
+
+static int
+lrw_if_get_addr(struct lrw_struct *lrw_st, struct sockaddr_lorawan *addr)
+{
+ int ret = 0;
+
+ switch (addr->addr_in.addr_type) {
+ case LRW_ADDR_DEVADDR:
+ addr->addr_in.devaddr = lrw_st->devaddr;
+ break;
+ case LRW_ADDR_DEVEUI:
+ addr->addr_in.dev_eui = lrw_st->dev_eui;
+ break;
+ case LRW_ADDR_APPEUI:
+ addr->addr_in.app_eui = lrw_st->app_eui;
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+
+static int
+lrw_if_set_addr(struct lrw_struct *lrw_st, struct sockaddr_lorawan *addr)
+{
+ struct lrw_hw *hw = &lrw_st->hw;
+ int ret = 0;
+
+ if (netif_running(lrw_st->ndev))
+ return -EBUSY;
+
+ switch (addr->addr_in.addr_type) {
+ case LRW_ADDR_DEVADDR:
+ lrw_set_devaddr(hw, addr->addr_in.devaddr);
+ break;
+ case LRW_ADDR_DEVEUI:
+ lrw_set_deveui(hw, addr->addr_in.dev_eui);
+ break;
+ case LRW_ADDR_APPEUI:
+ lrw_set_appeui(hw, addr->addr_in.app_eui);
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+
+static void
+swap_bytes(u8 *dst, u8 *src, size_t l)
+{
+ /* Human reading is big-endian, but LoRaWAN is little-endian */
+ unsigned int i;
+ for (i = 0; i < l; i++)
+ dst[i] = src[l - i - 1];
+}
+
+int
+lrw_set_key(struct lrw_hw *hw, u8 type, u8 *key, size_t key_len)
+{
+ struct lrw_struct *lrw_st;
+ int ret = 0;
+
+ lrw_st = container_of(hw, struct lrw_struct, hw);
+
+ netdev_dbg(lrw_st->ndev, "%s: type=%d\n", __func__, type);
+ if (lrw_st->state != LRW_STOP)
+ return -EINVAL;
+
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, key, key_len, true);
+ switch (type) {
+ case LRW_APPKEY:
+ swap_bytes(lrw_st->appkey, key, key_len);
+ break;
+ case LRW_NWKSKEY:
+ swap_bytes(lrw_st->nwkskey, key, key_len);
+ break;
+ case LRW_APPSKEY:
+ swap_bytes(lrw_st->appskey, key, key_len);
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(lrw_set_key);
+
+int
+lrw_get_key(struct lrw_hw *hw, u8 type, u8 *key, size_t key_len)
+{
+ struct lrw_struct *lrw_st;
+ int ret = 0;
+
+ lrw_st = container_of(hw, struct lrw_struct, hw);
+
+ netdev_dbg(lrw_st->ndev, "%s: type=%d\n", __func__, type);
+ switch (type) {
+ case LRW_APPKEY:
+ swap_bytes(key, lrw_st->appkey, key_len);
+ break;
+ case LRW_NWKSKEY:
+ swap_bytes(key, lrw_st->nwkskey, key_len);
+ break;
+ case LRW_APPSKEY:
+ swap_bytes(key, lrw_st->appskey, key_len);
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+
+static int
+lrw_if_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+ struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+ struct sockaddr_lorawan *addr;
+ int ret = 0;
+
+ netdev_dbg(ndev, "%s: ioctl file (cmd=0x%X)\n", __func__, cmd);
+
+ /* I/O control by each command */
+ switch (cmd) {
+ /* Set & get the DevAddr, DevEUI and AppEUI */
+ case SIOCSIFADDR:
+ addr = (struct sockaddr_lorawan *)&ifr->ifr_addr;
+ ret = lrw_if_set_addr(lrw_st, addr);
+ break;
+ case SIOCGIFADDR:
+ addr = (struct sockaddr_lorawan *)&ifr->ifr_addr;
+ ret = lrw_if_get_addr(lrw_st, addr);
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+
+static int
+lrw_if_set_mac(struct net_device *ndev, void *p)
+{
+ struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+ struct sockaddr *addr = p;
+ __be32 *be_addr;
+
+ be_addr = (__be32 *)addr->sa_data;
+
+ netdev_dbg(ndev, "%s: AF_TYPE:%d set mac address %X\n",
+ __func__, addr->sa_family, be32_to_cpu(*be_addr));
+
+ if (netif_running(ndev))
+ return -EBUSY;
+
+ lrw_set_devaddr(&lrw_st->hw, be32_to_cpu(*be_addr));
+ memcpy(ndev->dev_addr, be_addr, ndev->addr_len);
+
+ return 0;
+}
+
+static const struct net_device_ops lrw_if_ops = {
+ .ndo_open = lrw_if_up,
+ .ndo_stop = lrw_if_down,
+ .ndo_start_xmit = lrw_if_start_xmit,
+ .ndo_do_ioctl = lrw_if_ioctl,
+ .ndo_set_mac_address = lrw_if_set_mac,
+};
+
+/**
+ * lrw_register_hw - Register as a LoRaWAN compatible device
+ * @hw: LoRa device going to be registered
+ *
+ * Return: 0 / negative number for success / error number
+ */
+int
+lrw_register_hw(struct lrw_hw *hw)
+{
+ struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+ int ret;
+
+ device_initialize(&lrw_st->dev);
+ dev_set_name(&lrw_st->dev, netdev_name(lrw_st->ndev));
+ lrw_st->dev.class = lrw_sys_class;
+ lrw_st->dev.platform_data = lrw_st;
+
+ ret = device_add(&lrw_st->dev);
+ if (ret)
+ goto lrw_register_hw_end;
+
+ /* Add a LoRa device node as a network device */
+ lrw_st->ndev->netdev_ops = &lrw_if_ops;
+ ret = lrw_add_hw(lrw_st);
+ if (!ret)
+ netdev_info(lrw_st->ndev, "register\n");
+
+lrw_register_hw_end:
+ return ret;
+}
+EXPORT_SYMBOL(lrw_register_hw);
+
+/**
+ * lrw_unregister_hw - Unregister the LoRaWAN compatible device
+ * @hw: LoRa device going to be unregistered
+ */
+void
+lrw_unregister_hw(struct lrw_hw *hw)
+{
+ struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+
+ netdev_info(lrw_st->ndev, "unregister\n");
+
+ /* Stop and remove the LoRaWAM hardware from system */
+ if (lrw_st->state != LRW_STOP)
+ lrw_stop_hw(lrw_st);
+ device_del(&lrw_st->dev);
+ lrw_remove_hw(lrw_st);
+
+ return;
+}
+EXPORT_SYMBOL(lrw_unregister_hw);
+
+static int __init
+lrw_init(void)
+{
+ int err = 0;
+
+ pr_info("%s: module inserted\n", LORAWAN_MODULE_NAME);
+
+ /* Create device class */
+ lrw_sys_class = class_create(THIS_MODULE, LORAWAN_MODULE_NAME);
+ if (IS_ERR(lrw_sys_class)) {
+ pr_err("%s: Failed to create a class of LoRaWAN\n",
+ LORAWAN_MODULE_NAME);
+ err = PTR_ERR(lrw_sys_class);
+ goto lrw_init_end;
+ }
+
+ pr_debug("%s: class created\n", LORAWAN_MODULE_NAME);
+
+lrw_init_end:
+ return err;
+}
+
+static void __exit
+lrw_exit(void)
+{
+ /* Delete device class */
+ class_destroy(lrw_sys_class);
+ pr_info("%s: module removed\n", LORAWAN_MODULE_NAME);
+}
+
+module_init(lrw_init);
+module_exit(lrw_exit);
+
+MODULE_AUTHOR("Jian-Hong Pan, <starnight@g.ncu.edu.tw>");
+MODULE_DESCRIPTION("LoRaWAN soft MAC kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
--
2.19.1
^ permalink raw reply related
* Re: [RFC v1 1/3] udp_tunnel: add config option to bind to a device
From: Nicolas Dichtel @ 2018-11-14 16:07 UTC (permalink / raw)
To: Alexis Bauvin, dsa, roopa; +Cc: netdev, akherbouche
In-Reply-To: <20181114093104.93286-2-abauvin@scaleway.com>
Le 14/11/2018 à 10:31, Alexis Bauvin a écrit :
> UDP tunnel sockets are always opened unbound to a specific device. This
> patch allow the socket to be bound on a custom device, which
> incidentally makes UDP tunnels VRF-aware if binding to an l3mdev.
>
> Signed-off-by: Alexis Bauvin <abauvin@scaleway.com>
> Reviewed-by: Amine Kherbouche <akherbouche@scaleway.com>
> Tested-by: Amine Kherbouche <akherbouche@scaleway.com>
What is the difference with the previous version?
Maybe a cover letter would help to track the history.
Regards,
Nicolas
^ permalink raw reply
* [PATCH net-next] isdn/hisax: remove set but not used variable 'total'
From: YueHaibing @ 2018-11-15 2:23 UTC (permalink / raw)
To: davem, isdn; +Cc: linux-kernel, netdev, natechancellor, YueHaibing
Fixes gcc '-Wunused-but-set-variable' warning:
drivers/isdn/hisax/hfc_pci.c:277:6: warning:
variable ‘total’ set but not used [-Wunused-but-set-variable]
It never used since git history start.
Signed-off-by: YueHaibing <yuehaibing@huawei.com>
---
drivers/isdn/hisax/hfc_pci.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c
index ea0e4c6..5b719b5 100644
--- a/drivers/isdn/hisax/hfc_pci.c
+++ b/drivers/isdn/hisax/hfc_pci.c
@@ -274,7 +274,7 @@ hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type *bz, u_char *bdata, int count
u_char *ptr, *ptr1, new_f2;
struct sk_buff *skb;
struct IsdnCardState *cs = bcs->cs;
- int total, maxlen, new_z2;
+ int maxlen, new_z2;
z_type *zp;
if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
@@ -297,7 +297,6 @@ hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type *bz, u_char *bdata, int count
} else if (!(skb = dev_alloc_skb(count - 3)))
printk(KERN_WARNING "HFCPCI: receive out of memory\n");
else {
- total = count;
count -= 3;
ptr = skb_put(skb, count);
--
2.7.0
^ permalink raw reply related
* Re: [RFC PATCH 07/12] soc: qcom: ipa: IPA register abstraction
From: Alex Elder @ 2018-11-15 2:48 UTC (permalink / raw)
To: Arnd Bergmann
Cc: David Miller, Bjorn Andersson, Ilias Apalodimas, Networking, DTML,
linux-arm-msm, linux-soc, Linux ARM, Linux Kernel Mailing List,
syadagir, mjavid, Rob Herring, Mark Rutland
In-Reply-To: <CAK8P3a2FsgR=SYAWsgFURVni1ReiCugKs1iUAfQzZhPkbW8aOA@mail.gmail.com>
On 11/7/18 9:00 AM, Arnd Bergmann wrote:
> On Wed, Nov 7, 2018 at 1:33 AM Alex Elder <elder@linaro.org> wrote:
>> diff --git a/drivers/net/ipa/ipa_reg.c b/drivers/net/ipa/ipa_reg.c
>> new file mode 100644
>> index 000000000000..5e0aa6163235
>> --- /dev/null
>> +++ b/drivers/net/ipa/ipa_reg.c
>> @@ -0,0 +1,972 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
>> + * Copyright (C) 2018 Linaro Ltd.
>> + */
>> +
>> +#include <linux/types.h>
>> +#include <linux/io.h>
>> +#include <linux/bitfield.h>
>> +
>> +#include "ipa_reg.h"
>> +
>> +/* I/O remapped base address of IPA register space */
>> +static void __iomem *ipa_reg_virt;
>
> This should of course be part of the device structure.
Yes, this should have been be in that structure to begin with.
I'm working through doing a comprehensive replacement of
global variables like this with values passed around as
arguments. I've intended to do it all along but your nudge
pushed it to the top of my list. It's a *lot* of work, much
more than I realized. But I'm making rapid progress.
>> +/* struct ipa_reg_desc - descriptor for an abstracted hardware register
>> + *
>> + * @construct - fn to construct the register value from its field structure
>> + * @parse - function to parse register field values into its field structure
>> + * @offset - register offset relative to base address
>> + * @n_ofst - size multiplier for "N-parameterized" registers
>> + */
>> +struct ipa_reg_desc {
>> + u32 (*construct)(enum ipa_reg reg, const void *fields);
>> + void (*parse)(enum ipa_reg reg, void *fields, u32 val);
>> + u32 offset;
>> + u16 n_ofst;
>> +};
>
> Indirect function pointers can be a bit expensive in the post-spectre
> days. It's probably not overly important if these are always part of
> an MMIO access function, but you should be careful about using
> these in the data path.
OK.
There used to be a more elaborate scheme for supporting
lots of versions, and I have tried to preserve at least part
of the underlying mechanism (the parse and construct functions).
Not all of these registers use the indirect functions, and
it looks to me like none of them are in the data path.
The most important registers for the fast path are found in
the GSI code. And that doesn't use this construct--it only
reads and writes 32-bit registers. (I think it differs
because it was originally developed by a different team.)
> How many different versions do we have to support in practice
I don't know for sure how many versions really were used,
but the original code had about 10 distinct version values,
many of which shared most (but not all) register definitions
(offset and bit field widths) with older versions.
With the upstream code the decision was made to ignore anything
older than IPA version 3 (and 3.5.1 in particular, which is
found in the SDM845 SoC).
It may be that this parse/construct mechanism isn't justified
at this point. I thought the way it presented a generic
interface was useful, but with just one (initial) hardware
target we don't (yet) realize its potential benefit. It could
be added back later, as support for new versions is added.
As of now I don't plan to change this, but if you or someone
else feels it would be better without it I can do that.
-Alex
> Arnd
>
^ permalink raw reply
* Re: [PATCH net-next 17/17] net: sched: unlock rules update API
From: Vlad Buslov @ 2018-11-14 16:45 UTC (permalink / raw)
To: Jiri Pirko
Cc: David Miller, netdev@vger.kernel.org, jhs@mojatatu.com,
xiyou.wangcong@gmail.com, ast@kernel.org, daniel@iogearbox.net
In-Reply-To: <20181114064453.GA2235@nanopsycho.orion>
On Wed 14 Nov 2018 at 06:44, Jiri Pirko <jiri@resnulli.us> wrote:
> Tue, Nov 13, 2018 at 02:46:54PM CET, vladbu@mellanox.com wrote:
>>On Mon 12 Nov 2018 at 17:30, David Miller <davem@davemloft.net> wrote:
>>> From: Vlad Buslov <vladbu@mellanox.com>
>>> Date: Mon, 12 Nov 2018 09:55:46 +0200
>>>
>>>> Register netlink protocol handlers for message types RTM_NEWTFILTER,
>>>> RTM_DELTFILTER, RTM_GETTFILTER as unlocked. Set rtnl_held variable that
>>>> tracks rtnl mutex state to be false by default.
>>>
>>> This whole conditional locking mechanism is really not clean and makes
>>> this code so much harder to understand and audit.
>>>
>>> Please improve the code so that this kind of construct is not needed.
>>>
>>> Thank you.
>>
>>Hi David,
>>
>>I considered several approaches to this problem and decided that this
>>one is most straightforward to implement. I understand your concern and
>>agree that this code is not easiest to understand and can suggest
>>several possible solutions that do not require this kind of elaborate
>>locking mechanism in cls API, but have their own drawbacks:
>>
>>1. Convert all qdiscs and classifiers to support unlocked execution,
>>like we did for actions. However, according to my experience with
>>converting flower classifier, these require much more code than actions.
>>I would estimate it to be more work than whole current unlocking effort
>>(hundred+ patches). Also, authors of some of them might be unhappy with
>>such intrusive changes. I don't think this approach is realistic.
>>
>>2. Somehow determine if rtnl is needed at the beginning of cls API rule
>>update functions. Currently, this is not possible because locking
>>requirements are determined by qdisc_class_ops and tcf_proto_ops 'flags'
>>field, which requires code to first do whole ops lookup sequence.
>>However, instead of class field I can put 'flags' in some kind of hash
>>table or array that will map qdisc/classifier type string to flags, so
>>it will be possible to determine locking requirements by just parsing
>>netlink message and obtaining flags by qdisc/classifier type. I do not
>>consider it pretty solution either, but maybe you have different
>>opinion.
>
> I think you will have to do 2. or some modification. Can't you just
> check for cls ability to run unlocked early on in tc_new_tfilter()?
> You would call tcf_proto_locking_check(nla_data(tca[TCA_KIND]), ...),
> which would do tcf_proto_lookup_ops() for ops and check the flags?
I guess that would work. However, such solution requires calling
tcf_proto_lookup_ops(), which iterates over tcf_proto_base list and
calls strcmp() for each proto, for every rule update call. That is why I
suggested to use some kind of optimized data structure for that purpose
in my first reply. Dunno if such solution will significantly impact rule
update performance. We don't have that many classifiers and their names
are short, so I guess not?
>
>
>>
>>3. Anything you can suggest? I might be missing something simple that
>>you would consider more elegant solution to this problem.
>>
>>Thanks,
>>Vlad
>>
^ permalink raw reply
* Re: [PATCH net-next v2 0/6] net: aquantia: add rx-flow filter support
From: David Miller @ 2018-11-14 16:50 UTC (permalink / raw)
To: Igor.Russkikh; +Cc: netdev
In-Reply-To: <cover.1542037157.git.igor.russkikh@aquantia.com>
From: Igor Russkikh <Igor.Russkikh@aquantia.com>
Date: Mon, 12 Nov 2018 15:45:56 +0000
> In this patchset the rx-flow filters functionality and vlan filter offloads
> are implemented.
>
> The rules in NIC hardware have fixed order and priorities.
> To support this, the locations of filters from ethtool perspective are also fixed:
>
> * Locations 0 - 15 for VLAN ID filters
> * Locations 16 - 31 for L2 EtherType and PCP filters
> * Locations 32 - 39 for L3/L4 5-tuple filters (locations 32, 36 for IPv6)
Series applied, thanks.
^ permalink raw reply
* Re: [PATCH net-next 00/13] nfp: abm: track all Qdiscs
From: David Miller @ 2018-11-14 16:53 UTC (permalink / raw)
To: jakub.kicinski; +Cc: oss-drivers, netdev, jiri, xiyou.wangcong, jhs
In-Reply-To: <20181112225819.29823-1-jakub.kicinski@netronome.com>
From: Jakub Kicinski <jakub.kicinski@netronome.com>
Date: Mon, 12 Nov 2018 14:58:06 -0800
> Our Qdisc offload so far has been very simplistic. We held
> and array of marking thresholds and statistics sized to the
> number of PF queues. This was sufficient since the only
> configuration we supported was single layer of RED Qdiscs
> (on top of MQ or not, but MQ isn't really about queuing).
>
> As we move to add more Qdiscs it's time to actually try to
> track the full Qdisc hierarchy. This allows us to make sure
> our offloaded configuration reflects the SW path better.
> We add graft notifications to MQ and RED (PRIO already sends
> them) to allow drivers offloading those to learn how Qdiscs
> are linked. MQ graft gives us the obvious advantage of being
> able to track when Qdiscs are shared or moved. It seems
> unlikely HW would offload RED's child Qdiscs but since the
> behaviour would change based on linked child we should
> stop offloading REDs with modified child. RED will also
> handle the child differently during reconfig when limit
> parameter is set - so we have to inform the drivers about
> the limit, and have them reset the child state when
> appropriate.
>
> The NFP driver will now allocate a structure to track each
> Qdisc and link it to its children. We will also maintain
> a shadow copy of threshold settings - to save device writes
> and make it easier to apply defaults when config is
> re-evaluated.
Series applied, thanks.
^ permalink raw reply
* Re: [RFC PATCH 09/12] soc: qcom: ipa: main IPA source file
From: Alex Elder @ 2018-11-15 3:11 UTC (permalink / raw)
To: Arnd Bergmann
Cc: David Miller, Bjorn Andersson, Ilias Apalodimas, Networking, DTML,
linux-arm-msm, linux-soc, Linux ARM, Linux Kernel Mailing List,
syadagir, mjavid, Rob Herring, Mark Rutland
In-Reply-To: <CAK8P3a0RH4wbigKR0zsbxPzqWDjrLEkYfQk4GNxhGuxs=HJHHw@mail.gmail.com>
On 11/7/18 8:08 AM, Arnd Bergmann wrote:
> On Wed, Nov 7, 2018 at 1:33 AM Alex Elder <elder@linaro.org> wrote:
>
>> +static void ipa_client_remove_deferred(struct work_struct *work);
>
> Try to avoid forward declarations by reordering the code in call order,
> it will also make it easier to read.
>
>> +static DECLARE_WORK(ipa_client_remove_work, ipa_client_remove_deferred);
Done. I've actually reworked this a lot, and pulled all the
clock (and interconnect) related code into a separate source file.
No more forward declarations (there anyway), and the work structure
is now embedded in the top-level IPA structure so I can derive
it again in the work function (rather than using the global).
>> +static struct ipa_context ipa_ctx_struct;
>> +struct ipa_context *ipa_ctx = &ipa_ctx_struct;
>
> Global state variables should generally be removed as well, and
> passed around as function arguments.
Working on this.
>> +static int hdr_init_local_cmd(u32 offset, u32 size)
>> +{
>> + struct ipa_desc desc = { };
>> + struct ipa_dma_mem mem;
>> + void *payload;
>> + int ret;
>> +
>> + if (ipa_dma_alloc(&mem, size, GFP_KERNEL))
>> + return -ENOMEM;
>> +
>> + offset += ipa_ctx->smem_offset;
>> +
>> + payload = ipahal_hdr_init_local_pyld(&mem, offset);
>> + if (!payload) {
>> + ret = -ENOMEM;
>> + goto err_dma_free;
>> + }
>> +
>> + desc.type = IPA_IMM_CMD_DESC;
>> + desc.len_opcode = IPA_IMM_CMD_HDR_INIT_LOCAL;
>> + desc.payload = payload;
>> +
>> + ret = ipa_send_cmd(&desc);
>
> You have a bunch of dynamic allocations in here, which you
> then immediately tear down again after the command is complete.
> I can't see at all what you do with the DMA address, since you
> seem to not use the virtual address at all but only store
> the physical address in some kind of descriptor without ever
> writing to it.
I should probably have added at least a comment here. The
DMA memory was zeroed at the time of allocation. That zero
buffer is then referred to in the payload to the HDR_INIT_LOCAL
immediate command. So that command, when executing in the IPA
hardware, uses the contents of the buffer whose physical address
it's supplied, which in this case is full of zeroes. We don't
use the virtual address because the buffer came pre-zeroed.
Based on your comment elsewhere I will be putting the command
payload in a structure on the stack rather than allocating it
dynamically.
> Am I missing something here?
>
>> +/* Remoteproc callbacks for SSR events: prepare, start, stop, unprepare */
>> +int ipa_ssr_prepare(struct rproc_subdev *subdev)
>> +{
>> + printk("======== SSR prepare received ========\n");
>
> I think you mean dev_dbg() here. A plain printk() without a level
> is not correct and we probably don't want those messages to arrive
> on the console for normal users.
Yes, this was obviously a debug message in some code I should
have removed before sending...
>> +static int ipa_firmware_load(struct de
>> +
>> +err_clear_dev:
>> + ipa_ctx->lan_cons_ep_id = 0;
>> + ipa_ctx->cmd_prod_ep_id = 0;
>> + ipahal_exit();
>> +err_dma_exit:
>> + ipa_dma_exit();
>> +err_clear_gsi:
>> + ipa_ctx->gsi = NULL;
>> + ipa_ctx->ipa_phys = 0;
>> + ipa_reg_exit();
>> +err_clear_ipa_irq:
>> + ipa_ctx->ipa_irq = 0;
>> +err_clear_filter_bitmap:
>> + ipa_ctx->filter_bitmap = 0;
>> +err_interconnect_exit:
>> + ipa_interconnect_exit();
>> +err_clock_exit:
>> + ipa_clock_exit();
>> + ipa_ctx->dev = NULL;
>> +out_smp2p_exit:
>> + ipa_smp2p_exit(dev);
>> +
>
> No need to initialize members to zero when you are about
> to free the structure.
The IPA context is in fact a global, static structure at
the moment. All of this bookkeeping (zeroing out things)
is a habitual practice, basically. Regardless your point
is good and I'll remove these kinds of things as part of
converting to not using globals.
>> +static struct platform_driver ipa_plat_drv = {
>> + .probe = ipa_plat_drv_probe,
>> + .remove = ipa_plat_drv_remove,
>> + .driver = {
>> + .name = "ipa",
>> + .owner = THIS_MODULE,
>> + .pm = &ipa_pm_ops,
>> + .of_match_table = ipa_plat_drv_match,
>> + },
>> +};
>> +
>> +builtin_platform_driver(ipa_plat_drv);
>
> This should be module_platform_driver(), and allow unloading
> the driver.
Yes. I've done this for my own use, but the code is not
currently able to shut down cleanly. I've been fixing a
*lot* of the things that don't clean up after themselves
today but there's more work before I can say this can be
safely built as a module. But it's a requirement.
-Alex
>
> Arnd
>
^ permalink raw reply
* Re: [PATCH net] net: bridge: fix per-port vlan stats use-after-free on destruction
From: Nikolay Aleksandrov @ 2018-11-14 17:08 UTC (permalink / raw)
To: netdev; +Cc: roopa, davem, bridge, syzkaller-bugs
In-Reply-To: <20181113010154.13935-1-nikolay@cumulusnetworks.com>
On 11/13/18 3:01 AM, Nikolay Aleksandrov wrote:
> Syzbot reported a use-after-free of the global vlan context on port vlan
> destruction. When I added per-port vlan stats I missed the fact that the
> global vlan context can be freed before the per-port vlan rcu callback.
> There're a few different ways to deal with this, I've chosen to add a
> new private flag that is set only when per-port stats are allocated so
> we can directly check it on destruction without dereferencing the global
> context at all. The flag is internally controlled by the kernel and
> user-space isn't allowed to set it.
>
> Fixes: 9163a0fc1f0c ("net: bridge: add support for per-port vlan stats")
> Reported-by: syzbot+04681da557a0e49a52e5@syzkaller.appspotmail.com
> Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
> ---
I'll post v2 with a cosmetic change - move the check up one level where
it's more logical to be and the rest of the checks are done.
^ permalink raw reply
* Re: [RFC PATCH 10/12] soc: qcom: ipa: data path
From: Alex Elder @ 2018-11-15 3:31 UTC (permalink / raw)
To: Arnd Bergmann
Cc: David Miller, Bjorn Andersson, Ilias Apalodimas, Networking, DTML,
linux-arm-msm, linux-soc, Linux ARM, Linux Kernel Mailing List,
syadagir, mjavid, Rob Herring, Mark Rutland
In-Reply-To: <CAK8P3a0nkfwFCCVHaTJ+kJGWxO+qFTzTLnRgB-NG0AyMEsv3bA@mail.gmail.com>
On 11/7/18 8:55 AM, Arnd Bergmann wrote:
> On Wed, Nov 7, 2018 at 1:33 AM Alex Elder <elder@linaro.org> wrote:
>>
>> This patch contains "ipa_dp.c", which includes the bulk of the data
>> path code. There is an overview in the code of how things operate,
>> but there are already plans to rework this portion of the driver.
>>
>> In particular:
>> - Interrupt handling will be replaced with a threaded interrupt
>> handler. Currently handling occurs in a combination of
>> interrupt and workqueue context, and this requires locking
>> and atomic operations for proper synchronization.
>
> You probably don't want to use just a threaded IRQ handler to
> start the poll function, that would still require an extra indirection.
That's a really good point. However I think that the path I'll
take to *getting* to scheduling the poll in interrupt context
will use a threaded interrupt handler. I'm hoping that will
allow me to simplify the code in steps.
The main reason for this split between working in interrupt
context when possible, but pushing to a workqueue when not, is
to allow IPA clock(s) to be turned off. Enabling the clocks
is a blocking operation, so can't' be done in the top half
interrupt handler. The thought was it would be best to work
in interrupt context--if the clock was already active--but
to defer to a workqueue to turn the clock on if necessary.
The result requires locking and duplication of code that I
find to be pretty confusing--and hard to reason about. I
have been planning to re-do things to be better suited to
NAPI, and knowing that, I haven't given the data path as
much attention as some of the rest.
> However, you can probably use the top half of the threaded
> handler to request the poll function if necessary but use
> the bottom half for anything that does not go through poll.
>
>> - Currently, only receive endpoints use NAPI. Transmit
>> completion interrupts are disabled, and are handled in batches
>> by periodically scheduling an interrupting no-op request.
>> The plan is to arrange for transmit requests to generate
>> interrupts, and their completion will be processed with other
>> completions in the NAPI poll function. This will also allow
>> accurate feedback about packet sojourn time to be provided to
>> queue limiting mechanisms.
>
> Right, that is definitely required here. I also had a look at
> the gsi_channel_queue() function, which sits in the middle of
> the transmit function and is rather unoptimized. I'd suggest moving
> that into the caller so we can see what is going on, and then
> optimizing it from there.
Yes, I agree with that. There are multiple levels of abstraction
in play and they aren't helpful. We have ipa_desc structures that
are translated by ipa_send() into gsi_xfer_elem structures, which
are ultimately recorded by gsi_channel_queue() as 16 byte gsi_tre
structures. At least one of those translations can go away.
>> - Not all receive endpoints use NAPI. The plan is for *all*
>> endpoints to use NAPI. And because all endpoints share a
>> common GSI interrupt, a single NAPI structure will used to
>> managing the processing for all completions on all endpoints.
>> - Receive buffers are posted to the hardware by a workqueue
>> function. Instead, the plan is to have this done by the
>> NAPI poll routine.
>
> Makes sense, yes.
Thanks.
-Alex
>
> Arnd
>
^ permalink raw reply
* [PATCH net v2] net: bridge: fix vlan stats use-after-free on destruction
From: Nikolay Aleksandrov @ 2018-11-14 17:27 UTC (permalink / raw)
To: netdev; +Cc: roopa, davem, bridge, syzkaller-bugs, Nikolay Aleksandrov
In-Reply-To: <ae147dd3-1ad8-8b8b-93f1-09297171883a@cumulusnetworks.com>
Syzbot reported a use-after-free of the global vlan context on port vlan
destruction. When I added per-port vlan stats I missed the fact that the
global vlan context can be freed before the per-port vlan rcu callback.
There're a few different ways to deal with this, I've chosen to add a
new private flag that is set only when per-port stats are allocated so
we can directly check it on destruction without dereferencing the global
context at all. The flag is internally controlled by the kernel and
user-space isn't allowed to set it.
Fixes: 9163a0fc1f0c ("net: bridge: add support for per-port vlan stats")
Reported-by: syzbot+04681da557a0e49a52e5@syzkaller.appspotmail.com
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
---
v2: cosmetic change, move the check to br_process_vlan_info where the
other checks are done
include/uapi/linux/if_bridge.h | 3 +++
net/bridge/br_netlink.c | 4 ++++
net/bridge/br_vlan.c | 3 ++-
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index e41eda3c71f1..fa1f72276712 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -130,6 +130,9 @@ enum {
#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */
#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */
#define BRIDGE_VLAN_INFO_BRENTRY (1<<5) /* Global bridge VLAN entry */
+#define BRIDGE_VLAN_INFO_PORT_STATS (1<<6) /* Per-port VLAN stats */
+
+#define BRIDGE_VLAN_INFO_PRIVATE_FLAGS BRIDGE_VLAN_INFO_PORT_STATS
struct bridge_vlan_info {
__u16 flags;
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 3345f1984542..a017ed566b67 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -573,6 +573,10 @@ static int br_process_vlan_info(struct net_bridge *br,
if (!vinfo_curr->vid || vinfo_curr->vid >= VLAN_VID_MASK)
return -EINVAL;
+ /* don't allow user-space control over private flags */
+ if (vinfo_curr->flags & BRIDGE_VLAN_INFO_PRIVATE_FLAGS)
+ return -EINVAL;
+
if (vinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
/* check if we are already processing a range */
if (*vinfo_last)
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 8c9297a01947..004e1f8c5040 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -197,7 +197,7 @@ static void nbp_vlan_rcu_free(struct rcu_head *rcu)
v = container_of(rcu, struct net_bridge_vlan, rcu);
WARN_ON(br_vlan_is_master(v));
/* if we had per-port stats configured then free them here */
- if (v->brvlan->stats != v->stats)
+ if (v->flags & BRIDGE_VLAN_INFO_PORT_STATS)
free_percpu(v->stats);
v->stats = NULL;
kfree(v);
@@ -264,6 +264,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
err = -ENOMEM;
goto out_filt;
}
+ v->flags |= BRIDGE_VLAN_INFO_PORT_STATS;
} else {
v->stats = masterv->stats;
}
--
2.17.2
^ permalink raw reply related
* Re: [RFC v1 1/3] udp_tunnel: add config option to bind to a device
From: Alexis Bauvin @ 2018-11-14 17:22 UTC (permalink / raw)
To: nicolas.dichtel, dsa, roopa; +Cc: netdev, akherbouche
In-Reply-To: <f6e43d80-4657-685e-b88b-452331744683@6wind.com>
Le 14 nov. 2018 à 17:07, Nicolas Dichtel <nicolas.dichtel@6wind.com> a écrit :
> Le 14/11/2018 à 10:31, Alexis Bauvin a écrit :
>> UDP tunnel sockets are always opened unbound to a specific device. This
>> patch allow the socket to be bound on a custom device, which
>> incidentally makes UDP tunnels VRF-aware if binding to an l3mdev.
>>
>> Signed-off-by: Alexis Bauvin <abauvin@scaleway.com>
>> Reviewed-by: Amine Kherbouche <akherbouche@scaleway.com>
>> Tested-by: Amine Kherbouche <akherbouche@scaleway.com>
> What is the difference with the previous version?
> Maybe a cover letter would help to track the history.
Unless a mistake from my side, you should have received the cover letter in
the previous email. The previous version had a typo in the commit log of the
third patch of this patch set.
> Regards,
> Nicolas
Regards,
Alexis
^ permalink raw reply
* [RFC] Discuss about an new idea "Vsock over Virtio-net"
From: jiangyiwen @ 2018-11-15 3:56 UTC (permalink / raw)
To: stefanha, stefanha, Jason Wang, mst; +Cc: netdev, kvm, virtualization
Hi Stefan, Michael, Jason and everyone,
Several days ago, I discussed with jason about "Vsock over Virtio-net".
This idea has two advantages:
First, it can use many great features of virtio-net, like batching,
mergeable rx buffer and multiqueue, etc.
Second, it can reduce many duplicate codes and make it easy to be
maintained.
Before the implement, I want to discuss with everyone again, and
want to know everyone's suggestions.
After the discussion, based on this point I will try to implement
this idea, but I am not familiar with the virtio-net, that is a
pity. :(
-------------------------Simple idea------------------------------
1. The packet layout will become as follows:
+---------------------------------+
| Virtio-net header |
|(struct virtio_net_hdr_mrg_rxbuf)|
+---------------------------------+
| Vsock header |
| (struct virtio_vsock_hdr) |
+---------------------------------+
| payload |
| (until end of packet) |
+---------------------------------+
2. The Guest->Host basic code flow as follow:
+------------+
| Client |
+------------+
|
|
+------------------------------------------------------------------+
|VSOCK Core Module |
|ops->sendmsg; (vsock_stream_sendmsg) |
| -> alloc_skb; /* it will packet a skb buffer, and include vsock |
| * hdr and payload */ |
| -> dev_queue_xmit(); /* it will call start_xmit(virtio-net.c) */|
|vsock hdr and payload, and then call |
+------------------------------------------------------------------+
|
|
+------------------------------------------------------------------+
|Virtio-net Module |
|start_xmit |
| -> add virtio_net_hdr and pack sg in ring desc, notify Host |
+------------------------------------------------------------------+
|
|
+------------------------------------------------------------------+
|Vhost-net Module |
|handle_tx |
| -> get tx buffer, skip virtio_net_hdr and call Vsock function. |
| /* This point has some differences, vhost-net use ->sendmsg to |
| * forward information, however vsock only need to notify server |
| * that data ready. */ |
+------------------------------------------------------------------+
|
|
+------------------------------------------------------------------+
|VSOCK Core Module |
|alloc_pkt, copy skb data to pkt. |
|add pkt to rx_queue and notify server to get data. |
+------------------------------------------------------------------+
3. To Host->Guest
I have a problem and difficult, mainly I know about virtio-net a little),
because I have been doing work related with storage and file system.
The problem as follows:
we should monitor all of socket of vsock in handle_rx, when there are
data coming, and copy data to vq desc. Vhost-net use ->recvmsg to
get data, it is different with socket. To vsock, I think host will
not call ->recvmsg when it need to send message to guest. To net,
vhost-net only as forwarding layer.
^ permalink raw reply
* [PATCH v3 net-next 0/4] net: batched receive in GRO path
From: Edward Cree @ 2018-11-14 18:07 UTC (permalink / raw)
To: linux-net-drivers, davem; +Cc: netdev, eric.dumazet
This series listifies part of GRO processing, in a manner which allows those
packets which are not GROed (i.e. for which dev_gro_receive returns
GRO_NORMAL) to be passed on to the listified regular receive path.
dev_gro_receive() itself is not listified, nor the per-protocol GRO
callback, since GRO's need to hold packets on lists under napi->gro_hash
makes keeping the packets on other lists awkward, and since the GRO control
block state of held skbs can refer only to one 'new' skb at a time.
Performance figures with this series, collected on a back-to-back pair of
Solarflare sfn8522-r2 NICs with 120-second NetPerf tests. In the stats,
sample size n for old and new code is 6 runs each; p is from a Welch t-test.
Tests were run both with GRO enabled and disabled, the latter simulating
uncoalesceable packets (e.g. due to IP or TCP options). Payload_size in all
tests was 8000 bytes. BW tests use 4 streams, RR tests use 100.
TCP Stream, GRO on:
net-next: 9.415 Gb/s (line rate); 190% total rxcpu
after #4: 9.415 Gb/s; 192% total rxcpu
p_bw = 0.155; p_cpu = 0.382
TCP Stream, GRO off:
net-next: 5.625 Gb/s
after #4: 6.551 Gb/s
16.5% faster; p < 0.001
TCP RR, GRO on:
net-next: 837.6 us
after #4: 840.0 us
0.3% slower; p = 0.229
TCP RR, GRO off:
net-next: 867.6 us
after #4: 860.1 us
0.9% faster; p = 0.064
UDP Stream (GRO off):
net-next: 7.808 Gb/s
after #4: 7.848 Gb/s
0.5% slower; p = 0.144
Conclusion:
* TCP b/w is 16.5% faster for traffic which cannot be coalesced by GRO.
* TCP latency might be slightly improved in the same case, but it's not
quite statistically significant
* Both see no statistically significant change in performance with GRO
active
* UDP throughput might be slightly slowed (probably by patch #3) but it's
not statistically significant. Note that drivers which (unlike sfc) pass
UDP traffic to GRO will probably see gains here as this gives them access
to bundling.
Change history:
v3: Rebased on latest net-next. Re-ran performance tests and added TCP_RR
tests at suggestion of Eric Dumazet. Expanded changelog of patch #3.
v2: Rebased on latest net-next. Removed RFC tags. Otherwise unchanged
owing to lack of comments on v1.
Edward Cree (4):
net: introduce list entry point for GRO
sfc: use batched receive for GRO
net: make listified RX functions return number of good packets
net/core: handle GRO_NORMAL skbs as a list in napi_gro_receive_list
drivers/net/ethernet/sfc/efx.c | 11 +++-
drivers/net/ethernet/sfc/net_driver.h | 1 +
drivers/net/ethernet/sfc/rx.c | 16 +++++-
include/linux/netdevice.h | 6 +-
include/net/ip.h | 4 +-
include/net/ipv6.h | 4 +-
net/core/dev.c | 104 ++++++++++++++++++++++++++--------
net/ipv4/ip_input.c | 39 ++++++++-----
net/ipv6/ip6_input.c | 37 +++++++-----
9 files changed, 157 insertions(+), 65 deletions(-)
^ permalink raw reply
* [PATCH v3 net-next 1/4] net: introduce list entry point for GRO
From: Edward Cree @ 2018-11-14 18:09 UTC (permalink / raw)
To: linux-net-drivers, davem; +Cc: netdev, eric.dumazet
In-Reply-To: <8e9ea3c4-82e0-a34c-08ea-32a387e4c9e1@solarflare.com>
Also export napi_frags_skb() so that drivers using the napi_gro_frags()
interface can prepare their SKBs properly for submitting on such a list.
Signed-off-by: Edward Cree <ecree@solarflare.com>
---
include/linux/netdevice.h | 2 ++
net/core/dev.c | 28 +++++++++++++++++++++++++++-
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 487fa5e0e165..2cef1d0fb2b1 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3589,8 +3589,10 @@ int netif_receive_skb(struct sk_buff *skb);
int netif_receive_skb_core(struct sk_buff *skb);
void netif_receive_skb_list(struct list_head *head);
gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb);
+int napi_gro_receive_list(struct napi_struct *napi, struct list_head *head);
void napi_gro_flush(struct napi_struct *napi, bool flush_old);
struct sk_buff *napi_get_frags(struct napi_struct *napi);
+struct sk_buff *napi_frags_skb(struct napi_struct *napi);
gro_result_t napi_gro_frags(struct napi_struct *napi);
struct packet_offload *gro_find_receive_by_type(__be16 type);
struct packet_offload *gro_find_complete_by_type(__be16 type);
diff --git a/net/core/dev.c b/net/core/dev.c
index bf7e0a471186..8f0fb56170b3 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5645,6 +5645,31 @@ gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
}
EXPORT_SYMBOL(napi_gro_receive);
+/* Returns the number of SKBs on the list successfully received */
+int napi_gro_receive_list(struct napi_struct *napi, struct list_head *head)
+{
+ struct sk_buff *skb, *next;
+ gro_result_t result;
+ int kept = 0;
+
+ list_for_each_entry(skb, head, list) {
+ skb_mark_napi_id(skb, napi);
+ trace_napi_gro_receive_entry(skb);
+ skb_gro_reset_offset(skb);
+ }
+
+ list_for_each_entry_safe(skb, next, head, list) {
+ list_del(&skb->list);
+ skb->next = NULL;
+ result = dev_gro_receive(napi, skb);
+ result = napi_skb_finish(result, skb);
+ if (result != GRO_DROP)
+ kept++;
+ }
+ return kept;
+}
+EXPORT_SYMBOL(napi_gro_receive_list);
+
static void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb)
{
if (unlikely(skb->pfmemalloc)) {
@@ -5716,7 +5741,7 @@ static gro_result_t napi_frags_finish(struct napi_struct *napi,
* Drivers could call both napi_gro_frags() and napi_gro_receive()
* We copy ethernet header into skb->data to have a common layout.
*/
-static struct sk_buff *napi_frags_skb(struct napi_struct *napi)
+struct sk_buff *napi_frags_skb(struct napi_struct *napi)
{
struct sk_buff *skb = napi->skb;
const struct ethhdr *eth;
@@ -5752,6 +5777,7 @@ static struct sk_buff *napi_frags_skb(struct napi_struct *napi)
return skb;
}
+EXPORT_SYMBOL(napi_frags_skb);
gro_result_t napi_gro_frags(struct napi_struct *napi)
{
^ permalink raw reply related
* [PATCH v3 net-next 2/4] sfc: use batched receive for GRO
From: Edward Cree @ 2018-11-14 18:09 UTC (permalink / raw)
To: linux-net-drivers, davem; +Cc: netdev, eric.dumazet
In-Reply-To: <8e9ea3c4-82e0-a34c-08ea-32a387e4c9e1@solarflare.com>
Signed-off-by: Edward Cree <ecree@solarflare.com>
---
drivers/net/ethernet/sfc/efx.c | 11 +++++++++--
drivers/net/ethernet/sfc/net_driver.h | 1 +
drivers/net/ethernet/sfc/rx.c | 16 +++++++++++++---
3 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 98fe7e762e17..dbe4a70b36b0 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -263,9 +263,9 @@ static int efx_check_disabled(struct efx_nic *efx)
*/
static int efx_process_channel(struct efx_channel *channel, int budget)
{
+ struct list_head rx_list, gro_list;
struct efx_tx_queue *tx_queue;
- struct list_head rx_list;
- int spent;
+ int spent, gro_count;
if (unlikely(!channel->enabled))
return 0;
@@ -275,6 +275,10 @@ static int efx_process_channel(struct efx_channel *channel, int budget)
INIT_LIST_HEAD(&rx_list);
channel->rx_list = &rx_list;
+ EFX_WARN_ON_PARANOID(channel->gro_list != NULL);
+ INIT_LIST_HEAD(&gro_list);
+ channel->gro_list = &gro_list;
+
efx_for_each_channel_tx_queue(tx_queue, channel) {
tx_queue->pkts_compl = 0;
tx_queue->bytes_compl = 0;
@@ -300,6 +304,9 @@ static int efx_process_channel(struct efx_channel *channel, int budget)
/* Receive any packets we queued up */
netif_receive_skb_list(channel->rx_list);
channel->rx_list = NULL;
+ gro_count = napi_gro_receive_list(&channel->napi_str, channel->gro_list);
+ channel->irq_mod_score += gro_count * 2;
+ channel->gro_list = NULL;
return spent;
}
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
index 961b92979640..72addac7a84a 100644
--- a/drivers/net/ethernet/sfc/net_driver.h
+++ b/drivers/net/ethernet/sfc/net_driver.h
@@ -502,6 +502,7 @@ struct efx_channel {
unsigned int rx_pkt_index;
struct list_head *rx_list;
+ struct list_head *gro_list;
struct efx_rx_queue rx_queue;
struct efx_tx_queue tx_queue[EFX_TXQ_TYPES];
diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c
index 396ff01298cd..0534a54048c6 100644
--- a/drivers/net/ethernet/sfc/rx.c
+++ b/drivers/net/ethernet/sfc/rx.c
@@ -453,9 +453,19 @@ efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf,
skb_record_rx_queue(skb, channel->rx_queue.core_index);
- gro_result = napi_gro_frags(napi);
- if (gro_result != GRO_DROP)
- channel->irq_mod_score += 2;
+ /* Pass the packet up */
+ if (channel->gro_list != NULL) {
+ /* Clear napi->skb and prepare skb for GRO */
+ skb = napi_frags_skb(napi);
+ if (skb)
+ /* Add to list, will pass up later */
+ list_add_tail(&skb->list, channel->gro_list);
+ } else {
+ /* No list, so pass it up now */
+ gro_result = napi_gro_frags(napi);
+ if (gro_result != GRO_DROP)
+ channel->irq_mod_score += 2;
+ }
}
/* Allocate and construct an SKB around page fragments */
^ permalink raw reply related
* [PATCH v3 net-next 3/4] net: make listified RX functions return number of good packets
From: Edward Cree @ 2018-11-14 18:09 UTC (permalink / raw)
To: linux-net-drivers, davem; +Cc: netdev, eric.dumazet
In-Reply-To: <8e9ea3c4-82e0-a34c-08ea-32a387e4c9e1@solarflare.com>
'Good' packets are defined as skbs for which netif_receive_skb() would
have returned %NET_RX_SUCCESS. Thus, drivers can use this number for
adaptive interrupt moderation where they previously reacted to the
return code from netif_receive_skb().
Signed-off-by: Edward Cree <ecree@solarflare.com>
---
include/linux/netdevice.h | 4 +--
include/net/ip.h | 4 +--
include/net/ipv6.h | 4 +--
net/core/dev.c | 63 +++++++++++++++++++++++++++++------------------
net/ipv4/ip_input.c | 39 ++++++++++++++++++-----------
net/ipv6/ip6_input.c | 37 +++++++++++++++++-----------
6 files changed, 92 insertions(+), 59 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 2cef1d0fb2b1..76b98386a5dd 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2357,7 +2357,7 @@ struct packet_type {
struct net_device *,
struct packet_type *,
struct net_device *);
- void (*list_func) (struct list_head *,
+ int (*list_func) (struct list_head *,
struct packet_type *,
struct net_device *);
bool (*id_match)(struct packet_type *ptype,
@@ -3587,7 +3587,7 @@ int netif_rx(struct sk_buff *skb);
int netif_rx_ni(struct sk_buff *skb);
int netif_receive_skb(struct sk_buff *skb);
int netif_receive_skb_core(struct sk_buff *skb);
-void netif_receive_skb_list(struct list_head *head);
+int netif_receive_skb_list(struct list_head *head);
gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb);
int napi_gro_receive_list(struct napi_struct *napi, struct list_head *head);
void napi_gro_flush(struct napi_struct *napi, bool flush_old);
diff --git a/include/net/ip.h b/include/net/ip.h
index 8866bfce6121..33ab464f7a09 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -152,8 +152,8 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk,
struct ip_options_rcu *opt);
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
struct net_device *orig_dev);
-void ip_list_rcv(struct list_head *head, struct packet_type *pt,
- struct net_device *orig_dev);
+int ip_list_rcv(struct list_head *head, struct packet_type *pt,
+ struct net_device *orig_dev);
int ip_local_deliver(struct sk_buff *skb);
void ip_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int proto);
int ip_mr_input(struct sk_buff *skb);
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index daf80863d3a5..e25920829a94 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -914,8 +914,8 @@ static inline __be32 flowi6_get_flowlabel(const struct flowi6 *fl6)
int ipv6_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev);
-void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,
- struct net_device *orig_dev);
+int ipv6_list_rcv(struct list_head *head, struct packet_type *pt,
+ struct net_device *orig_dev);
int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
diff --git a/net/core/dev.c b/net/core/dev.c
index 8f0fb56170b3..35427167f6fb 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4969,24 +4969,27 @@ int netif_receive_skb_core(struct sk_buff *skb)
}
EXPORT_SYMBOL(netif_receive_skb_core);
-static inline void __netif_receive_skb_list_ptype(struct list_head *head,
- struct packet_type *pt_prev,
- struct net_device *orig_dev)
+static inline int __netif_receive_skb_list_ptype(struct list_head *head,
+ struct packet_type *pt_prev,
+ struct net_device *orig_dev)
{
struct sk_buff *skb, *next;
+ int kept = 0;
if (!pt_prev)
- return;
+ return 0;
if (list_empty(head))
- return;
+ return 0;
if (pt_prev->list_func != NULL)
- pt_prev->list_func(head, pt_prev, orig_dev);
+ kept = pt_prev->list_func(head, pt_prev, orig_dev);
else
list_for_each_entry_safe(skb, next, head, list)
- pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
+ if (pt_prev->func(skb, skb->dev, pt_prev, orig_dev) == NET_RX_SUCCESS)
+ kept++;
+ return kept;
}
-static void __netif_receive_skb_list_core(struct list_head *head, bool pfmemalloc)
+static int __netif_receive_skb_list_core(struct list_head *head, bool pfmemalloc)
{
/* Fast-path assumptions:
* - There is no RX handler.
@@ -5003,6 +5006,7 @@ static void __netif_receive_skb_list_core(struct list_head *head, bool pfmemallo
struct net_device *od_curr = NULL;
struct list_head sublist;
struct sk_buff *skb, *next;
+ int kept = 0, ret;
INIT_LIST_HEAD(&sublist);
list_for_each_entry_safe(skb, next, head, list) {
@@ -5010,12 +5014,15 @@ static void __netif_receive_skb_list_core(struct list_head *head, bool pfmemallo
struct packet_type *pt_prev = NULL;
list_del(&skb->list);
- __netif_receive_skb_core(skb, pfmemalloc, &pt_prev);
- if (!pt_prev)
+ ret = __netif_receive_skb_core(skb, pfmemalloc, &pt_prev);
+ if (!pt_prev) {
+ if (ret == NET_RX_SUCCESS)
+ kept++;
continue;
+ }
if (pt_curr != pt_prev || od_curr != orig_dev) {
/* dispatch old sublist */
- __netif_receive_skb_list_ptype(&sublist, pt_curr, od_curr);
+ kept += __netif_receive_skb_list_ptype(&sublist, pt_curr, od_curr);
/* start new sublist */
INIT_LIST_HEAD(&sublist);
pt_curr = pt_prev;
@@ -5025,7 +5032,8 @@ static void __netif_receive_skb_list_core(struct list_head *head, bool pfmemallo
}
/* dispatch final sublist */
- __netif_receive_skb_list_ptype(&sublist, pt_curr, od_curr);
+ kept += __netif_receive_skb_list_ptype(&sublist, pt_curr, od_curr);
+ return kept;
}
static int __netif_receive_skb(struct sk_buff *skb)
@@ -5053,11 +5061,12 @@ static int __netif_receive_skb(struct sk_buff *skb)
return ret;
}
-static void __netif_receive_skb_list(struct list_head *head)
+static int __netif_receive_skb_list(struct list_head *head)
{
unsigned long noreclaim_flag = 0;
struct sk_buff *skb, *next;
bool pfmemalloc = false; /* Is current sublist PF_MEMALLOC? */
+ int kept = 0;
list_for_each_entry_safe(skb, next, head, list) {
if ((sk_memalloc_socks() && skb_pfmemalloc(skb)) != pfmemalloc) {
@@ -5066,7 +5075,7 @@ static void __netif_receive_skb_list(struct list_head *head)
/* Handle the previous sublist */
list_cut_before(&sublist, head, &skb->list);
if (!list_empty(&sublist))
- __netif_receive_skb_list_core(&sublist, pfmemalloc);
+ kept += __netif_receive_skb_list_core(&sublist, pfmemalloc);
pfmemalloc = !pfmemalloc;
/* See comments in __netif_receive_skb */
if (pfmemalloc)
@@ -5077,10 +5086,11 @@ static void __netif_receive_skb_list(struct list_head *head)
}
/* Handle the remaining sublist */
if (!list_empty(head))
- __netif_receive_skb_list_core(head, pfmemalloc);
+ kept += __netif_receive_skb_list_core(head, pfmemalloc);
/* Restore pflags */
if (pfmemalloc)
memalloc_noreclaim_restore(noreclaim_flag);
+ return kept;
}
static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp)
@@ -5156,17 +5166,20 @@ static int netif_receive_skb_internal(struct sk_buff *skb)
return ret;
}
-static void netif_receive_skb_list_internal(struct list_head *head)
+static int netif_receive_skb_list_internal(struct list_head *head)
{
struct bpf_prog *xdp_prog = NULL;
struct sk_buff *skb, *next;
struct list_head sublist;
+ int kept = 0;
INIT_LIST_HEAD(&sublist);
list_for_each_entry_safe(skb, next, head, list) {
net_timestamp_check(netdev_tstamp_prequeue, skb);
list_del(&skb->list);
- if (!skb_defer_rx_timestamp(skb))
+ if (skb_defer_rx_timestamp(skb))
+ kept++;
+ else
list_add_tail(&skb->list, &sublist);
}
list_splice_init(&sublist, head);
@@ -5196,13 +5209,15 @@ static void netif_receive_skb_list_internal(struct list_head *head)
if (cpu >= 0) {
/* Will be handled, remove from list */
list_del(&skb->list);
- enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
+ if (enqueue_to_backlog(skb, cpu, &rflow->last_qtail) == NET_RX_SUCCESS)
+ kept++;
}
}
}
#endif
- __netif_receive_skb_list(head);
+ kept += __netif_receive_skb_list(head);
rcu_read_unlock();
+ return kept;
}
/**
@@ -5232,21 +5247,21 @@ EXPORT_SYMBOL(netif_receive_skb);
* netif_receive_skb_list - process many receive buffers from network
* @head: list of skbs to process.
*
- * Since return value of netif_receive_skb() is normally ignored, and
- * wouldn't be meaningful for a list, this function returns void.
+ * Returns the number of skbs for which netif_receive_skb() would have
+ * returned %NET_RX_SUCCESS.
*
* This function may only be called from softirq context and interrupts
* should be enabled.
*/
-void netif_receive_skb_list(struct list_head *head)
+int netif_receive_skb_list(struct list_head *head)
{
struct sk_buff *skb;
if (list_empty(head))
- return;
+ return 0;
list_for_each_entry(skb, head, list)
trace_netif_receive_skb_list_entry(skb);
- netif_receive_skb_list_internal(head);
+ return netif_receive_skb_list_internal(head);
}
EXPORT_SYMBOL(netif_receive_skb_list);
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
index 72250b4e466d..fa4eb82f9e55 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -525,22 +525,26 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
ip_rcv_finish);
}
-static void ip_sublist_rcv_finish(struct list_head *head)
+static int ip_sublist_rcv_finish(struct list_head *head)
{
struct sk_buff *skb, *next;
+ int kept = 0;
list_for_each_entry_safe(skb, next, head, list) {
skb_list_del_init(skb);
- dst_input(skb);
+ if (dst_input(skb) == NET_RX_SUCCESS)
+ kept++;
}
+ return kept;
}
-static void ip_list_rcv_finish(struct net *net, struct sock *sk,
- struct list_head *head)
+static int ip_list_rcv_finish(struct net *net, struct sock *sk,
+ struct list_head *head)
{
struct dst_entry *curr_dst = NULL;
struct sk_buff *skb, *next;
struct list_head sublist;
+ int kept = 0;
INIT_LIST_HEAD(&sublist);
list_for_each_entry_safe(skb, next, head, list) {
@@ -551,8 +555,10 @@ static void ip_list_rcv_finish(struct net *net, struct sock *sk,
* skb to its handler for processing
*/
skb = l3mdev_ip_rcv(skb);
- if (!skb)
+ if (!skb) {
+ kept++;
continue;
+ }
if (ip_rcv_finish_core(net, sk, skb) == NET_RX_DROP)
continue;
@@ -560,7 +566,7 @@ static void ip_list_rcv_finish(struct net *net, struct sock *sk,
if (curr_dst != dst) {
/* dispatch old sublist */
if (!list_empty(&sublist))
- ip_sublist_rcv_finish(&sublist);
+ kept += ip_sublist_rcv_finish(&sublist);
/* start new sublist */
INIT_LIST_HEAD(&sublist);
curr_dst = dst;
@@ -568,25 +574,27 @@ static void ip_list_rcv_finish(struct net *net, struct sock *sk,
list_add_tail(&skb->list, &sublist);
}
/* dispatch final sublist */
- ip_sublist_rcv_finish(&sublist);
+ kept += ip_sublist_rcv_finish(&sublist);
+ return kept;
}
-static void ip_sublist_rcv(struct list_head *head, struct net_device *dev,
- struct net *net)
+static int ip_sublist_rcv(struct list_head *head, struct net_device *dev,
+ struct net *net)
{
NF_HOOK_LIST(NFPROTO_IPV4, NF_INET_PRE_ROUTING, net, NULL,
head, dev, NULL, ip_rcv_finish);
- ip_list_rcv_finish(net, NULL, head);
+ return ip_list_rcv_finish(net, NULL, head);
}
-/* Receive a list of IP packets */
-void ip_list_rcv(struct list_head *head, struct packet_type *pt,
- struct net_device *orig_dev)
+/* Receive a list of IP packets; return number of successful receives */
+int ip_list_rcv(struct list_head *head, struct packet_type *pt,
+ struct net_device *orig_dev)
{
struct net_device *curr_dev = NULL;
struct net *curr_net = NULL;
struct sk_buff *skb, *next;
struct list_head sublist;
+ int kept = 0;
INIT_LIST_HEAD(&sublist);
list_for_each_entry_safe(skb, next, head, list) {
@@ -601,7 +609,7 @@ void ip_list_rcv(struct list_head *head, struct packet_type *pt,
if (curr_dev != dev || curr_net != net) {
/* dispatch old sublist */
if (!list_empty(&sublist))
- ip_sublist_rcv(&sublist, curr_dev, curr_net);
+ kept += ip_sublist_rcv(&sublist, curr_dev, curr_net);
/* start new sublist */
INIT_LIST_HEAD(&sublist);
curr_dev = dev;
@@ -610,5 +618,6 @@ void ip_list_rcv(struct list_head *head, struct packet_type *pt,
list_add_tail(&skb->list, &sublist);
}
/* dispatch final sublist */
- ip_sublist_rcv(&sublist, curr_dev, curr_net);
+ kept += ip_sublist_rcv(&sublist, curr_dev, curr_net);
+ return kept;
}
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
index 3c06cc9e9b79..6e013672e1de 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -76,20 +76,24 @@ int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
return dst_input(skb);
}
-static void ip6_sublist_rcv_finish(struct list_head *head)
+static int ip6_sublist_rcv_finish(struct list_head *head)
{
struct sk_buff *skb, *next;
+ int kept = 0;
list_for_each_entry_safe(skb, next, head, list)
- dst_input(skb);
+ if (dst_input(skb) == NET_RX_SUCCESS)
+ kept++;
+ return kept;
}
-static void ip6_list_rcv_finish(struct net *net, struct sock *sk,
- struct list_head *head)
+static int ip6_list_rcv_finish(struct net *net, struct sock *sk,
+ struct list_head *head)
{
struct dst_entry *curr_dst = NULL;
struct sk_buff *skb, *next;
struct list_head sublist;
+ int kept = 0;
INIT_LIST_HEAD(&sublist);
list_for_each_entry_safe(skb, next, head, list) {
@@ -100,14 +104,16 @@ static void ip6_list_rcv_finish(struct net *net, struct sock *sk,
* skb to its handler for processing
*/
skb = l3mdev_ip6_rcv(skb);
- if (!skb)
+ if (!skb) {
+ kept++;
continue;
+ }
ip6_rcv_finish_core(net, sk, skb);
dst = skb_dst(skb);
if (curr_dst != dst) {
/* dispatch old sublist */
if (!list_empty(&sublist))
- ip6_sublist_rcv_finish(&sublist);
+ kept += ip6_sublist_rcv_finish(&sublist);
/* start new sublist */
INIT_LIST_HEAD(&sublist);
curr_dst = dst;
@@ -115,7 +121,8 @@ static void ip6_list_rcv_finish(struct net *net, struct sock *sk,
list_add_tail(&skb->list, &sublist);
}
/* dispatch final sublist */
- ip6_sublist_rcv_finish(&sublist);
+ kept += ip6_sublist_rcv_finish(&sublist);
+ return kept;
}
static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,
@@ -274,22 +281,23 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
ip6_rcv_finish);
}
-static void ip6_sublist_rcv(struct list_head *head, struct net_device *dev,
- struct net *net)
+static int ip6_sublist_rcv(struct list_head *head, struct net_device *dev,
+ struct net *net)
{
NF_HOOK_LIST(NFPROTO_IPV6, NF_INET_PRE_ROUTING, net, NULL,
head, dev, NULL, ip6_rcv_finish);
- ip6_list_rcv_finish(net, NULL, head);
+ return ip6_list_rcv_finish(net, NULL, head);
}
/* Receive a list of IPv6 packets */
-void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,
- struct net_device *orig_dev)
+int ipv6_list_rcv(struct list_head *head, struct packet_type *pt,
+ struct net_device *orig_dev)
{
struct net_device *curr_dev = NULL;
struct net *curr_net = NULL;
struct sk_buff *skb, *next;
struct list_head sublist;
+ int kept = 0;
INIT_LIST_HEAD(&sublist);
list_for_each_entry_safe(skb, next, head, list) {
@@ -304,7 +312,7 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,
if (curr_dev != dev || curr_net != net) {
/* dispatch old sublist */
if (!list_empty(&sublist))
- ip6_sublist_rcv(&sublist, curr_dev, curr_net);
+ kept += ip6_sublist_rcv(&sublist, curr_dev, curr_net);
/* start new sublist */
INIT_LIST_HEAD(&sublist);
curr_dev = dev;
@@ -313,7 +321,8 @@ void ipv6_list_rcv(struct list_head *head, struct packet_type *pt,
list_add_tail(&skb->list, &sublist);
}
/* dispatch final sublist */
- ip6_sublist_rcv(&sublist, curr_dev, curr_net);
+ kept += ip6_sublist_rcv(&sublist, curr_dev, curr_net);
+ return kept;
}
/*
^ permalink raw reply related
* [PATCH v3 net-next 4/4] net/core: handle GRO_NORMAL skbs as a list in napi_gro_receive_list
From: Edward Cree @ 2018-11-14 18:10 UTC (permalink / raw)
To: linux-net-drivers, davem; +Cc: netdev, eric.dumazet
In-Reply-To: <8e9ea3c4-82e0-a34c-08ea-32a387e4c9e1@solarflare.com>
Allows GRO-using drivers to get the benefits of batching for non-GROable
traffic.
Signed-off-by: Edward Cree <ecree@solarflare.com>
---
net/core/dev.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index 35427167f6fb..65bfe28fbc81 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5664,6 +5664,7 @@ EXPORT_SYMBOL(napi_gro_receive);
int napi_gro_receive_list(struct napi_struct *napi, struct list_head *head)
{
struct sk_buff *skb, *next;
+ struct list_head sublist;
gro_result_t result;
int kept = 0;
@@ -5673,14 +5674,26 @@ int napi_gro_receive_list(struct napi_struct *napi, struct list_head *head)
skb_gro_reset_offset(skb);
}
+ INIT_LIST_HEAD(&sublist);
list_for_each_entry_safe(skb, next, head, list) {
list_del(&skb->list);
skb->next = NULL;
result = dev_gro_receive(napi, skb);
- result = napi_skb_finish(result, skb);
- if (result != GRO_DROP)
- kept++;
+ if (result == GRO_NORMAL) {
+ list_add_tail(&skb->list, &sublist);
+ continue;
+ } else {
+ if (!list_empty(&sublist)) {
+ /* Handle the GRO_NORMAL skbs to prevent OoO */
+ kept += netif_receive_skb_list_internal(&sublist);
+ INIT_LIST_HEAD(&sublist);
+ }
+ result = napi_skb_finish(result, skb);
+ if (result != GRO_DROP)
+ kept++;
+ }
}
+ kept += netif_receive_skb_list_internal(&sublist);
return kept;
}
EXPORT_SYMBOL(napi_gro_receive_list);
^ permalink raw reply related
* [PATCH net-next] nxp: fix trivial comment typo
From: Andrea Claudi @ 2018-11-14 18:47 UTC (permalink / raw)
To: netdev, davem, vz
s/rxfliterctrl/rxfilterctrl
Signed-off-by: Andrea Claudi <aclaudi@redhat.com>
---
drivers/net/ethernet/nxp/lpc_eth.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c
index bd8695a4faaa..89d17399fb5a 100644
--- a/drivers/net/ethernet/nxp/lpc_eth.c
+++ b/drivers/net/ethernet/nxp/lpc_eth.c
@@ -280,7 +280,7 @@
#define LPC_FCCR_MIRRORCOUNTERCURRENT(n) ((n) & 0xFFFF)
/*
- * rxfliterctrl, rxfilterwolstatus, and rxfilterwolclear shared
+ * rxfilterctrl, rxfilterwolstatus, and rxfilterwolclear shared
* register definitions
*/
#define LPC_RXFLTRW_ACCEPTUNICAST (1 << 0)
@@ -291,7 +291,7 @@
#define LPC_RXFLTRW_ACCEPTPERFECT (1 << 5)
/*
- * rxfliterctrl register definitions
+ * rxfilterctrl register definitions
*/
#define LPC_RXFLTRWSTS_MAGICPACKETENWOL (1 << 12)
#define LPC_RXFLTRWSTS_RXFILTERENWOL (1 << 13)
--
2.17.2
^ permalink raw reply related
* Re: [PATCH net] ipv6: fix a dst leak when removing its exception
From: David Ahern @ 2018-11-14 19:03 UTC (permalink / raw)
To: Xin Long, network dev; +Cc: davem
In-Reply-To: <b031b44110fcb06d780ed60ecadfa4e8bc39acfb.1542127708.git.lucien.xin@gmail.com>
On 11/13/18 8:48 AM, Xin Long wrote:
> These is no need to hold dst before calling rt6_remove_exception_rt().
> The call to dst_hold_safe() in ip6_link_failure() was for ip6_del_rt(),
> which has been removed in Commit 93531c674315 ("net/ipv6: separate
> handling of FIB entries from dst based routes"). Otherwise, it will
> cause a dst leak.
>
> This patch is to simply remove the dst_hold_safe() call before calling
> rt6_remove_exception_rt() and also do the same in ip6_del_cached_rt().
> It's safe, because the removal of the exception that holds its dst's
> refcnt is protected by rt6_exception_lock.
>
> Fixes: 93531c674315 ("net/ipv6: separate handling of FIB entries from dst based routes")
> Fixes: 23fb93a4d3f1 ("net/ipv6: Cleanup exception and cache route handling")
> Reported-by: Li Shuang <shuali@redhat.com>
> Signed-off-by: Xin Long <lucien.xin@gmail.com>
> ---
> net/ipv6/route.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
was this problem actually hit or is this patch based on a code analysis?
^ permalink raw reply
* [PATCH 1/1] net-next/hinic:fix three problems in HiNIC Driver
From: Xue Chaojing @ 2018-11-15 5:23 UTC (permalink / raw)
To: davem
Cc: linux-kernel, xuechaojing, netdev, wulike1, chiqijun, fy.wang,
tony.qu, luoshaokai
This patch fixes these problems:
1. In order to improve performance, this patch add rx checksum offload
for the HiNIC driver. Performance test(Iperf) shows more than 95%
improvement in TCP streams.
2. In add_mac_addr(), if the MAC address is a muliticast address, it
will not be set, which causes the network card fail to receive the
multicast packet. This patch fixes this bug.
3. In rx_alloc_pkts(), there is a loop call of tasklet, which causes
100% cpu utilization, even no packets are being received. This patch
fixes this bug.
Signed-off-by: Xue Chaojing <xuechaojing@huawei.com>
---
.../net/ethernet/huawei/hinic/hinic_hw_dev.h | 2 +
.../net/ethernet/huawei/hinic/hinic_hw_wq.c | 43 +++++++++++++------
.../net/ethernet/huawei/hinic/hinic_hw_wq.h | 3 +-
.../net/ethernet/huawei/hinic/hinic_hw_wqe.h | 4 ++
.../net/ethernet/huawei/hinic/hinic_main.c | 14 ++++--
.../net/ethernet/huawei/hinic/hinic_port.c | 28 ++++++++++++
.../net/ethernet/huawei/hinic/hinic_port.h | 10 +++++
drivers/net/ethernet/huawei/hinic/hinic_rx.c | 26 ++++++++++-
drivers/net/ethernet/huawei/hinic/hinic_rx.h | 5 +++
9 files changed, 116 insertions(+), 19 deletions(-)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h
index 097b5502603f..d1a7d2522d82 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h
@@ -50,6 +50,8 @@ enum hinic_port_cmd {
HINIC_PORT_CMD_GET_LINK_STATE = 24,
+ HINIC_PORT_CMD_SET_RX_CSUM = 26,
+
HINIC_PORT_CMD_SET_PORT_STATE = 41,
HINIC_PORT_CMD_FWCTXT_INIT = 69,
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c
index f92f1bf3901a..34859502c932 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c
@@ -74,12 +74,6 @@
((void *)((cmdq_pages)->shadow_page_vaddr) \
+ (wq)->block_idx * CMDQ_BLOCK_SIZE)
-#define WQE_PAGE_OFF(wq, idx) (((idx) & ((wq)->num_wqebbs_per_page - 1)) * \
- (wq)->wqebb_size)
-
-#define WQE_PAGE_NUM(wq, idx) (((idx) / ((wq)->num_wqebbs_per_page)) \
- & ((wq)->num_q_pages - 1))
-
#define WQ_PAGE_ADDR(wq, idx) \
((wq)->shadow_block_vaddr[WQE_PAGE_NUM(wq, idx)])
@@ -93,6 +87,17 @@
(((unsigned long)(wqe) - (unsigned long)(wq)->shadow_wqe) \
/ (wq)->max_wqe_size)
+static inline int WQE_PAGE_OFF(struct hinic_wq *wq, u16 idx)
+{
+ return (((idx) & ((wq)->num_wqebbs_per_page - 1))
+ << (wq)->wqebb_size_shift);
+}
+
+static inline int WQE_PAGE_NUM(struct hinic_wq *wq, u16 idx)
+{
+ return (((idx) >> ((wq)->wqebbs_per_page_shift))
+ & ((wq)->num_q_pages - 1));
+}
/**
* queue_alloc_page - allocate page for Queue
* @hwif: HW interface for allocating DMA
@@ -513,6 +518,7 @@ int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
struct hinic_hwif *hwif = wqs->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 num_wqebbs_per_page;
+ u16 wqebb_size_shift;
int err;
if (wqebb_size == 0) {
@@ -530,7 +536,9 @@ int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
return -EINVAL;
}
- num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size;
+ wqebb_size_shift = ilog2(wqebb_size);
+ num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size)
+ >> wqebb_size_shift;
if (num_wqebbs_per_page & (num_wqebbs_per_page - 1)) {
dev_err(&pdev->dev, "num wqebbs per page must be power of 2\n");
@@ -550,7 +558,8 @@ int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
wq->q_depth = q_depth;
wq->max_wqe_size = max_wqe_size;
wq->num_wqebbs_per_page = num_wqebbs_per_page;
-
+ wq->wqebbs_per_page_shift = ilog2(num_wqebbs_per_page);
+ wq->wqebb_size_shift = wqebb_size_shift;
wq->block_vaddr = WQ_BASE_VADDR(wqs, wq);
wq->shadow_block_vaddr = WQ_BASE_ADDR(wqs, wq);
wq->block_paddr = WQ_BASE_PADDR(wqs, wq);
@@ -604,7 +613,9 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
u16 q_depth, u16 max_wqe_size)
{
struct pci_dev *pdev = hwif->pdev;
+ u16 num_wqebbs_per_page_shift;
u16 num_wqebbs_per_page;
+ u16 wqebb_size_shift;
int i, j, err = -ENOMEM;
if (wqebb_size == 0) {
@@ -622,7 +633,9 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
return -EINVAL;
}
- num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size;
+ wqebb_size_shift = ilog2(wqebb_size);
+ num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size)
+ >> wqebb_size_shift;
if (num_wqebbs_per_page & (num_wqebbs_per_page - 1)) {
dev_err(&pdev->dev, "num wqebbs per page must be power of 2\n");
@@ -636,6 +649,7 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
dev_err(&pdev->dev, "Failed to allocate CMDQ page\n");
return err;
}
+ num_wqebbs_per_page_shift = ilog2(num_wqebbs_per_page);
for (i = 0; i < cmdq_blocks; i++) {
wq[i].hwif = hwif;
@@ -647,7 +661,8 @@ int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
wq[i].q_depth = q_depth;
wq[i].max_wqe_size = max_wqe_size;
wq[i].num_wqebbs_per_page = num_wqebbs_per_page;
-
+ wq[i].wqebbs_per_page_shift = num_wqebbs_per_page_shift;
+ wq[i].wqebb_size_shift = wqebb_size_shift;
wq[i].block_vaddr = CMDQ_BASE_VADDR(cmdq_pages, &wq[i]);
wq[i].shadow_block_vaddr = CMDQ_BASE_ADDR(cmdq_pages, &wq[i]);
wq[i].block_paddr = CMDQ_BASE_PADDR(cmdq_pages, &wq[i]);
@@ -741,7 +756,7 @@ struct hinic_hw_wqe *hinic_get_wqe(struct hinic_wq *wq, unsigned int wqe_size,
*prod_idx = MASKED_WQE_IDX(wq, atomic_read(&wq->prod_idx));
- num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+ num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) >> wq->wqebb_size_shift;
if (atomic_sub_return(num_wqebbs, &wq->delta) <= 0) {
atomic_add(num_wqebbs, &wq->delta);
@@ -795,7 +810,8 @@ void hinic_return_wqe(struct hinic_wq *wq, unsigned int wqe_size)
**/
void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size)
{
- int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+ int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size)
+ >> wq->wqebb_size_shift;
atomic_add(num_wqebbs, &wq->cons_idx);
@@ -813,7 +829,8 @@ void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size)
struct hinic_hw_wqe *hinic_read_wqe(struct hinic_wq *wq, unsigned int wqe_size,
u16 *cons_idx)
{
- int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+ int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size)
+ >> wq->wqebb_size_shift;
u16 curr_cons_idx, end_cons_idx;
int curr_pg, end_pg;
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h
index 9b66545ba563..0a936cd6709b 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h
@@ -39,7 +39,8 @@ struct hinic_wq {
u16 q_depth;
u16 max_wqe_size;
u16 num_wqebbs_per_page;
-
+ u16 wqebbs_per_page_shift;
+ u16 wqebb_size_shift;
/* The addresses are 64 bit in the HW */
u64 block_paddr;
void **shadow_block_vaddr;
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h
index 9754d6ed5f4a..138941527872 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h
@@ -170,6 +170,10 @@
#define HINIC_RQ_CQE_STATUS_RXDONE_MASK 0x1
+#define HINIC_RQ_CQE_STATUS_CSUM_ERR_SHIFT 0
+
+#define HINIC_RQ_CQE_STATUS_CSUM_ERR_MASK 0xFFFFU
+
#define HINIC_RQ_CQE_STATUS_GET(val, member) \
(((val) >> HINIC_RQ_CQE_STATUS_##member##_SHIFT) & \
HINIC_RQ_CQE_STATUS_##member##_MASK)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c
index fdf2bdb6b0d0..6d48dc62a44b 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_main.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c
@@ -600,9 +600,6 @@ static int add_mac_addr(struct net_device *netdev, const u8 *addr)
u16 vid = 0;
int err;
- if (!is_valid_ether_addr(addr))
- return -EADDRNOTAVAIL;
-
netif_info(nic_dev, drv, netdev, "set mac addr = %02x %02x %02x %02x %02x %02x\n",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
@@ -726,6 +723,7 @@ static void set_rx_mode(struct work_struct *work)
{
struct hinic_rx_mode_work *rx_mode_work = work_to_rx_mode_work(work);
struct hinic_dev *nic_dev = rx_mode_work_to_nic_dev(rx_mode_work);
+ struct netdev_hw_addr *ha;
netif_info(nic_dev, drv, nic_dev->netdev, "set rx mode work\n");
@@ -733,6 +731,9 @@ static void set_rx_mode(struct work_struct *work)
__dev_uc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr);
__dev_mc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr);
+
+ netdev_for_each_mc_addr(ha, nic_dev->netdev)
+ add_mac_addr(nic_dev->netdev, ha->addr);
}
static void hinic_set_rx_mode(struct net_device *netdev)
@@ -806,7 +807,8 @@ static const struct net_device_ops hinic_netdev_ops = {
static void netdev_features_init(struct net_device *netdev)
{
netdev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
- NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6;
+ NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |
+ NETIF_F_RXCSUM;
netdev->vlan_features = netdev->hw_features;
@@ -869,12 +871,16 @@ static int set_features(struct hinic_dev *nic_dev,
netdev_features_t features, bool force_change)
{
netdev_features_t changed = force_change ? ~0 : pre_features ^ features;
+ u32 csum_en = HINIC_RX_CSUM_OFFLOAD_EN;
int err = 0;
if (changed & NETIF_F_TSO)
err = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ?
HINIC_TSO_ENABLE : HINIC_TSO_DISABLE);
+ if (changed & NETIF_F_RXCSUM)
+ err = hinic_set_rx_csum_offload(nic_dev, csum_en);
+
return err;
}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c
index 7575a7d3bd9f..e9f76e904610 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_port.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c
@@ -409,3 +409,31 @@ int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state)
return 0;
}
+
+int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en)
+{
+ struct hinic_checksum_offload rx_csum_cfg = {0};
+ struct hinic_hwdev *hwdev = nic_dev->hwdev;
+ struct hinic_hwif *hwif = hwdev->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ u16 out_size;
+ int err;
+
+ if (!hwdev)
+ return -EINVAL;
+
+ rx_csum_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
+ rx_csum_cfg.rx_csum_offload = en;
+
+ err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_CSUM,
+ &rx_csum_cfg, sizeof(rx_csum_cfg),
+ &rx_csum_cfg, &out_size);
+ if (err || !out_size || rx_csum_cfg.status) {
+ dev_err(&pdev->dev,
+ "Failed to set rx csum offload, ret = %d\n",
+ rx_csum_cfg.status);
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h
index f6e3220fe28f..02d896eed455 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_port.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h
@@ -183,6 +183,15 @@ struct hinic_tso_config {
u8 resv2[3];
};
+struct hinic_checksum_offload {
+ u8 status;
+ u8 version;
+ u8 rsvd0[6];
+
+ u16 func_id;
+ u16 rsvd1;
+ u32 rx_csum_offload;
+};
int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr,
u16 vlan_id);
@@ -213,4 +222,5 @@ int hinic_port_get_cap(struct hinic_dev *nic_dev,
int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state);
+int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en);
#endif
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c
index 4c0f7eda1166..f86f2e693224 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_rx.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c
@@ -89,6 +89,28 @@ static void rxq_stats_init(struct hinic_rxq *rxq)
hinic_rxq_clean_stats(rxq);
}
+static void rx_csum(struct hinic_rxq *rxq, u16 cons_idx,
+ struct sk_buff *skb)
+{
+ struct net_device *netdev = rxq->netdev;
+ struct hinic_rq_cqe *cqe;
+ struct hinic_rq *rq;
+ u32 csum_err;
+ u32 status;
+
+ rq = rxq->rq;
+ cqe = rq->cqe[cons_idx];
+ status = be32_to_cpu(cqe->status);
+ csum_err = HINIC_RQ_CQE_STATUS_GET(status, CSUM_ERR);
+
+ if (!(netdev->features & NETIF_F_RXCSUM))
+ return;
+
+ if (!csum_err)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb->ip_summed = CHECKSUM_NONE;
+}
/**
* rx_alloc_skb - allocate skb and map it to dma address
* @rxq: rx queue
@@ -207,9 +229,9 @@ static int rx_alloc_pkts(struct hinic_rxq *rxq)
wmb(); /* write all the wqes before update PI */
hinic_rq_update(rxq->rq, prod_idx);
+ tasklet_schedule(&rxq->rx_task);
}
- tasklet_schedule(&rxq->rx_task);
return i;
}
@@ -328,6 +350,8 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
rx_unmap_skb(rxq, hinic_sge_to_dma(&sge));
+ rx_csum(rxq, ci, skb);
+
prefetch(skb->data);
pkt_len = sge.len;
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.h b/drivers/net/ethernet/huawei/hinic/hinic_rx.h
index 27c9af4b1c12..7ab57d6ff83f 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_rx.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.h
@@ -23,6 +23,11 @@
#include "hinic_hw_qp.h"
+#define BIT(nr) (1UL << (nr))
+#define HINIC_RX_CSUM_OFFLOAD_EN 0xFFF
+#define HINIC_RX_CSUM_HW_CHECK_NONE BIT(7)
+#define HINIC_RX_CSUM_IPSU_OTHER_ERR BIT(8)
+
struct hinic_rxq_stats {
u64 pkts;
u64 bytes;
--
2.17.1
^ permalink raw reply related
* [PATCH v5] net: phy: mdio-gpio: Fix working over slow can_sleep GPIOs
From: Martin Schiller @ 2018-11-15 5:24 UTC (permalink / raw)
To: andrew, sergei.shtylyov, f.fainelli
Cc: davem, netdev, linux-kernel, Martin Schiller
In-Reply-To: <20181114061703.11026-1-ms@dev.tdt.de>
Up until commit 7e5fbd1e0700 ("net: mdio-gpio: Convert to use gpiod
functions where possible"), the _cansleep variants of the gpio_ API was
used. After that commit and the change to gpiod_ API, the _cansleep()
was dropped. This then results in WARN_ON() when used with GPIO
devices which do sleep. Add back the _cansleep() to avoid this.
Fixes: 7e5fbd1e0700 ("net: mdio-gpio: Convert to use gpiod functions where possible")
Signed-off-by: Martin Schiller <ms@dev.tdt.de>
---
v5:
- reworked commit message
- added "Fixes:" tag
- based on DaveM net tree instead of mainline
v4:
- remove linewrap of kernel message
v3:
- modified commit summary
- fixed commit cites in commit message
- fixed line continuation
v2:
- fixed copy/paste bug in warning about slow GPIO pins
---
drivers/net/phy/mdio-gpio.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index 33265747bf39..3a5a24daf384 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -63,7 +63,7 @@ static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
* assume the pin serves as pull-up. If direction is
* output, the default value is high.
*/
- gpiod_set_value(bitbang->mdo, 1);
+ gpiod_set_value_cansleep(bitbang->mdo, 1);
return;
}
@@ -78,7 +78,7 @@ static int mdio_get(struct mdiobb_ctrl *ctrl)
struct mdio_gpio_info *bitbang =
container_of(ctrl, struct mdio_gpio_info, ctrl);
- return gpiod_get_value(bitbang->mdio);
+ return gpiod_get_value_cansleep(bitbang->mdio);
}
static void mdio_set(struct mdiobb_ctrl *ctrl, int what)
@@ -87,9 +87,9 @@ static void mdio_set(struct mdiobb_ctrl *ctrl, int what)
container_of(ctrl, struct mdio_gpio_info, ctrl);
if (bitbang->mdo)
- gpiod_set_value(bitbang->mdo, what);
+ gpiod_set_value_cansleep(bitbang->mdo, what);
else
- gpiod_set_value(bitbang->mdio, what);
+ gpiod_set_value_cansleep(bitbang->mdio, what);
}
static void mdc_set(struct mdiobb_ctrl *ctrl, int what)
@@ -97,7 +97,7 @@ static void mdc_set(struct mdiobb_ctrl *ctrl, int what)
struct mdio_gpio_info *bitbang =
container_of(ctrl, struct mdio_gpio_info, ctrl);
- gpiod_set_value(bitbang->mdc, what);
+ gpiod_set_value_cansleep(bitbang->mdc, what);
}
static const struct mdiobb_ops mdio_gpio_ops = {
@@ -162,6 +162,10 @@ static int mdio_gpio_probe(struct platform_device *pdev)
if (ret)
return ret;
+ if (gpiod_cansleep(bitbang->mdc) || gpiod_cansleep(bitbang->mdio) ||
+ gpiod_cansleep(bitbang->mdo))
+ dev_warn(&pdev->dev, "Slow GPIO pins might wreak havoc into MDIO bus timing");
+
if (pdev->dev.of_node) {
bus_id = of_alias_get_id(pdev->dev.of_node, "mdio-gpio");
if (bus_id < 0) {
--
2.11.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox