* Re: [PATCH] ipw2x00: clean up clang warning on extraneous parentheses
From: Kalle Valo @ 2018-10-01 15:49 UTC (permalink / raw)
To: Colin King
Cc: Stanislav Yakovlev, David S . Miller, linux-wireless, netdev,
kernel-janitors, linux-kernel
In-Reply-To: <20180930223809.18136-1-colin.king@canonical.com>
Colin King <colin.king@canonical.com> wrote:
> From: Colin Ian King <colin.king@canonical.com>
>
> There are extraneous parantheses that are causing clang to produce a
> warning so remove these.
>
> Clean up clang warning:
> equality comparison with extraneous parentheses [-Wparentheses-equality]
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
I applied an identical patch few minutes ago:
c15ace868dd1 ipw2x00: Remove unnecessary parentheses
Patch set to Rejected.
--
https://patchwork.kernel.org/patch/10621549/
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
^ permalink raw reply
* pull request (net-next): ipsec-next 2018-10-01
From: Steffen Klassert @ 2018-10-01 9:16 UTC (permalink / raw)
To: David Miller; +Cc: Herbert Xu, Steffen Klassert, netdev
1) Make xfrmi_get_link_net() static to silence a sparse warning.
From Wei Yongjun.
2) Remove a unused esph pointer definition in esp_input().
From Haishuang Yan.
3) Allow the NIC driver to quietly refuse xfrm offload
in case it does not support it, the SA is created
without offload in this case.
From Shannon Nelson.
Please pull or let me know if there are problems.
Thanks!
The following changes since commit 817e60a7a2bb1f22052f18562990d675cb3a3762:
Merge branch 'nfp-add-NFP5000-support' (2018-08-28 16:01:48 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next.git master
for you to fetch changes up to 4a132095dd64fefabdc5dad1cd9e9809b126e582:
xfrm: allow driver to quietly refuse offload (2018-08-29 08:04:44 +0200)
----------------------------------------------------------------
Haishuang Yan (1):
esp: remove redundant define esph
Shannon Nelson (1):
xfrm: allow driver to quietly refuse offload
Wei Yongjun (1):
xfrm: Make function xfrmi_get_link_net() static
Documentation/networking/xfrm_device.txt | 4 ++++
net/ipv4/esp4.c | 7 +++----
net/ipv6/esp6.c | 7 +++----
net/xfrm/xfrm_device.c | 6 +++++-
net/xfrm/xfrm_interface.c | 2 +-
5 files changed, 16 insertions(+), 10 deletions(-)
^ permalink raw reply
* [PATCH 3/3] xfrm: allow driver to quietly refuse offload
From: Steffen Klassert @ 2018-10-01 9:16 UTC (permalink / raw)
To: David Miller; +Cc: Herbert Xu, Steffen Klassert, netdev
In-Reply-To: <20181001091609.16646-1-steffen.klassert@secunet.com>
From: Shannon Nelson <shannon.nelson@oracle.com>
If the "offload" attribute is used to create an IPsec SA
and the .xdo_dev_state_add() fails, the SA creation fails.
However, if the "offload" attribute is used on a device that
doesn't offer it, the attribute is quietly ignored and the SA
is created without an offload.
Along the same line of that second case, it would be good to
have a way for the device to refuse to offload an SA without
failing the whole SA creation. This patch adds that feature
by allowing the driver to return -EOPNOTSUPP as a signal that
the SA may be fine, it just can't be offloaded.
This allows the user a little more flexibility in requesting
offloads and not needing to know every detail at all times about
each specific NIC when trying to create SAs.
Signed-off-by: Shannon Nelson <shannon.nelson@oracle.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
---
Documentation/networking/xfrm_device.txt | 4 ++++
net/xfrm/xfrm_device.c | 6 +++++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/Documentation/networking/xfrm_device.txt b/Documentation/networking/xfrm_device.txt
index 50c34ca65efe..267f55b5f54a 100644
--- a/Documentation/networking/xfrm_device.txt
+++ b/Documentation/networking/xfrm_device.txt
@@ -68,6 +68,10 @@ and an indication of whether it is for Rx or Tx. The driver should
- verify the algorithm is supported for offloads
- store the SA information (key, salt, target-ip, protocol, etc)
- enable the HW offload of the SA
+ - return status value:
+ 0 success
+ -EOPNETSUPP offload not supported, try SW IPsec
+ other fail the request
The driver can also set an offload_handle in the SA, an opaque void pointer
that can be used to convey context into the fast-path offload requests.
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index 5611b7521020..3a1d9d6aefb4 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -192,9 +192,13 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
err = dev->xfrmdev_ops->xdo_dev_state_add(x);
if (err) {
+ xso->num_exthdrs = 0;
+ xso->flags = 0;
xso->dev = NULL;
dev_put(dev);
- return err;
+
+ if (err != -EOPNOTSUPP)
+ return err;
}
return 0;
--
2.17.1
^ permalink raw reply related
* [PATCH 2/3] esp: remove redundant define esph
From: Steffen Klassert @ 2018-10-01 9:16 UTC (permalink / raw)
To: David Miller; +Cc: Herbert Xu, Steffen Klassert, netdev
In-Reply-To: <20181001091609.16646-1-steffen.klassert@secunet.com>
From: Haishuang Yan <yanhaishuang@cmss.chinamobile.com>
The pointer 'esph' is defined but is never used hence it is redundant
and canbe removed.
Signed-off-by: Haishuang Yan <yanhaishuang@cmss.chinamobile.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
---
net/ipv4/esp4.c | 7 +++----
net/ipv6/esp6.c | 7 +++----
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 97689012b357..211caaf27f6e 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -683,12 +683,11 @@ static void esp_input_done_esn(struct crypto_async_request *base, int err)
*/
static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
{
- struct ip_esp_hdr *esph;
struct crypto_aead *aead = x->data;
struct aead_request *req;
struct sk_buff *trailer;
int ivlen = crypto_aead_ivsize(aead);
- int elen = skb->len - sizeof(*esph) - ivlen;
+ int elen = skb->len - sizeof(struct ip_esp_hdr) - ivlen;
int nfrags;
int assoclen;
int seqhilen;
@@ -698,13 +697,13 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
struct scatterlist *sg;
int err = -EINVAL;
- if (!pskb_may_pull(skb, sizeof(*esph) + ivlen))
+ if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + ivlen))
goto out;
if (elen <= 0)
goto out;
- assoclen = sizeof(*esph);
+ assoclen = sizeof(struct ip_esp_hdr);
seqhilen = 0;
if (x->props.flags & XFRM_STATE_ESN) {
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 88a7579c23bd..63b2b66f9dfa 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -601,12 +601,11 @@ static void esp_input_done_esn(struct crypto_async_request *base, int err)
static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
{
- struct ip_esp_hdr *esph;
struct crypto_aead *aead = x->data;
struct aead_request *req;
struct sk_buff *trailer;
int ivlen = crypto_aead_ivsize(aead);
- int elen = skb->len - sizeof(*esph) - ivlen;
+ int elen = skb->len - sizeof(struct ip_esp_hdr) - ivlen;
int nfrags;
int assoclen;
int seqhilen;
@@ -616,7 +615,7 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
u8 *iv;
struct scatterlist *sg;
- if (!pskb_may_pull(skb, sizeof(*esph) + ivlen)) {
+ if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + ivlen)) {
ret = -EINVAL;
goto out;
}
@@ -626,7 +625,7 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
goto out;
}
- assoclen = sizeof(*esph);
+ assoclen = sizeof(struct ip_esp_hdr);
seqhilen = 0;
if (x->props.flags & XFRM_STATE_ESN) {
--
2.17.1
^ permalink raw reply related
* [PATCH 1/3] xfrm: Make function xfrmi_get_link_net() static
From: Steffen Klassert @ 2018-10-01 9:16 UTC (permalink / raw)
To: David Miller; +Cc: Herbert Xu, Steffen Klassert, netdev
In-Reply-To: <20181001091609.16646-1-steffen.klassert@secunet.com>
From: Wei Yongjun <weiyongjun1@huawei.com>
Fixes the following sparse warning:
net/xfrm/xfrm_interface.c:745:12: warning:
symbol 'xfrmi_get_link_net' was not declared. Should it be static?
Fixes: f203b76d7809 ("xfrm: Add virtual xfrm interfaces")
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
---
net/xfrm/xfrm_interface.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c
index 31acc6f33d98..2c0a5c59dcd0 100644
--- a/net/xfrm/xfrm_interface.c
+++ b/net/xfrm/xfrm_interface.c
@@ -742,7 +742,7 @@ static int xfrmi_fill_info(struct sk_buff *skb, const struct net_device *dev)
return -EMSGSIZE;
}
-struct net *xfrmi_get_link_net(const struct net_device *dev)
+static struct net *xfrmi_get_link_net(const struct net_device *dev)
{
struct xfrm_if *xi = netdev_priv(dev);
--
2.17.1
^ permalink raw reply related
* Re: [PATCH net-next 2/7] net: phy: mscc: add support for VSC8584 PHY
From: Quentin Schulz @ 2018-10-01 9:16 UTC (permalink / raw)
To: Andrew Lunn
Cc: alexandre.belloni, ralf, paul.burton, jhogan, robh+dt,
mark.rutland, davem, f.fainelli, allan.nielsen, linux-mips,
devicetree, linux-kernel, netdev, thomas.petazzoni,
antoine.tenart
In-Reply-To: <20180914165824.GA3811@lunn.ch>
[-- Attachment #1: Type: text/plain, Size: 423 bytes --]
Hi Andrew,
On Fri, Sep 14, 2018 at 06:58:24PM +0200, Andrew Lunn wrote:
> > Confirmed by HW engineers, it only impacts PHYs in the same package.
>
> Hi Quentin
>
> Thanks for checking. As you said, it would be counter intuitive,
> meaning a lot of confusion if it actually did happen.
>
> Maybe you can add "in package" before broadcast in the commit message
> and the code comments.
>
ACK.
Quentin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [PATCH net] team: Forbid enslaving team device to itself
From: Ido Schimmel @ 2018-10-01 9:21 UTC (permalink / raw)
To: netdev; +Cc: davem, jiri, Ido Schimmel
team's ndo_add_slave() acquires 'team->lock' and later tries to open the
newly enslaved device via dev_open(). This emits a 'NETDEV_UP' event
that causes the VLAN driver to add VLAN 0 on the team device. team's
ndo_vlan_rx_add_vid() will also try to acquire 'team->lock' and
deadlock.
Fix this by checking early at the enslavement function that a team
device is not being enslaved to itself.
A similar check was added to the bond driver in commit 09a89c219baf
("bonding: disallow enslaving a bond to itself").
WARNING: possible recursive locking detected
4.18.0-rc7+ #176 Not tainted
--------------------------------------------
syz-executor4/6391 is trying to acquire lock:
(____ptrval____) (&team->lock){+.+.}, at: team_vlan_rx_add_vid+0x3b/0x1e0 drivers/net/team/team.c:1868
but task is already holding lock:
(____ptrval____) (&team->lock){+.+.}, at: team_add_slave+0xdb/0x1c30 drivers/net/team/team.c:1947
other info that might help us debug this:
Possible unsafe locking scenario:
CPU0
----
lock(&team->lock);
lock(&team->lock);
*** DEADLOCK ***
May be due to missing lock nesting notation
2 locks held by syz-executor4/6391:
#0: (____ptrval____) (rtnl_mutex){+.+.}, at: rtnl_lock net/core/rtnetlink.c:77 [inline]
#0: (____ptrval____) (rtnl_mutex){+.+.}, at: rtnetlink_rcv_msg+0x412/0xc30 net/core/rtnetlink.c:4662
#1: (____ptrval____) (&team->lock){+.+.}, at: team_add_slave+0xdb/0x1c30 drivers/net/team/team.c:1947
stack backtrace:
CPU: 1 PID: 6391 Comm: syz-executor4 Not tainted 4.18.0-rc7+ #176
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x1c9/0x2b4 lib/dump_stack.c:113
print_deadlock_bug kernel/locking/lockdep.c:1765 [inline]
check_deadlock kernel/locking/lockdep.c:1809 [inline]
validate_chain kernel/locking/lockdep.c:2405 [inline]
__lock_acquire.cold.64+0x1fb/0x486 kernel/locking/lockdep.c:3435
lock_acquire+0x1e4/0x540 kernel/locking/lockdep.c:3924
__mutex_lock_common kernel/locking/mutex.c:757 [inline]
__mutex_lock+0x176/0x1820 kernel/locking/mutex.c:894
mutex_lock_nested+0x16/0x20 kernel/locking/mutex.c:909
team_vlan_rx_add_vid+0x3b/0x1e0 drivers/net/team/team.c:1868
vlan_add_rx_filter_info+0x14a/0x1d0 net/8021q/vlan_core.c:210
__vlan_vid_add net/8021q/vlan_core.c:278 [inline]
vlan_vid_add+0x63e/0x9d0 net/8021q/vlan_core.c:308
vlan_device_event.cold.12+0x2a/0x2f net/8021q/vlan.c:381
notifier_call_chain+0x180/0x390 kernel/notifier.c:93
__raw_notifier_call_chain kernel/notifier.c:394 [inline]
raw_notifier_call_chain+0x2d/0x40 kernel/notifier.c:401
call_netdevice_notifiers_info+0x3f/0x90 net/core/dev.c:1735
call_netdevice_notifiers net/core/dev.c:1753 [inline]
dev_open+0x173/0x1b0 net/core/dev.c:1433
team_port_add drivers/net/team/team.c:1219 [inline]
team_add_slave+0xa8b/0x1c30 drivers/net/team/team.c:1948
do_set_master+0x1c9/0x220 net/core/rtnetlink.c:2248
do_setlink+0xba4/0x3e10 net/core/rtnetlink.c:2382
rtnl_setlink+0x2a9/0x400 net/core/rtnetlink.c:2636
rtnetlink_rcv_msg+0x46e/0xc30 net/core/rtnetlink.c:4665
netlink_rcv_skb+0x172/0x440 net/netlink/af_netlink.c:2455
rtnetlink_rcv+0x1c/0x20 net/core/rtnetlink.c:4683
netlink_unicast_kernel net/netlink/af_netlink.c:1317 [inline]
netlink_unicast+0x5a0/0x760 net/netlink/af_netlink.c:1343
netlink_sendmsg+0xa18/0xfd0 net/netlink/af_netlink.c:1908
sock_sendmsg_nosec net/socket.c:642 [inline]
sock_sendmsg+0xd5/0x120 net/socket.c:652
___sys_sendmsg+0x7fd/0x930 net/socket.c:2126
__sys_sendmsg+0x11d/0x290 net/socket.c:2164
__do_sys_sendmsg net/socket.c:2173 [inline]
__se_sys_sendmsg net/socket.c:2171 [inline]
__x64_sys_sendmsg+0x78/0xb0 net/socket.c:2171
do_syscall_64+0x1b9/0x820 arch/x86/entry/common.c:290
entry_SYSCALL_64_after_hwframe+0x49/0xbe
RIP: 0033:0x456b29
Code: fd b4 fb ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 cb b4 fb ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007f9706bf8c78 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f9706bf96d4 RCX: 0000000000456b29
RDX: 0000000000000000 RSI: 0000000020000240 RDI: 0000000000000004
RBP: 00000000009300a0 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00000000ffffffff
R13: 00000000004d3548 R14: 00000000004c8227 R15: 0000000000000000
Fixes: 87002b03baab ("net: introduce vlan_vid_[add/del] and use them instead of direct [add/kill]_vid ndo calls")
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Reported-and-tested-by: syzbot+bd051aba086537515cdb@syzkaller.appspotmail.com
---
drivers/net/team/team.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 6a047d30e8c6..d887016e54b6 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -1167,6 +1167,12 @@ static int team_port_add(struct team *team, struct net_device *port_dev,
return -EBUSY;
}
+ if (dev == port_dev) {
+ NL_SET_ERR_MSG(extack, "Cannot enslave team device to itself");
+ netdev_err(dev, "Cannot enslave team device to itself\n");
+ return -EINVAL;
+ }
+
if (port_dev->features & NETIF_F_VLAN_CHALLENGED &&
vlan_uses_dev(dev)) {
NL_SET_ERR_MSG(extack, "Device is VLAN challenged and team device has VLAN set up");
--
2.17.1
^ permalink raw reply related
* [PATCH] net: usbnet: make driver_info const
From: Ben Dooks @ 2018-10-01 16:02 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, oneukum, davem, linux-usb, linux-kernel, Ben Dooks,
Ben Dooks
From: Ben Dooks <ben-linux@fluff.org>
The driver_info field that is used for describing each of the usb-net
drivers using the usbnet.c core all declare their information as const
and the usbnet.c itself does not try and modify the struct.
It is therefore a good idea to make this const in the usbnet.c structure
in case anyone tries to modify it.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
---
drivers/net/usb/usbnet.c | 12 ++++++------
include/linux/usb/usbnet.h | 2 +-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 770aa624147f..d6b3833c292d 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -802,7 +802,7 @@ static void usbnet_terminate_urbs(struct usbnet *dev)
int usbnet_stop (struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
- struct driver_info *info = dev->driver_info;
+ const struct driver_info *info = dev->driver_info;
int retval, pm, mpn;
clear_bit(EVENT_DEV_OPEN, &dev->flags);
@@ -865,7 +865,7 @@ int usbnet_open (struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
int retval;
- struct driver_info *info = dev->driver_info;
+ const struct driver_info *info = dev->driver_info;
if ((retval = usb_autopm_get_interface(dev->intf)) < 0) {
netif_info(dev, ifup, dev->net,
@@ -1205,7 +1205,7 @@ usbnet_deferred_kevent (struct work_struct *work)
}
if (test_bit (EVENT_LINK_RESET, &dev->flags)) {
- struct driver_info *info = dev->driver_info;
+ const struct driver_info *info = dev->driver_info;
int retval = 0;
clear_bit (EVENT_LINK_RESET, &dev->flags);
@@ -1353,7 +1353,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
unsigned int length;
struct urb *urb = NULL;
struct skb_data *entry;
- struct driver_info *info = dev->driver_info;
+ const struct driver_info *info = dev->driver_info;
unsigned long flags;
int retval;
@@ -1646,7 +1646,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
struct usbnet *dev;
struct net_device *net;
struct usb_host_interface *interface;
- struct driver_info *info;
+ const struct driver_info *info;
struct usb_device *xdev;
int status;
const char *name;
@@ -1662,7 +1662,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
}
name = udev->dev.driver->name;
- info = (struct driver_info *) prod->driver_info;
+ info = (const struct driver_info *) prod->driver_info;
if (!info) {
dev_dbg (&udev->dev, "blacklisted by %s\n", name);
return -ENODEV;
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index e2ec3582e549..d8860f2d0976 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -28,7 +28,7 @@ struct usbnet {
/* housekeeping */
struct usb_device *udev;
struct usb_interface *intf;
- struct driver_info *driver_info;
+ const struct driver_info *driver_info;
const char *driver_name;
void *driver_priv;
wait_queue_head_t wait;
--
2.19.0
^ permalink raw reply related
* Re: [net-next, PATCH 1/2, v3] net: socionext: different approach on DMA
From: Jesper Dangaard Brouer @ 2018-10-01 9:26 UTC (permalink / raw)
To: Ilias Apalodimas
Cc: netdev, jaswinder.singh, ard.biesheuvel, masami.hiramatsu, arnd,
bjorn.topel, magnus.karlsson, daniel, ast, jesus.sanchez-palencia,
vinicius.gomes, makita.toshiaki, brouer, Tariq Toukan,
Tariq Toukan
In-Reply-To: <1538220482-16129-2-git-send-email-ilias.apalodimas@linaro.org>
On Sat, 29 Sep 2018 14:28:01 +0300 Ilias Apalodimas <ilias.apalodimas@linaro.org> wrote:
> +static void *netsec_alloc_rx_data(struct netsec_priv *priv,
> + dma_addr_t *dma_handle, u16 *desc_len)
> +{
> + size_t len = priv->ndev->mtu + ETH_HLEN + 2 * VLAN_HLEN + NET_SKB_PAD +
> + NET_IP_ALIGN;
> + dma_addr_t mapping;
> + void *buf;
> +
> + len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
> + len = SKB_DATA_ALIGN(len);
> +
> + buf = napi_alloc_frag(len);
Using napi_alloc_frag here ^^^^
> + if (!buf)
> + return NULL;
> +
> + mapping = dma_map_single(priv->dev, buf, len, DMA_FROM_DEVICE);
> + if (unlikely(dma_mapping_error(priv->dev, mapping)))
> + goto err_out;
> +
> + *dma_handle = mapping;
> + *desc_len = len;
> +
> + return buf;
> +
> +err_out:
> + skb_free_frag(buf);
> + return NULL;
> +}
Hmmm, you are using napi_alloc_frag() in above code, which behind
your-back allocates order-3 pages (32 Kbytes memory in 8 order-0 pages).
This violates at-least two XDP principals:
#1: You are NOT using order-0 page based allocations for XDP.
Notice, I'm not saying 1-page per packet, as ixgbe + i40e violated
this, and it is now "per-practical-code-example" acceptable to split up
the order-0 page, and store two RX frames per order-0 page (4096 bytes).
(To make this fit you have to reduce XDP_HEADROOM to 192 bytes, which
killed the idea of placing the SKB in this area).
#2: You have allocations on the XDP fast-path.
The REAL secret behind the XDP performance is to avoid allocations on
the fast-path. While I just told you to use the page-allocator and
order-0 pages, this will actually kill performance. Thus, to make this
fast, you need a driver local recycle scheme that avoids going through
the page allocator, which makes XDP_DROP and XDP_TX extremely fast.
For the XDP_REDIRECT action (which you seems to be interested in, as
this is needed for AF_XDP), there is a xdp_return_frame() API that can
make this fast.
To avoid every driver inventing their own driver local page-recycle
cache (which many does today), we/I have created the page pool API.
See include/net/page_pool.h, and look at how mlx5 driver uses it
in v4.18 links[1][2][3]. Do notice, that mlx5 ALSO have a driver
recycle scheme on top, which Tariq is working on removing or
generalizing. AND also that mlx5 does not use the DMA mapping feature
that page_pool also provide yet. (Contact me if you want to use
page_pool for handing DMA mapping, we might need to export
__page_pool_clean_page and call it before XDP_PASS action).
[1] https://github.com/torvalds/linux/blob/v4.18/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c#L226
[2] https://github.com/torvalds/linux/blob/v4.18/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c#L255
[3] https://github.com/torvalds/linux/blob/v4.18/drivers/net/ethernet/mellanox/mlx5/core/en_main.c#L598-L618
--
Best regards,
Jesper Dangaard Brouer
MSc.CS, Principal Kernel Engineer at Red Hat
LinkedIn: http://www.linkedin.com/in/brouer
^ permalink raw reply
* Re: [PATCH] knav: qmss: Introduce queue descriptors monitor
From: Gomonovych, Vasyl @ 2018-10-01 9:34 UTC (permalink / raw)
To: w-kwok2, m-karicheri2, davem, grygorii.strashko,
Santosh Shilimkar; +Cc: netdev
In-Reply-To: <20180911211549.12092-1-gomonovych@gmail.com>
Hi, TI experts
During work with rapidio and ethernet driver, we have faced ingress
and egress queue descriptors starvation.
Obvious things were to find which queues were under pressure. Existing
interface knav_queue in debugfs can help debug starvation issue by a
periodic process which cat existing debugfs file.
In some cases process with cat not so precise.
Also, some IP blocks have a dedicated interrupt line for starvation
notification.
But some hardware resource configuration and setup can live Linux in a
situation without available interrupt line for starvation detection.
And because of this reason, I thought about a dedicated monitor for
knav_queue descriptors.
I thought about dedicated kernel thread with timer and ring buffer and
in this case more natural to keep it like
loadable kernel module.
But in other cases we can face ingress starvation on early startup and
in we should be able to start monitor
as soon as possible after the main target platform driver load.
So I have thought about
- starvation on early startup
- starvation later during normal system work
- periodic measurement queue available descriptors
Mainly my question about design for this descriptor monitoring and
first of all do we need it at all.
Regards Vasyl
On Tue, Sep 11, 2018 at 11:16 PM Vasyl Gomonovych <gomonovych@gmail.com> wrote:
>
> Monitor and record available descriptors in knav_qmss queues
> Get amount of available descriptors in free-descriptor queue
> base on event-triggered RX traffic.
> Also monitor free-descriptor queue base on periodic time interval
> in kernel thread.
> To start monitoring available descriptors in queue earlyi,
> module parameters, enable start monitoring in boottime
>
> This queue descriptor monitor helps debugging starvation issue.
> The monitor should help debug queue under traffic pressure
> and can describe the shape of this pressure when a queue
> faced descriptors starvation.
> Monitor helpful for IP blocks which do not have dedicated
> descriptor starvation interrupt like RapidIO IP.
>
> Registration and enable file in debugfs hierarchy
>
> |-/sys/kernel/debug
> |-- knav_qmssm_soc:hwqueue@2a40000
> | |-- 8710
> | | |-- buffer_size
> | | |-- enable
> | | |-- monitor_stats
> | | -- unregister
>
> ---
>
> The current implementation is the first iteration
> and require additional work.
> By this patch I would like to know does this could be
> helpful for other components and continue my work in a right way.
>
> Signed-off-by: Vasyl Gomonovych <vasyl.gomonovych@nokia.com>
> ---
> drivers/net/ethernet/ti/netcp_core.c | 14 +
> drivers/soc/ti/Makefile | 2 +-
> drivers/soc/ti/knav_qmss.h | 3 +
> drivers/soc/ti/knav_qmss_queue.c | 18 +
> drivers/soc/ti/knav_qmssm.c | 1147 ++++++++++++++++++++++++++++++++++
> drivers/soc/ti/knav_qmssm.h | 145 +++++
> include/linux/soc/ti/knav_qmss.h | 19 +-
> 7 files changed, 1346 insertions(+), 2 deletions(-)
> create mode 100644 drivers/soc/ti/knav_qmssm.c
> create mode 100644 drivers/soc/ti/knav_qmssm.h
>
> diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c
> index e40aa3e31af2..d270901c48e1 100644
> --- a/drivers/net/ethernet/ti/netcp_core.c
> +++ b/drivers/net/ethernet/ti/netcp_core.c
> @@ -1592,12 +1592,15 @@ static void netcp_free_navigator_resources(struct netcp_intf *netcp)
> knav_pool_destroy(netcp->tx_pool);
> netcp->tx_pool = NULL;
> }
> +
> + knav_queue_device_control(netcp->rx_queue, KNAV_QUEUE_SET_MONITOR, 0);
> }
>
> static int netcp_setup_navigator_resources(struct net_device *ndev)
> {
> struct netcp_intf *netcp = netdev_priv(ndev);
> struct knav_queue_notify_config notify_cfg;
> + struct knav_queue_monitor_config monitor_cfg;
> struct knav_dma_cfg config;
> u32 last_fdq = 0;
> u8 name[16];
> @@ -1664,6 +1667,9 @@ static int netcp_setup_navigator_resources(struct net_device *ndev)
> knav_queue_disable_notify(netcp->rx_queue);
>
> /* open Rx FDQs */
> + for (i = 0; i < KNAV_DMA_FDQ_PER_CHAN; i++)
> + monitor_cfg.fdq_arg[i] = NULL;
> +
> for (i = 0; i < KNAV_DMA_FDQ_PER_CHAN && netcp->rx_queue_depths[i];
> ++i) {
> snprintf(name, sizeof(name), "rx-fdq-%s-%d", ndev->name, i);
> @@ -1672,8 +1678,16 @@ static int netcp_setup_navigator_resources(struct net_device *ndev)
> ret = PTR_ERR(netcp->rx_fdq[i]);
> goto fail;
> }
> + monitor_cfg.fdq_arg[i] = netcp->rx_fdq[i];
> }
>
> + /* Set monitor for Rx queue */
> + monitor_cfg.fn = knav_qmssm_event_callback;
> + ret = knav_queue_device_control(netcp->rx_queue,
> + KNAV_QUEUE_SET_MONITOR, &monitor_cfg);
> + if (ret)
> + dev_err(netcp->ndev_dev, "fail set qmms %d", netcp->rx_queue_id);
> +
> memset(&config, 0, sizeof(config));
> config.direction = DMA_DEV_TO_MEM;
> config.u.rx.einfo_present = true;
> diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile
> index a22edc0b258a..4375f3c4f013 100644
> --- a/drivers/soc/ti/Makefile
> +++ b/drivers/soc/ti/Makefile
> @@ -3,7 +3,7 @@
> # TI Keystone SOC drivers
> #
> obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o
> -knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o
> +knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o knav_qmssm.o
> obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o
> obj-$(CONFIG_AMX3_PM) += pm33xx.o
> obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o
> diff --git a/drivers/soc/ti/knav_qmss.h b/drivers/soc/ti/knav_qmss.h
> index 3efc47e82973..a05b6174ab3a 100644
> --- a/drivers/soc/ti/knav_qmss.h
> +++ b/drivers/soc/ti/knav_qmss.h
> @@ -289,6 +289,8 @@ struct knav_queue {
> knav_queue_notify_fn notifier_fn;
> void *notifier_fn_arg;
> atomic_t notifier_enabled;
> + struct knav_queue_monitor_config *monitor_cfg;
> + atomic_t monitor_enabled;
> struct rcu_head rcu;
> unsigned flags;
> struct list_head list;
> @@ -313,6 +315,7 @@ struct knav_device {
> struct list_head pdsps;
> struct list_head qmgrs;
> enum qmss_version version;
> + struct knav_qmssm *qmon;
> };
>
> struct knav_range_ops {
> diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c
> index 6755f2af5619..9516bba9261e 100644
> --- a/drivers/soc/ti/knav_qmss_queue.c
> +++ b/drivers/soc/ti/knav_qmss_queue.c
> @@ -30,6 +30,7 @@
> #include <linux/soc/ti/knav_qmss.h>
>
> #include "knav_qmss.h"
> +#include "knav_qmssm.h"
>
> static struct knav_device *kdev;
> static DEFINE_MUTEX(knav_dev_lock);
> @@ -101,6 +102,12 @@ void knav_queue_notify(struct knav_queue_inst *inst)
> continue;
> this_cpu_inc(qh->stats->notifies);
> qh->notifier_fn(qh->notifier_fn_arg);
> +
> + if (atomic_read(&qh->monitor_enabled) == KNAV_QMSSM_DISABLE)
> + continue;
> + if (WARN_ON(!qh->monitor_cfg))
> + continue;
> + qh->monitor_cfg->fn(qh);
> }
> rcu_read_unlock();
> }
> @@ -600,6 +607,7 @@ int knav_queue_device_control(void *qhandle, enum knav_queue_ctrl_cmd cmd,
> {
> struct knav_queue *qh = qhandle;
> struct knav_queue_notify_config *cfg;
> + struct knav_queue_monitor_config *mcfg;
> int ret;
>
> switch ((int)cmd) {
> @@ -628,6 +636,11 @@ int knav_queue_device_control(void *qhandle, enum knav_queue_ctrl_cmd cmd,
> ret = knav_queue_get_count(qh);
> break;
>
> + case KNAV_QUEUE_SET_MONITOR:
> + mcfg = (void *)arg;
> + ret = knav_queue_set_monitor(qh, mcfg);
> + break;
> +
> default:
> ret = -ENOTSUPP;
> break;
> @@ -1874,6 +1887,10 @@ static int knav_queue_probe(struct platform_device *pdev)
> goto err;
> }
>
> + ret = knav_qmssm_register(kdev);
> + if (ret < 0)
> + dev_err(dev, "knav qmssm registration failed\n");
> +
> debugfs_create_file("qmss", S_IFREG | S_IRUGO, NULL, NULL,
> &knav_queue_debug_ops);
> device_ready = true;
> @@ -1891,6 +1908,7 @@ static int knav_queue_probe(struct platform_device *pdev)
> static int knav_queue_remove(struct platform_device *pdev)
> {
> /* TODO: Free resources */
> + knav_qmssm_unregister(kdev);
> pm_runtime_put_sync(&pdev->dev);
> pm_runtime_disable(&pdev->dev);
> return 0;
> diff --git a/drivers/soc/ti/knav_qmssm.c b/drivers/soc/ti/knav_qmssm.c
> new file mode 100644
> index 000000000000..2d249a998819
> --- /dev/null
> +++ b/drivers/soc/ti/knav_qmssm.c
> @@ -0,0 +1,1147 @@
> +/*
> + * Texas Instruments Keystone Navigator Queue Management SubSystem
> + * Queue Managers Monitor Implementation
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + */
> +
> +#include <linux/seq_file.h>
> +#include <linux/debugfs.h>
> +#include <linux/fs.h>
> +#include <linux/list.h>
> +#include <linux/slab.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/rhashtable.h>
> +#include <linux/kthread.h>
> +#include <linux/delay.h>
> +#include <linux/tick.h>
> +#include <linux/ktime.h>
> +#include <linux/io.h>
> +#include <linux/stat.h>
> +#include <linux/moduleparam.h>
> +#include <linux/sched/signal.h>
> +#include "knav_qmssm.h"
> +
> +static char *knav_qmssm_name;
> +module_param(knav_qmssm_name, charp, 0644);
> +MODULE_PARM_DESC(knav_qmssm_name, "Knav queue monitor name");
> +
> +#define MAX_BOOTIME_MQ 4
> +static int knav_qmssm_qid[MAX_BOOTIME_MQ] = {-1, -1, -1, -1};
> +module_param_array(knav_qmssm_qid, int, NULL, 0644);
> +MODULE_PARM_DESC(knav_qmssm_qid, "Predefine knav queue ID for early monitoring start");
> +
> +static u64 knav_qmssm_interval_ms;
> +module_param(knav_qmssm_interval_ms, ullong, 0644);
> +MODULE_PARM_DESC(knav_qmssm_interval_ms, "Knav queue logger interval");
> +
> +static LIST_HEAD(knav_qmssm_list);
> +static DEFINE_MUTEX(knav_qmssm_lock);
> +
> +#define TEMP_BUFF_SIZE 128
> +
> +static const char knav_qmssm_prefix[] = "knav_qmssm";
> +static const char knav_qmssm_register_str[] = "register";
> +static const char knav_qmssm_unregister_str[] = "unregister";
> +static const char knav_qmssm_interval[] = "interval_ms";
> +
> +static const char knav_qmssm_qid_enable[] = "enable";
> +static const char knav_qmssm_qid_wmark[] = "watermark";
> +static const char knav_qmssm_qid_bufsize[] = "buffer_size";
> +static const char knav_qmssm_qid_stats[] = "monitor_stats";
> +
> +#define MONITOR_WMARK_MIN 64
> +#define MONITOR_WMARK_LOW 128
> +#define MONITOR_WMARK_HIGH 256
> +
> +#define MONITOR_INTERVAL_MS (10*1000) /* 10 sec */
> +
> +#define MONITOR_BS 8192
> +#define MONITOR_BS_MAX (64*1024)
> +
> +#define qmssm_dev(qmon) (qmon->kdev->dev)
> +#define mqd_dev(mqd) (qmssm_dev(mqd->qmon))
> +#define data_dev(data) (mqd_dev(data->mqd))
> +#define mqd_kdev(mqd) (mqd->qmon->kdev)
> +
> +#define property(mqd) (mqd->mqe->data->property)
> +
> +#define INT_PTR(x) ((void *)(unsigned long)x)
> +#define PTR_INT(x) ((int)(unsigned long)x)
> +
> +static inline void knav_qmssm_property_dump(struct monitor_queue_dentry *mqd)
> +{
> + dev_dbg(mqd_dev(mqd), "dump: qid %d bs=%d, enable=%d, wmark=[%d %d %d]\n",
> + mqd->qid,
> + property(mqd).buffsize,
> + atomic_read(&property(mqd).enable),
> + property(mqd).wmark[WM_MIN],
> + property(mqd).wmark[WM_LOW],
> + property(mqd).wmark[WM_HIGH]);
> +}
> +
> +static void knav_qmssm_dump(struct knav_qmssm *qmon)
> +{
> + struct monitor_queue_dentry *mqd;
> +
> + rcu_read_lock();
> + list_for_each_entry_rcu(mqd, &qmon->mqlist, list) {
> + if (mqd->mqe)
> + knav_qmssm_property_dump(mqd);
> + }
> + rcu_read_unlock();
> +}
> +
> +static struct knav_queue *knav_qmssm_get_queue(struct knav_device *kdev,
> + unsigned int qid)
> +{
> + struct knav_queue *qh = NULL;
> + struct knav_queue_inst *inst = NULL;
> +
> + if (!kdev)
> + return NULL;
> +
> + if (kdev->base_id <= qid && kdev->base_id + kdev->num_queues > qid) {
> + qid -= kdev->base_id;
> + inst = kdev->instances + (qid << kdev->inst_shift);
> + }
> +
> + if (!inst)
> + return NULL;
> +
> + rcu_read_lock();
> + list_for_each_entry_rcu(qh, &inst->handles, list) {
> + if (knav_queue_device_control(qh, KNAV_QUEUE_GET_ID, 0) == qid)
> + break;
> + }
> + rcu_read_unlock();
> +
> + return qh;
> +}
> +
> +static int knav_qmssm_thread_logger(void *arg)
> +{
> + struct knav_qmssm *qmon;
> + struct monitor_queue_dentry *mqd;
> + struct knav_queue *qh, *q[KNAV_QMSSM_FDQ_PER_CHAN];
> + unsigned int count[KNAV_QMSSM_FDQ_PER_CHAN];
> + unsigned int i = 0;
> + unsigned int qid[KNAV_QMSSM_FDQ_PER_CHAN];
> + ktime_t delay;
> + int ret;
> +
> + qmon = (struct knav_qmssm *) arg;
> +
> + while (!kthread_should_stop()) {
> +
> + rcu_read_lock();
> + list_for_each_entry_rcu(mqd, &qmon->mqlist, list) {
> +
> + qh = mqd->qh;
> + if (!qh || !qh->inst)
> + continue;
> +
> + if (!qh->monitor_cfg) {
> + count[0] = knav_queue_device_control(qh,
> + KNAV_QUEUE_GET_COUNT, 0);
> + dev_info(qmssm_dev(qmon), "%d:%d",
> + mqd->qid, count[0]);
> + continue;
> + }
> +
> + for (i = 0; i < KNAV_QMSSM_FDQ_PER_CHAN; i++) {
> + q[i] = qh->monitor_cfg->fdq_arg[i];
> + count[i] = 0;
> + qid[i] = 0;
> + if (!q[i] || !q[i]->inst)
> + continue;
> +
> + count[i] = knav_queue_device_control(q[i],
> + KNAV_QUEUE_GET_COUNT, 0);
> + qid[i] = knav_queue_device_control(qh,
> + KNAV_QUEUE_GET_ID, 0);
> + }
> + dev_info(qmssm_dev(qmon), "%d:%d %d:%d %d:%d %d:%d",
> + qid[0], count[0], qid[1], count[1],
> + qid[2], count[2], qid[3], count[3]);
> + }
> + rcu_read_unlock();
> +
> + delay = ms_to_ktime(qmon->ilogger.interval_ms);
> + set_current_state(TASK_INTERRUPTIBLE);
> + ret = schedule_hrtimeout(&delay, HRTIMER_MODE_REL);
> + set_current_state(TASK_RUNNING);
> + }
> +
> + return 0;
> +}
> +
> +static int knav_qmssm_logger_start(struct knav_qmssm *qmon)
> +{
> + struct task_struct *thread;
> + char name[TEMP_BUFF_SIZE] = {0};
> + int len = 0;
> + int ret = 0;
> +
> + if (!qmon)
> + return -EINVAL;
> +
> + mutex_lock(&qmon->mqlock);
> +
> + if (qmon->ilogger.thread)
> + goto out;
> +
> + len = snprintf(name, TEMP_BUFF_SIZE, "kthread_%s/%s\n",
> + knav_qmssm_prefix,
> + dev_name(qmssm_dev(qmon)));
> + name[len] = '\0';
> +
> + thread = kthread_run(knav_qmssm_thread_logger, qmon, name);
> +
> + if (IS_ERR(thread)) {
> + ret = PTR_ERR(thread);
> + goto out;
> + }
> +
> + qmon->ilogger.thread = thread;
> +
> +out:
> + mutex_unlock(&qmon->mqlock);
> +
> + return ret;
> +}
> +
> +static void knav_qmssm_logger_stop(struct knav_qmssm *qmon)
> +{
> + int ret;
> +
> + if (!qmon)
> + return;
> +
> + mutex_lock(&qmon->mqlock);
> +
> + if (WARN_ON(!qmon->ilogger.thread)) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + send_sig(SIGTERM, qmon->ilogger.thread, 1);
> + ret = kthread_stop(qmon->ilogger.thread);
> + qmon->ilogger.thread = NULL;
> +
> +out:
> + mutex_unlock(&qmon->mqlock);
> +}
> +
> +static int knav_qmssm_alloc_rb(struct knav_qmssm_qdata *data)
> +{
> + int size;
> +
> + if (!data)
> + return -EINVAL;
> +
> + size = data->property.buffsize;
> + (*data).ring_buffer = ring_buffer_alloc(size, RB_FL_OVERWRITE);
> + if (!data->ring_buffer) {
> + dev_dbg(data_dev(data), "cannot allocate log buffer\n");
> + return -ENOMEM;
> + }
> + dev_dbg(data_dev(data), "ring buffer allocated\n");
> +
> + return 0;
> +}
> +
> +static int knav_qmssm_free_rb(struct knav_qmssm_qdata *data)
> +{
> + if (!data) {
> + dev_warn(data_dev(data), "%s() null data pointer\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (data->ring_buffer) {
> + ring_buffer_free(data->ring_buffer);
> + data->ring_buffer = NULL;
> + dev_dbg(data_dev(data), "ring buffer freed\n");
> + }
> +
> + return 0;
> +}
> +
> +static int knav_qmssm_write_rb(struct ring_buffer *buffer, void *entry,
> + unsigned long size)
> +{
> + int ret = 0;
> +
> + if (!buffer || !entry) {
> + pr_err("%s() rb=%p entry=%p", __func__, buffer, entry);
> + return -EINVAL;
> + }
> +
> + ret = ring_buffer_write(buffer, size, entry);
> + if (ret)
> + pr_err("dropped log event %p size %lu\n", buffer, size);
> +
> + return 0;
> +}
> +
> +static void knav_qmssm_read_rb(struct seq_file *filp,
> + struct knav_qmssm_qdata *data)
> +{
> + struct ring_buffer_event *event;
> + struct knav_qmssm_record_item *entry;
> + struct ring_buffer *rb;
> + int cpu;
> + int found = 1;
> + u64 ts;
> + unsigned long lost;
> + unsigned long rem_nsec;
> +
> + rb = data->ring_buffer;
> +
> + if (!rb) {
> + dev_err(data_dev(data), "%s null data\n", __func__);
> + return;
> + }
> +
> + if (ring_buffer_empty(rb)) {
> + dev_dbg(data_dev(data), "log buffer empty qid %d\n",
> + data->mqd->qid);
> + found = 0;
> + }
> +
> + while (found) {
> +
> + found = 0;
> + for_each_online_cpu(cpu) {
> +
> + event = ring_buffer_consume(rb, cpu, &ts, &lost);
> + if (!event)
> + continue;
> + else {
> + entry = ring_buffer_event_data(event);
> + found = 1;
> + if (!entry)
> + continue;
> +
> + rem_nsec = do_div(ts, 1000000000);
> + seq_printf(filp, "[%5lu.%06lu]\t\t%d\t\t%d\n",
> + (unsigned long)ts, rem_nsec,
> + entry->qid, entry->count);
> + }
> + }
> + }
> +}
> +
> +static void knav_qmssm_write_data(struct knav_qmssm_qdata *data,
> + struct knav_qmssm_record_item *entry)
> +{
> + int ret = 0;
> +
> + if (!data || !entry) {
> + pr_err("error ring buffer write\n");
> + return;
> + }
> +
> + ret = knav_qmssm_write_rb(data->ring_buffer, entry, sizeof(*entry));
> + if (ret) {
> + pr_err("ring buffer error to write qid %d\n", data->mqd->qid);
> + return;
> + }
> +}
> +
> +static void knav_qmssm_start_rb(struct knav_qmssm_qdata *data)
> +{
> + if (!data->ring_buffer)
> + return;
> + ring_buffer_record_on(data->ring_buffer);
> + dev_dbg(data_dev(data), "%s() ring buffer start\n", __func__);
> +}
> +
> +static void knav_qmssm_stop_rb(struct knav_qmssm_qdata *data)
> +{
> + if (!data->ring_buffer)
> + return;
> + ring_buffer_record_off(data->ring_buffer);
> + dev_dbg(data_dev(data), "%s() ring buffer stop\n", __func__);
> +}
> +
> +static int knav_qmssm_start(struct monitor_queue_dentry *mqd)
> +{
> + struct knav_qmssm_qdata *data;
> + int ret = 0;
> +
> + if (!mqd || WARN_ON(!mqd->mqe))
> + return -EINVAL;
> +
> + mutex_lock(&mqd->lock);
> +
> + data = mqd->mqe->data;
> + if (!data)
> + goto out0;
> +
> + if (!(&mqd->mqe->data->property.enable)) //TODO
> + goto out0;
> +
> + if (atomic_read(&property(mqd).enable) == KNAV_QMSSM_ENABLE)
> + goto out0;
> +
> + ret = knav_qmssm_free_rb(data);
> + if (ret)
> + goto out0;
> +
> + ret = knav_qmssm_alloc_rb(data);
> + if (ret)
> + goto out0;
> +
> + knav_qmssm_start_rb(data);
> + ret = knav_queue_enable_monitor(mqd->qh);
> + if (ret) {
> + dev_dbg(mqd_dev(mqd), "cannot start not event-driven queue\n");
> + goto out1;
> + }
> + dev_dbg(mqd_dev(mqd), "%s(%d) success\n", __func__, mqd->qid);
> + atomic_set(&property(mqd).enable, KNAV_QMSSM_ENABLE);
> +
> +out0:
> + mutex_unlock(&mqd->lock);
> + return ret;
> +
> +out1:
> + knav_qmssm_stop_rb(data);
> + knav_qmssm_free_rb(data);
> + mutex_unlock(&mqd->lock);
> + return ret;
> +}
> +
> +static int knav_qmssm_stop(struct monitor_queue_dentry *mqd)
> +{
> + struct knav_qmssm_qdata *data;
> + int ret = 0;
> +
> + if (!mqd)
> + return -EINVAL;
> +
> + if (!mqd->mqe) {
> + dev_err(mqd_dev(mqd), "skip not event-driven queue");
> + return -EINVAL;
> + }
> +
> + mutex_lock(&mqd->lock);
> + data = mqd->mqe->data;
> + if (atomic_read(&property(mqd).enable) == KNAV_QMSSM_DISABLE)
> + goto out;
> +
> + ret = knav_queue_disable_monitor(mqd->qh);
> + if (ret) {
> + dev_dbg(mqd_dev(mqd), "%s(%d) error\n", __func__, mqd->qid);
> + goto out;
> + }
> + knav_qmssm_stop_rb(data);
> + atomic_set(&property(mqd).enable, KNAV_QMSSM_DISABLE);
> + dev_dbg(mqd_dev(mqd), "%s(%d) success\n", __func__, mqd->qid);
> +
> +out:
> + mutex_unlock(&mqd->lock);
> + return ret;
> +}
> +
> +static int knav_qmssm_init_data(struct monitor_queue_dentry **mqd)
> +{
> + struct knav_qmssm_qdata *data;
> + struct knav_qmssm *qmon;
> +
> + if (!*mqd || !(*mqd)->mqe)
> + return -EINVAL;
> +
> + qmon = (*mqd)->qmon;
> +
> + data = devm_kzalloc(qmssm_dev(qmon), sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + (*mqd)->mqe->data = data;
> + data->mqd = *mqd;
> + data->property.buffsize = MONITOR_BS;
> + data->property.wmark[WM_MIN] = MONITOR_WMARK_MIN;
> + data->property.wmark[WM_LOW] = MONITOR_WMARK_LOW;
> + data->property.wmark[WM_HIGH] = MONITOR_WMARK_HIGH;
> + atomic_set(&data->property.enable, KNAV_QMSSM_DISABLE);
> + data->ring_buffer = NULL;
> + memset(&data->lchachee, 0, sizeof(data->lchachee));
> +
> + return 0;
> +}
> +
> +static int knav_qmssm_free_data(struct monitor_queue_dentry *mqd)
> +{
> + struct knav_qmssm_qdata *data;
> +
> + if (!mqd || !mqd->mqe)
> + return -EINVAL;
> +
> + data = mqd->mqe->data;
> + mqd->mqe->data = NULL;
> +
> + knav_qmssm_free_rb(data);
> + devm_kfree(mqd_dev(mqd), data);
> +
> + return 0;
> +}
> +
> +
> +static int knav_qmssm_set_enable(void *d, u64 enable)
> +{
> + struct monitor_queue_dentry *mqd = d;
> + int ret = 0;
> +
> + if (enable == KNAV_QMSSM_ENABLE)
> + ret = knav_qmssm_start(mqd);
> + else if (enable == KNAV_QMSSM_DISABLE)
> + ret = knav_qmssm_stop(mqd);
> +
> + return ret;
> +}
> +
> +static int knav_qmssm_get_enable(void *d, u64 *enable)
> +{
> + struct monitor_queue_dentry *mqd = d;
> + *enable = atomic_read(&property(mqd).enable);
> + return 0;
> +}
> +
> +static int knav_qmssm_set_bufsize(void *d, u64 bufsize)
> +{
> + struct monitor_queue_dentry *mqd = d;
> +
> + mutex_lock(&mqd->lock);
> + if (atomic_read(&property(mqd).enable) == KNAV_QMSSM_ENABLE)
> + goto out;
> + property(mqd).buffsize =
> + bufsize > MONITOR_BS_MAX ? MONITOR_BS_MAX : bufsize;
> +
> +out:
> + mutex_unlock(&mqd->lock);
> + return 0;
> +}
> +
> +static int knav_qmssm_get_bufsize(void *d, u64 *bufsize)
> +{
> + struct monitor_queue_dentry *mqd = d;
> + struct ring_buffer *rb;
> + long size;
> +
> + mutex_lock(&mqd->lock);
> + rb = mqd->mqe->data->ring_buffer;
> + if (rb)
> + size = ring_buffer_size(rb, smp_processor_id());
> + else
> + size = property(mqd).buffsize;
> + *bufsize = size;
> + mutex_unlock(&mqd->lock);
> + return 0;
> +}
> +
> +/**
> + * knav_qmssm_set_watermark() - write watermark vector
> + */
> +static ssize_t knav_qmssm_set_watermark(struct file *filp,
> + const char __user *buffer, size_t count, loff_t *ppos)
> +{
> + struct monitor_queue_dentry *mqd;
> + char buff[TEMP_BUFF_SIZE] = {0};
> + ssize_t len = 0;
> + int num = 0;
> + unsigned int wmark[NR_WATERMARK] = {0};
> + unsigned int i = 0;
> + int ret = count;
> +
> + if (!filp || *ppos != 0)
> + return -EINVAL;
> +
> + mqd = (struct monitor_queue_dentry *)filp->private_data;
> +
> + if (count >= sizeof(buff))
> + return -ENOSPC;
> +
> + mutex_lock(&mqd->lock);
> +
> + len = simple_write_to_buffer(buff, sizeof(buff)-1, ppos, buffer, count);
> +
> + if (len < 0) {
> + ret = len;
> + goto out;
> + }
> +
> + buff[len] = '\n';
> +
> + num = sscanf(buff, "%u %u %u",
> + &wmark[WM_MIN], &wmark[WM_LOW], &wmark[WM_HIGH]);
> + if (num != NR_WATERMARK) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + for (i = 0; i < NR_WATERMARK; ++i) {
> + wmark[i] = wmark[i] < KNAV_QMSSM_WM_MIN ?
> + KNAV_QMSSM_WM_MIN : wmark[i];
> + wmark[i] = wmark[i] > KNAV_QMSSM_WM_MAX ?
> + KNAV_QMSSM_WM_MAX : wmark[i];
> + property(mqd).wmark[i] = wmark[i];
> + }
> +out:
> + mutex_unlock(&mqd->lock);
> + return ret;
> +}
> +
> +/**
> + * knav_qmssm_get_watermark() - read watermark vector
> + */
> +static ssize_t knav_qmssm_get_watermark(struct file *filp,
> + char __user *buffer, size_t count, loff_t *ppos)
> +{
> + struct monitor_queue_dentry *mqd;
> + char buff[TEMP_BUFF_SIZE] = {0};
> + ssize_t len = 0;
> + int ret = 0;
> +
> + if (*ppos != 0 || !filp)
> + return -EINVAL;
> +
> + mqd = (struct monitor_queue_dentry *)filp->private_data;
> +
> + mutex_lock(&mqd->lock);
> + len = snprintf(buff, TEMP_BUFF_SIZE, "%u %u %u\n",
> + property(mqd).wmark[WM_MIN],
> + property(mqd).wmark[WM_LOW],
> + property(mqd).wmark[WM_HIGH]);
> +
> + if (count < strlen(buff)) {
> + ret = -ENOSPC;
> + goto out;
> + }
> + ret = simple_read_from_buffer(buffer, count, ppos, buff, len);
> +out:
> + mutex_unlock(&mqd->lock);
> + return ret;
> +}
> +
> +static ssize_t knav_qmssm_show_statistics(struct seq_file *filp, void *d)
> +{
> + struct monitor_queue_dentry *mqd = filp->private;
> +
> + if (!mqd)
> + return -EINVAL;
> +
> + if (atomic_read(&property(mqd).enable) == KNAV_QMSSM_DISABLE) {
> + seq_printf(filp, "%s to enable use:\n", mqd->qmon->name);
> + seq_printf(filp, "echo 1 > /sys/kernel/debug/%s/%d/enable\n\n",
> + mqd->qmon->name, mqd->qid);
> + }
> +
> + seq_puts(filp, "timestamp\t\t\tfdq number\tsubmit count\n");
> + knav_qmssm_read_rb(filp, mqd->mqe->data);
> +
> + return 0;
> +}
> +
> +static int knav_qmssm_open_statistics(struct inode *inode, struct file *filp)
> +{
> + return single_open(filp, knav_qmssm_show_statistics, inode->i_private);
> +}
> +
> +DEFINE_DEBUGFS_ATTRIBUTE(queue_enable_fops, knav_qmssm_get_enable,
> + knav_qmssm_set_enable, "%llu\n");
> +
> +DEFINE_DEBUGFS_ATTRIBUTE(queue_bufsize_fops, knav_qmssm_get_bufsize,
> + knav_qmssm_set_bufsize, "%llu\n");
> +
> +static const struct file_operations knav_qmssm_watermark_fops = {
> + .owner = THIS_MODULE,
> + .open = simple_open,
> + .read = knav_qmssm_get_watermark,
> + .write = knav_qmssm_set_watermark,
> +};
> +
> +static const struct file_operations knav_qmssm_stats_fops = {
> + .owner = THIS_MODULE,
> + .open = knav_qmssm_open_statistics,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +static inline bool knav_qmssm_int_queue(struct knav_queue *qh)
> +{
> + unsigned int acc;
> +
> + if (!qh)
> + return false;
> +
> + acc = qh->inst->range->flags;
> + if (acc & RANGE_HAS_IRQ)
> + return true;
> +
> + return false;
> +}
> +
> +static int knav_qmssm_register_queue(void *arg, u64 qid)
> +{
> + struct knav_qmssm *qmon = NULL;
> + struct monitor_queue_dentry *mqd = NULL;
> + struct monitor_queue_entry *mqe = NULL;
> + char hwq_monitor_qid[TEMP_BUFF_SIZE] = {0};
> + int ret = 0;
> + int len = 0;
> +
> + if (!arg)
> + return -EINVAL;
> +
> + qmon = (struct knav_qmssm *)arg;
> +
> + if (qmon->kdev->num_queues <= qid)
> + return ret;
> +
> + mutex_lock(&qmon->mqlock);
> +
> + rcu_read_lock();
> + list_for_each_entry_rcu(mqd, &qmon->mqlist, list) {
> + if (qid == mqd->qid) {
> + dev_info(qmssm_dev(qmon),
> + "%llu already registered\n", qid);
> + ret = -EEXIST;
> + break;
> + }
> + }
> + rcu_read_unlock();
> + if (ret)
> + goto out0;
> +
> + mqd = devm_kzalloc(qmssm_dev(qmon), sizeof(*mqd), GFP_KERNEL);
> + if (!mqd) {
> + ret = -ENOMEM;
> + goto out0;
> + }
> + len = snprintf(hwq_monitor_qid, TEMP_BUFF_SIZE, "%llu", qid);
> + hwq_monitor_qid[len] = '\0';
> +
> + mqd->root_qid = debugfs_create_dir(hwq_monitor_qid, qmon->mq_root);
> + mqd->qid = qid;
> + mqd->qmon = qmon;
> + mutex_init(&mqd->lock);
> + mqd->mqe = NULL;
> + mqd->qh = knav_qmssm_get_queue(qmon->kdev, qid);
> + if (!mqd->qh)
> + goto out1;
> +
> + if (knav_qmssm_int_queue(mqd->qh)) {
> +
> + mqe = devm_kzalloc(qmssm_dev(qmon), sizeof(*mqe), GFP_KERNEL);
> + if (!mqe)
> + goto out1;
> +
> + mqe->enable = debugfs_create_file(knav_qmssm_qid_enable,
> + 0660, mqd->root_qid, mqd, &queue_enable_fops);
> + mqe->wmark = debugfs_create_file(knav_qmssm_qid_wmark,
> + 0660, mqd->root_qid, mqd, &knav_qmssm_watermark_fops);
> + mqe->bufsize = debugfs_create_file(knav_qmssm_qid_bufsize,
> + 0660, mqd->root_qid, mqd, &queue_bufsize_fops);
> + mqe->monitor_stats = debugfs_create_file(knav_qmssm_qid_stats,
> + 0440, mqd->root_qid, mqd, &knav_qmssm_stats_fops);
> +
> + mqd->mqe = mqe;
> +
> + ret = knav_qmssm_init_data(&mqd);
> + if (ret)
> + goto out2;
> + }
> +
> + list_add_rcu(&mqd->list, &qmon->mqlist);
> +
> + knav_qmssm_dump(qmon);
> +
> + mutex_unlock(&qmon->mqlock);
> +
> + if (!qmon->ilogger.thread) {
> + ret = knav_qmssm_logger_start(qmon);
> + if (ret)
> + dev_err(qmssm_dev(qmon), "cannot run monitor thread\n");
> + }
> +
> + dev_dbg(qmssm_dev(qmon), "%s(%d)\n", __func__, mqd->qid);
> +
> + return PTR_INT(mqd);
> +
> +out2:
> + devm_kfree(qmssm_dev(qmon), mqe);
> +
> +out1:
> + debugfs_remove_recursive(mqd->root_qid);
> + devm_kfree(qmssm_dev(qmon), mqd);
> + ret = -EINVAL;
> + dev_err(qmssm_dev(qmon), "cannot initialize monitor data\n");
> +
> +out0:
> + mutex_unlock(&qmon->mqlock);
> + return ret;
> +}
> +
> +static int knav_qmssm_unregister_queue_item(struct knav_qmssm *qmon,
> + struct monitor_queue_dentry *mqd)
> +{
> + int ret = 0;
> +
> + list_del_rcu(&mqd->list);
> + synchronize_rcu();
> +
> + if (!mqd->mqe) {
> + dev_info(mqd_dev(mqd), "free not event-driven queue");
> + goto out;
> + }
> +
> + ret = knav_qmssm_stop(mqd);
> + if (ret)
> + dev_err(qmssm_dev(qmon), "error stop monitoring\n");
> + dev_info(qmssm_dev(qmon), "stop event-driven queue\n");
> +
> + ret = knav_qmssm_free_data(mqd);
> + if (ret)
> + dev_err(qmssm_dev(qmon), "error free monitoring data\n");
> +
> + debugfs_remove_recursive(mqd->root_qid);
> +
> + devm_kfree(qmssm_dev(qmon), mqd->mqe);
> +
> + dev_dbg(qmssm_dev(qmon), "free event-driven queue %d", mqd->qid);
> +
> +out:
> + devm_kfree(qmssm_dev(qmon), mqd);
> +
> + return ret;
> +}
> +
> +static int knav_qmssm_unregister_queue(void *arg, u64 qid)
> +{
> + struct knav_qmssm *qmon = NULL;
> + struct monitor_queue_dentry *mqd = NULL;
> + struct list_head *curr, *next;
> +
> + if (!arg)
> + return -ENOENT;
> +
> + qmon = (struct knav_qmssm *)arg;
> +
> + mutex_lock(&qmon->mqlock);
> +
> + if (list_empty(&qmon->mqlist)) {
> + dev_dbg(qmssm_dev(qmon), "no queues under monitoring\n");
> + goto out;
> + }
> +
> + list_for_each_safe(curr, next, &qmon->mqlist) {
> +
> + mqd = list_entry(curr, struct monitor_queue_dentry, list);
> + if (mqd->qid != qid)
> + continue;
> +
> + knav_qmssm_unregister_queue_item(qmon, mqd);
> + break;
> + }
> +
> + dev_dbg(qmssm_dev(qmon), "queue %llu unregistered\n", qid);
> +
> +out:
> + mutex_unlock(&qmon->mqlock);
> +
> + if (list_empty(&qmon->mqlist))
> + knav_qmssm_logger_stop(qmon);
> +
> + return 0;
> +}
> +
> +static int knav_qmssm_setup_parm_base_queue(struct knav_qmssm *qmon)
> +{
> + struct monitor_queue_dentry *mqd;
> + int i = 0, ret = 0;
> +
> + dev_dbg(qmssm_dev(qmon), "%s %s %llu", __func__,
> + knav_qmssm_name,
> + knav_qmssm_interval_ms);
> +
> + if (!knav_qmssm_name || strcmp(qmon->name, knav_qmssm_name))
> + return 0;
> +
> + qmon->ilogger.interval_ms = knav_qmssm_interval_ms;
> +
> + for (i = 0; i < MAX_BOOTIME_MQ && knav_qmssm_qid[i] > 0; i++) {
> + dev_dbg(qmssm_dev(qmon), "register boottime queue %d monitor %s\n",
> + knav_qmssm_qid[i], knav_qmssm_name);
> + ret = knav_qmssm_register_queue(qmon, knav_qmssm_qid[i]);
> + mqd = INT_PTR(ret);
> + if (IS_ERR(mqd))
> + continue;
> + knav_qmssm_start(mqd);
> + }
> +
> + return 0;
> +}
> +
> +static int knav_qmssm_set_interval(void *arg, u64 interval)
> +{
> + struct knav_qmssm *qmon = arg;
> +
> + qmon->ilogger.interval_ms = interval;
> +
> + return 0;
> +}
> +
> +static int knav_qmssm_get_interval(void *arg, u64 *interval)
> +{
> + struct knav_qmssm *qmon = arg;
> +
> + *interval = qmon->ilogger.interval_ms;
> +
> + return 0;
> +}
> +
> +DEFINE_DEBUGFS_ATTRIBUTE(knav_register_queue_fops,
> + NULL, knav_qmssm_register_queue, "%llu\n");
> +
> +DEFINE_DEBUGFS_ATTRIBUTE(knav_unregister_queue_fops,
> + NULL, knav_qmssm_unregister_queue, "%llu\n");
> +
> +DEFINE_DEBUGFS_ATTRIBUTE(knav_interval_legger_fops,
> + knav_qmssm_get_interval, knav_qmssm_set_interval, "%llu\n");
> +
> +/**
> + * knav_qmssm_event_callback() - Main monitor handler callback
> + */
> +void knav_qmssm_event_callback(void *arg)
> +{
> + struct knav_queue *qh;
> + struct knav_queue *q_submit;
> + struct knav_device *kdev;
> + struct knav_qmssm *qmon;
> + struct monitor_queue_dentry *mqd;
> + struct knav_qmssm_record_item itm;
> +
> + int count = 0;
> + int cdiff = 0, pdiff = 0;
> + uint16_t qid = 0;
> + int i = 0, j = 0;
> +
> + qh = (struct knav_queue *)arg;
> + kdev = qh->inst->kdev;
> + qmon = kdev->qmon;
> + qid = knav_queue_device_control(qh, KNAV_QUEUE_GET_ID, 0);
> + if (!qh->monitor_cfg)
> + return;
> +
> + rcu_read_lock();
> + list_for_each_entry_rcu(mqd, &qmon->mqlist, list) {
> +
> + if (mqd->qid != qid)
> + continue;
> +
> + for (i = 0; i < KNAV_QMSSM_FDQ_PER_CHAN; i++) {
> +
> + if (!qh->monitor_cfg->fdq_arg[i])
> + break;
> +
> + q_submit = qh->monitor_cfg->fdq_arg[i];
> + itm.count = knav_queue_device_control(q_submit,
> + KNAV_QUEUE_GET_COUNT, 0);
> + count = mqd->mqe->data->lchachee[i].count;
> +
> + for (j = WM_MIN; j < NR_WATERMARK; j++) {
> +
> + cdiff = property(mqd).wmark[j] - itm.count;
> + pdiff = property(mqd).wmark[j] - count;
> +
> + if (!((cdiff < 0 && pdiff > 0) ||
> + (cdiff > 0 && pdiff < 0)))
> + continue;
> +
> + itm.qid = knav_queue_device_control(q_submit,
> + KNAV_QUEUE_GET_ID, 0);
> + knav_qmssm_write_data(mqd->mqe->data, &itm);
> + memcpy(&mqd->mqe->data->lchachee[i],
> + &itm, sizeof(itm));
> + }
> + }
> + }
> + rcu_read_unlock();
> +}
> +EXPORT_SYMBOL(knav_qmssm_event_callback);
> +
> +int knav_queue_enable_monitor(struct knav_queue *qh)
> +{
> + if (!qh || WARN_ON(!qh->monitor_cfg))
> + return -EINVAL;
> +
> + atomic_set(&qh->monitor_enabled, KNAV_QMSSM_ENABLE);
> +
> + return 0;
> +}
> +
> +int knav_queue_disable_monitor(struct knav_queue *qh)
> +{
> + if (!qh || WARN_ON(!qh->monitor_cfg))
> + return -EINVAL;
> +
> + atomic_set(&qh->monitor_enabled, KNAV_QMSSM_DISABLE);
> +
> + return 0;
> +}
> +
> +int knav_queue_set_monitor(struct knav_queue *qh,
> + struct knav_queue_monitor_config *cfg)
> +{
> + int i = 0, j = 0, k = 0;
> + unsigned int qid;
> + bool diff;
> +
> + if (!qh)
> + return -EINVAL;
> +
> + if (!knav_qmssm_int_queue(qh))
> + return -ENOTSUPP;
> +
> + knav_queue_disable_monitor(qh);
> +
> + if (!cfg && qh->monitor_cfg) {
> + kzfree(qh->monitor_cfg);
> + return 0;
> + }
> +
> + qh->monitor_cfg = kzalloc(sizeof(*(qh->monitor_cfg)), GFP_KERNEL);
> + if (!qh->monitor_cfg)
> + return -ENOMEM;
> +
> + qh->monitor_cfg->fn = cfg->fn;
> + for (i = 0; i < KNAV_QMSSM_FDQ_PER_CHAN; i++) {
> +
> + if (!cfg->fdq_arg[i])
> + continue;
> +
> + diff = true;
> + for (j = 0; j < i; j++)
> + if (cfg->fdq_arg[i] == qh->monitor_cfg->fdq_arg[j]) {
> + diff = false;
> + break;
> + }
> +
> + if (diff)
> + qh->monitor_cfg->fdq_arg[k++] = cfg->fdq_arg[i];
> + }
> +
> + qid = knav_queue_device_control(qh, KNAV_QUEUE_GET_ID, 0);
> + dev_dbg(qh->inst->kdev->dev, "%s %d [%p %p %p %p]", __func__, qid,
> + qh->monitor_cfg->fdq_arg[0],
> + qh->monitor_cfg->fdq_arg[1],
> + qh->monitor_cfg->fdq_arg[2],
> + qh->monitor_cfg->fdq_arg[3]);
> +
> + return 0;
> +}
> +
> +void knav_qmssm_unregister(struct knav_device *kdev)
> +{
> + struct list_head *cd, *nd;
> + struct list_head *cq, *nq;
> + struct knav_qmssm *qmon;
> + struct monitor_queue_dentry *mqd;
> +
> + if (WARN_ON(!kdev))
> + return;
> +
> + mutex_lock(&knav_qmssm_lock);
> +
> + if (list_empty(&knav_qmssm_list))
> + goto out;
> +
> + list_for_each_safe(cd, nd, &knav_qmssm_list) {
> +
> + qmon = list_entry(cd, struct knav_qmssm, list);
> + if (qmon->kdev != kdev)
> + continue;
> +
> + list_for_each_safe(cq, nq, &qmon->mqlist) {
> + mqd = list_entry(cq, struct monitor_queue_dentry, list);
> + knav_qmssm_unregister_queue_item(qmon, mqd);
> + }
> +
> + debugfs_remove_recursive(qmon->mq_root);
> + list_del(cd);
> + kfree(qmon->name);
> + devm_kfree(qmssm_dev(qmon), qmon);
> + }
> +out:
> + mutex_unlock(&knav_qmssm_lock);
> +}
> +EXPORT_SYMBOL(knav_qmssm_unregister);
> +
> +int knav_qmssm_register(struct knav_device *kdev)
> +{
> + struct knav_qmssm *qmon = NULL;
> + int ret = 0;
> + int len = 0;
> +
> + char buff[TEMP_BUFF_SIZE] = {0};
> +
> + if (WARN_ON(!kdev))
> + return -ENODEV;
> +
> + mutex_lock(&knav_qmssm_lock);
> +
> + qmon = devm_kzalloc(kdev->dev, sizeof(*qmon), GFP_KERNEL);
> + if (!qmon) {
> + ret = -ENOSPC;
> + goto out;
> + }
> +
> + qmon->kdev = kdev;
> +
> + len = snprintf(buff, TEMP_BUFF_SIZE, "%s_%s", knav_qmssm_prefix,
> + dev_name(kdev->dev));
> + buff[len] = '\0';
> + qmon->name = kstrdup(buff, GFP_KERNEL);
> + if (!qmon->name) {
> + devm_kfree(kdev->dev, qmon);
> + goto out;
> + }
> +
> + qmon->mq_root = debugfs_create_dir(buff, NULL);
> +
> + qmon->mq_register = debugfs_create_file(knav_qmssm_register_str,
> + 0220, qmon->mq_root, qmon, &knav_register_queue_fops);
> +
> + qmon->mq_unregister = debugfs_create_file(knav_qmssm_unregister_str,
> + 0220, qmon->mq_root, qmon, &knav_unregister_queue_fops);
> +
> + qmon->ilogger.mq_interval = debugfs_create_file(knav_qmssm_interval,
> + 0220, qmon->mq_root, qmon, &knav_interval_legger_fops);
> +
> + qmon->ilogger.interval_ms = MONITOR_INTERVAL_MS;
> +
> + INIT_LIST_HEAD(&qmon->mqlist);
> + mutex_init(&qmon->mqlock);
> + list_add(&qmon->list, &knav_qmssm_list);
> + kdev->qmon = qmon;
> + dev_info(kdev->dev, "register %s\n", qmon->name);
> +
> + knav_qmssm_setup_parm_base_queue(qmon);
> +
> +out:
> + mutex_unlock(&knav_qmssm_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(knav_qmssm_register);
> diff --git a/drivers/soc/ti/knav_qmssm.h b/drivers/soc/ti/knav_qmssm.h
> new file mode 100644
> index 000000000000..5360ae9eca73
> --- /dev/null
> +++ b/drivers/soc/ti/knav_qmssm.h
> @@ -0,0 +1,145 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Texas Instruments Keystone Navigator Queue Management SubSystem
> + * Queue Managers Monitor Header
> + */
> +
> +#ifndef __KNAV_QMSSM_H
> +#define __KNAV_QMSSM_H
> +
> +#include <linux/rbtree.h>
> +#include <linux/rhashtable.h>
> +#include <linux/ring_buffer.h>
> +#include <linux/soc/ti/knav_qmss.h>
> +#include "knav_qmss.h"
> +
> +enum knav_mqmss_watermark {
> + WM_MIN,
> + WM_LOW,
> + WM_HIGH,
> + NR_WATERMARK
> +};
> +
> +#define KNAV_QMSSM_WM_MIN 1
> +#define KNAV_QMSSM_WM_MAX 4096
> +
> +#define KNAV_QMSSM_ENABLE 1
> +#define KNAV_QMSSM_DISABLE 0
> +
> +/*
> + * knav_qmssm_record_item - statistics entry descriptor
> + * qid - queue number
> + * count - keep amount of available descriptors in a hardware queue qid
> + */
> +struct knav_qmssm_record_item {
> + uint16_t qid;
> + uint16_t count;
> +} __packed;
> +
> +/*
> + * knav_qmssm_queue_property - describe property for queue under monitoring
> + * new features and functionality could be added here
> + * wmark - watermakr vector
> + * enable - enable tracing
> + * buffsize - sizeof buffer
> + */
> +struct knav_qmssm_queue_property {
> + atomic_t enable;
> + unsigned int wmark[NR_WATERMARK];
> + unsigned int buffsize;
> +};
> +
> +/*
> + * knav_qmssm_qdata - logical data struct to get/set property for queue
> + * property - property for this knav queue entry
> + * ring_buffer - buffer with collected statistics/logs
> + * lchachee - last cache entry keep entry from rb to calc logging threshold
> + * mqd - pointer to relative queue dentry
> + */
> +struct knav_qmssm_qdata {
> + struct knav_qmssm_queue_property property;
> + struct ring_buffer *ring_buffer;
> + struct knav_qmssm_record_item lchachee[KNAV_QMSSM_FDQ_PER_CHAN];
> + struct monitor_queue_dentry *mqd;
> +};
> +
> +/*
> + * struct monitor_queue_entry - descride extended entry
> + * wmark - dentry represent watermark file
> + * enable - interface to enable/disable statistics collection
> + * bufsize - change buffer size for current queue statistics collection
> + * monitor_statas - show collected statistics
> + * data - pointer to relative data
> + */
> +struct monitor_queue_entry {
> + struct dentry *wmark;
> + struct dentry *enable;
> + struct dentry *bufsize;
> + struct dentry *monitor_stats;
> + struct knav_qmssm_qdata *data;
> +};
> +
> +/*
> + * monitor_queue_dentry - struct represent item for registered queue
> + * list - link all registered queues for monitoring for current device
> + * qid - queue id number
> + * qh - keep knav queue
> + * lock - protect property access
> + * root_qid - root dentry for queue
> + * qmon - point to parent monitor device
> + */
> +struct monitor_queue_dentry {
> + struct list_head list;
> + unsigned int qid;
> + struct knav_queue *qh;
> + struct mutex lock;
> + struct dentry *root_qid;
> + struct monitor_queue_entry *mqe;
> + struct knav_qmssm *qmon;
> +};
> +
> +/*
> + * struct knav_qmssm_ilogger - interval logger periodic interval monitor
> + * thread - kthread descriptor
> + * mq_interval - dentry descriptor for interval
> + * interval_ms - interval define monitor thread work cycle
> + */
> +struct knav_qmssm_ilogger {
> + struct task_struct *thread;
> + struct dentry *mq_interval;
> + u64 interval_ms;
> +};
> +
> +/*
> + * knav_qmssm - describe monitor for hardware queue device
> + * hdev - device which is under monitoring
> + * name - monitor instance name
> + * mqlist - monitoring queues list queue_dentry HEAD is a list to keep queues
> + * mqlock - protect for each list mqd for current device monitor
> + * list - link hardware queue devices monitors in a list
> + * mq_root - hwq_monitor_%device_name% root debugfs dentry for monitored device
> + * mq_register - interface for new queue register to monitor
> + * mq_unregister - interface for queue unregister and stop monitoring
> + * ilogger - interval thread logger for monitor device
> + */
> +struct knav_qmssm {
> + struct knav_device *kdev;
> + unsigned char *name;
> + struct list_head mqlist;
> + struct mutex mqlock;
> + struct list_head list;
> + struct dentry *mq_root;
> + struct dentry *mq_register;
> + struct dentry *mq_unregister;
> + struct knav_qmssm_ilogger ilogger;
> +};
> +
> +int knav_queue_enable_monitor(struct knav_queue *qh);
> +int knav_queue_disable_monitor(struct knav_queue *qh);
> +int knav_queue_set_monitor(struct knav_queue *qh,
> + struct knav_queue_monitor_config *mcfg);
> +
> +int knav_qmssm_register(struct knav_device *kdev);
> +void knav_qmssm_unregister(struct knav_device *kdev);
> +
> +#endif /*__KNAV_QMSSM_H*/
> diff --git a/include/linux/soc/ti/knav_qmss.h b/include/linux/soc/ti/knav_qmss.h
> index 9745df6ed9d3..cc7cf27f58b3 100644
> --- a/include/linux/soc/ti/knav_qmss.h
> +++ b/include/linux/soc/ti/knav_qmss.h
> @@ -34,6 +34,8 @@
> /* queue flags */
> #define KNAV_QUEUE_SHARED 0x0001 /* Queue can be shared */
>
> +#define KNAV_QMSSM_FDQ_PER_CHAN 4
> +
> /**
> * enum knav_queue_ctrl_cmd - queue operations.
> * @KNAV_QUEUE_GET_ID: Get the ID number for an open queue
> @@ -49,7 +51,9 @@ enum knav_queue_ctrl_cmd {
> KNAV_QUEUE_SET_NOTIFIER,
> KNAV_QUEUE_ENABLE_NOTIFY,
> KNAV_QUEUE_DISABLE_NOTIFY,
> - KNAV_QUEUE_GET_COUNT
> + KNAV_QUEUE_GET_COUNT,
> + KNAV_QUEUE_SET_MONITOR,
> + KNAV_QUEUE_ENABLE_MONITOR
> };
>
> /* Queue notifier callback prototype */
> @@ -65,6 +69,19 @@ struct knav_queue_notify_config {
> void *fn_arg;
> };
>
> +/* Queue monitor callback prototype */
> +typedef void (*knav_queue_monitor_fn)(void *arg);
> +
> +/**
> + * struct knav_queue_moncfg: Monitor configuration
> + * @fn: Monitor function
> + * @fdq_arg: Monitor fdq
> + */
> +struct knav_queue_monitor_config {
> + knav_queue_monitor_fn fn;
> + void *fdq_arg[KNAV_QMSSM_FDQ_PER_CHAN];
> +};
> +
> void *knav_queue_open(const char *name, unsigned id,
> unsigned flags);
> void knav_queue_close(void *qhandle);
> --
> 2.14.1
>
--
Доброї вам пори дня.
^ permalink raw reply
* Re: [PATCH] gpiolib: Fix incorrect use of find_next_zero_bit()
From: Linus Walleij @ 2018-10-01 9:37 UTC (permalink / raw)
To: Janusz Krzysztofik
Cc: Miguel Ojeda Sandonis, Peter Korsgaard, Peter Rosin, Ulf Hansson,
Andrew Lunn, Florian Fainelli, David S. Miller, Dominik Brodowski,
Greg KH, kishon, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Hartmut Knaack, Peter Meerwald, Jiri Slaby,
Willy Tarreau, Geert Uytterhoeven, Sebastien Bourdelin,
Lukas Wunner <luka
In-Reply-To: <20180929122022.6825-1-jmkrzyszt@gmail.com>
On Sat, Sep 29, 2018 at 2:19 PM Janusz Krzysztofik <jmkrzyszt@gmail.com> wrote:
> Commit b17566a6b08b ("gpiolib: Implement fast processing path in
> get/set array"), already fixed to some extent with commit 5d581d7e8cdc
> ("gpiolib: Fix missing updates of bitmap index"), introduced a new mode
> of processing bitmaps where bits applicable for fast bitmap processing
> path are supposed to be skipped while iterating bits which don't apply.
> Unfortunately, find_next_zero_bit() function supposed to skip over
> those fast bits is always called with a 'start' argument equal to an
> index of last zero bit found and returns that index value again an
> again, causing an infinite loop.
>
> Fix it by incrementing the index uncoditionally before
> find_next_zero_bit() is optionally called.
>
> Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Signed-off-by: Janusz Krzysztofik <jmkrzyszt@gmail.com>
Patch applied with Marek's Tested-by.
Thanks to both of you for digging in and fixing this up!
Now we are in good shape for the v4.20 cycle :)
Yours,
Linus Walleij
^ permalink raw reply
* Re: [net-next, PATCH 1/2, v3] net: socionext: different approach on DMA
From: Ilias Apalodimas @ 2018-10-01 9:44 UTC (permalink / raw)
To: Jesper Dangaard Brouer
Cc: netdev, jaswinder.singh, ard.biesheuvel, masami.hiramatsu, arnd,
bjorn.topel, magnus.karlsson, daniel, ast, jesus.sanchez-palencia,
vinicius.gomes, makita.toshiaki, Tariq Toukan, Tariq Toukan
In-Reply-To: <20181001112631.4a1fbb62@redhat.com>
On Mon, Oct 01, 2018 at 11:26:31AM +0200, Jesper Dangaard Brouer wrote:
>
> On Sat, 29 Sep 2018 14:28:01 +0300 Ilias Apalodimas <ilias.apalodimas@linaro.org> wrote:
>
> > +static void *netsec_alloc_rx_data(struct netsec_priv *priv,
> > + dma_addr_t *dma_handle, u16 *desc_len)
> > +{
> > + size_t len = priv->ndev->mtu + ETH_HLEN + 2 * VLAN_HLEN + NET_SKB_PAD +
> > + NET_IP_ALIGN;
> > + dma_addr_t mapping;
> > + void *buf;
> > +
> > + len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
> > + len = SKB_DATA_ALIGN(len);
> > +
> > + buf = napi_alloc_frag(len);
>
> Using napi_alloc_frag here ^^^^
>
> > + if (!buf)
> > + return NULL;
> > +
> > + mapping = dma_map_single(priv->dev, buf, len, DMA_FROM_DEVICE);
> > + if (unlikely(dma_mapping_error(priv->dev, mapping)))
> > + goto err_out;
> > +
> > + *dma_handle = mapping;
> > + *desc_len = len;
> > +
> > + return buf;
> > +
> > +err_out:
> > + skb_free_frag(buf);
> > + return NULL;
> > +}
>
> Hmmm, you are using napi_alloc_frag() in above code, which behind
> your-back allocates order-3 pages (32 Kbytes memory in 8 order-0 pages).
>
> This violates at-least two XDP principals:
>
> #1: You are NOT using order-0 page based allocations for XDP.
>
> Notice, I'm not saying 1-page per packet, as ixgbe + i40e violated
> this, and it is now "per-practical-code-example" acceptable to split up
> the order-0 page, and store two RX frames per order-0 page (4096 bytes).
> (To make this fit you have to reduce XDP_HEADROOM to 192 bytes, which
> killed the idea of placing the SKB in this area).
Yes i saw the Intel implementation. I just thought it wasn't worth the hassle
for an 1gbit interface (but wasn't aware it violates and XDP principle).
I also noticed that Netronome(and others) are allocating 1 page per packet
when using XDP
>
> #2: You have allocations on the XDP fast-path.
>
> The REAL secret behind the XDP performance is to avoid allocations on
> the fast-path. While I just told you to use the page-allocator and
> order-0 pages, this will actually kill performance. Thus, to make this
> fast, you need a driver local recycle scheme that avoids going through
> the page allocator, which makes XDP_DROP and XDP_TX extremely fast.
> For the XDP_REDIRECT action (which you seems to be interested in, as
> this is needed for AF_XDP), there is a xdp_return_frame() API that can
> make this fast.
I had an initial implementation that did exactly that (that's why you the
dma_sync_single_for_cpu() -> dma_unmap_single_attrs() is there). In the case
of AF_XDP isn't that introducing a 'bottleneck' though? I mean you'll feed fresh
buffers back to the hardware only when your packets have been processed from
your userspace application
>
> To avoid every driver inventing their own driver local page-recycle
> cache (which many does today), we/I have created the page pool API.
> See include/net/page_pool.h, and look at how mlx5 driver uses it
> in v4.18 links[1][2][3]. Do notice, that mlx5 ALSO have a driver
> recycle scheme on top, which Tariq is working on removing or
> generalizing. AND also that mlx5 does not use the DMA mapping feature
> that page_pool also provide yet. (Contact me if you want to use
> page_pool for handing DMA mapping, we might need to export
> __page_pool_clean_page and call it before XDP_PASS action).
Ok i'll have a look on that and let you know. i
P.S : A few months back we reported that Chelsio is using a different
'memory scheme' for incoming packets. Essentially they just feed the hardware
with unstructred pages and it decides were to place
the packet. Maybe that's worth considering for the page pool API?
>
>
> [1] https://github.com/torvalds/linux/blob/v4.18/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c#L226
> [2] https://github.com/torvalds/linux/blob/v4.18/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c#L255
> [3] https://github.com/torvalds/linux/blob/v4.18/drivers/net/ethernet/mellanox/mlx5/core/en_main.c#L598-L618
Thanks
/Ilias
^ permalink raw reply
* Re: [PATCH net-next 2/5] net: phy: mscc: Add EEE init sequence
From: Florian Fainelli @ 2018-10-01 16:27 UTC (permalink / raw)
To: Quentin Schulz
Cc: davem, andrew, allan.nielsen, linux-kernel, netdev,
thomas.petazzoni, Raju Lakkaraju
In-Reply-To: <20181001085136.qebnqu5kr3d5rgej@qschulz>
On 10/01/2018 01:51 AM, Quentin Schulz wrote:
> Hi Florian,
>
> On Fri, Sep 14, 2018 at 07:21:09PM -0700, Florian Fainelli wrote:
>>
>>
>> On 09/14/18 01:33, Quentin Schulz wrote:
>>> From: Raju Lakkaraju <Raju.Lakkaraju@microchip.com>
>>>
>>> Microsemi PHYs (VSC 8530/31/40/41) need to update the Energy Efficient
>>> Ethernet initialization sequence.
>>> In order to avoid certain link state errors that could result in link
>>> drops and packet loss, the physical coding sublayer (PCS) must be
>>> updated with settings related to EEE in order to improve performance.
>>>
>>> Signed-off-by: Raju Lakkaraju <Raju.Lakkaraju@microchip.com>
>>> Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com>
>>> ---
>>
>> [snip]
>>
>>> + vsc85xx_tr_write(phydev, 0x0f82, 0x0012b00a);
>>
>> Can you just make this an array of register + value pair? That would be
>
> Sure, I'll.
>
>> less error prone in case you need to update that sequence in the future.
>>
>
> I'm curious about the kind of errors you're worrying about or have
> experienced. Do you have any particular example or thought in mind?
Since this is just a completely non documented sequence likely given
as-is by the vendor, there could be in the future an arbitrary number of
changes made to that sequence because reasons. It seems to me that
putting that sequence in an array, instead of having to produce the
right sequence of calls, inlined in the source, is more manageable, and
will lead to an easier process if back porting/forward porting is necessary.
--
Florian
^ permalink raw reply
* Re: [net-next, PATCH 1/2, v3] net: socionext: different approach on DMA
From: Ilias Apalodimas @ 2018-10-01 9:56 UTC (permalink / raw)
To: Jesper Dangaard Brouer
Cc: netdev, jaswinder.singh, ard.biesheuvel, masami.hiramatsu, arnd,
bjorn.topel, magnus.karlsson, daniel, ast, jesus.sanchez-palencia,
vinicius.gomes, makita.toshiaki, Tariq Toukan, Tariq Toukan
In-Reply-To: <20181001094450.GA24329@apalos>
> > #2: You have allocations on the XDP fast-path.
> >
> > The REAL secret behind the XDP performance is to avoid allocations on
> > the fast-path. While I just told you to use the page-allocator and
> > order-0 pages, this will actually kill performance. Thus, to make this
> > fast, you need a driver local recycle scheme that avoids going through
> > the page allocator, which makes XDP_DROP and XDP_TX extremely fast.
> > For the XDP_REDIRECT action (which you seems to be interested in, as
> > this is needed for AF_XDP), there is a xdp_return_frame() API that can
> > make this fast.
> I had an initial implementation that did exactly that (that's why you the
> dma_sync_single_for_cpu() -> dma_unmap_single_attrs() is there). In the case
> of AF_XDP isn't that introducing a 'bottleneck' though? I mean you'll feed fresh
> buffers back to the hardware only when your packets have been processed from
> your userspace application
Just a clarification here. This is the case if ZC is implemented. In my case
the buffers will be 'ok' to be passed back to the hardware once the use
userspace payload has been copied by xdp_do_redirect()
/Ilias
^ permalink raw reply
* Re: [PATCH net-next] net: mscc: allow extracting the FCS into the skb
From: Florian Fainelli @ 2018-10-01 16:35 UTC (permalink / raw)
To: Antoine Tenart, davem
Cc: netdev, linux-kernel, thomas.petazzoni, alexandre.belloni,
quentin.schulz, allan.nielsen
In-Reply-To: <20181001095714.26024-1-antoine.tenart@bootlin.com>
On 10/01/2018 02:57 AM, Antoine Tenart wrote:
> This patch adds support for the NETIF_F_RXFCS feature in the Mscc
> Ethernet driver. This feature is disabled by default and allow an user
> to request the driver not to drop the FCS and to extract it into the skb
> for debugging purposes.
>
> Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
> ---
> drivers/net/ethernet/mscc/ocelot.c | 2 +-
> drivers/net/ethernet/mscc/ocelot_board.c | 7 ++++++-
> 2 files changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index 1a4f2bb48ead..eb5119e7e60b 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -1606,7 +1606,7 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
> dev->ethtool_ops = &ocelot_ethtool_ops;
> dev->switchdev_ops = &ocelot_port_switchdev_ops;
>
> - dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
> + dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS;
> dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
>
> memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN);
> diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c
> index 3cdf63e35b53..245452a0f244 100644
> --- a/drivers/net/ethernet/mscc/ocelot_board.c
> +++ b/drivers/net/ethernet/mscc/ocelot_board.c
> @@ -126,11 +126,16 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
> len += sz;
> } while (len < buf_len);
>
> - /* Read the FCS and discard it */
> + /* Read the FCS */
> sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
> /* Update the statistics if part of the FCS was read before */
> len -= ETH_FCS_LEN - sz;
>
Don't this needs to be len -= sz;
> + if (unlikely(dev->features & NETIF_F_RXFCS)) {
> + buf = (u32 *)skb_put(skb, ETH_FCS_LEN);
> + *buf = val;
and here len -= ETH_FCS_LEN
since "len" is later used for accounting how many bytes have been
received by the network device?
I am bit confused by the use of "buf", but presumably if NETIF_F_RXCFS
is turned on, the FCS needs to be part of the buffer passed to the
network stack, so adjusting len accordingly is required anyway.
--
Florian
^ permalink raw reply
* Re: [PATCH iproute2/net-next v2] tc_util: Add support for showing TCA_STATS_BASIC_HW statistics
From: Eelco Chaudron @ 2018-10-01 10:29 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev, davem
In-Reply-To: <20181001111051.44f1253b@shemminger-XPS-13-9360>
On 1 Oct 2018, at 11:10, Stephen Hemminger wrote:
> On Mon, 01 Oct 2018 09:08:32 +0200
> "Eelco Chaudron" <echaudro@redhat.com> wrote:
>
>> On 10 Aug 2018, at 16:48, Eelco Chaudron wrote:
>>
>>> On 10 Aug 2018, at 16:44, Stephen Hemminger wrote:
>>>
>>>> On Fri, 10 Aug 2018 07:59:30 -0400
>>>> Eelco Chaudron <echaudro@redhat.com> wrote:
>>>>
>>>>> + if (bs.bytes >= bs_hw.bytes && bs.packets >= bs_hw.packets) {
>>>>> + print_string(PRINT_FP, NULL, "%s", _SL_);
>>>>> + print_string(PRINT_FP, NULL, "%s", prefix);
>>>>> + print_lluint(PRINT_ANY, "sw_bytes",
>>>>> + "Sent software %llu bytes",
>>>>> + bs.bytes - bs_hw.bytes);
>>>>> + print_uint(PRINT_ANY, "sw_packets", " %u pkt",
>>>>> + bs.packets - bs_hw.packets);
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + print_string(PRINT_FP, NULL, "%s", _SL_);
>>>>> + print_string(PRINT_FP, NULL, "%s", prefix);
>>>>> + print_lluint(PRINT_ANY, "hw_bytes", "Sent hardware %llu bytes",
>>>>> + bs_hw.bytes);
>>>>
>>>> What does the output look like?
>>>
>>> See the two +’es below:
>>>
>>> $ tc -s filter show dev enp3s0np0 parent ffff:
>>> filter protocol ip pref 1 flower chain 0
>>> filter protocol ip pref 1 flower chain 0 handle 0x1
>>> eth_type ipv4
>>> dst_ip 2.0.0.0
>>> src_ip 1.0.0.0
>>> ip_flags nofrag
>>> in_hw
>>> action order 1: mirred (Egress Redirect to device eth1) stolen
>>> index 1 ref 1 bind 1 installed 0 sec used 0 sec
>>> Action statistics:
>>> Sent 534884742 bytes 8915697 pkt (dropped 0, overlimits 0
>>> requeues 0)
>>> + Sent software 187542 bytes 4077 pkt
>>> + Sent hardware 534697200 bytes 8911620 pkt
>>> backlog 0b 0p requeues 0
>>> cookie 89173e6a44447001becfd486bda17e29
>>
>> Hi Stephen, anything else required for this patch to be accepted?
>>
>> FYI the kernel side of this patch has been excepted on net-next.
>>
>> Cheers,
>>
>> Eelco
>
> David Ahern handles net-next see patchwork
> https://patchwork.ozlabs.org/patch/956225/
>
> I think he was just waiting for the kernel part to merge.
Thanks for making me aware of the patchwork for iproute.
^ permalink raw reply
* Re: [PATCH net-next v3 07/11] dt-bindings: phy: add DT binding for Microsemi Ocelot SerDes muxing
From: Rob Herring @ 2018-10-01 17:10 UTC (permalink / raw)
To: Quentin Schulz
Cc: Alexandre Belloni, Ralf Baechle, Paul Burton, James Hogan,
Mark Rutland, David Miller, Kishon Vijay Abraham I, Andrew Lunn,
Florian Fainelli, allan.nielsen, Linux-MIPS, devicetree,
linux-kernel@vger.kernel.org, netdev, Thomas Petazzoni
In-Reply-To: <20181001124605.jxiechvp6ztvh77p@qschulz>
On Mon, Oct 1, 2018 at 7:46 AM Quentin Schulz
<quentin.schulz@bootlin.com> wrote:
>
> Hi Rob,
>
> I'm not sure I've understood the way you wanted me to so let me know if
> I'm not on the right path.
>
> On Wed, Sep 26, 2018 at 04:35:09PM -0500, Rob Herring wrote:
> > On Fri, Sep 14, 2018 at 10:16:05AM +0200, Quentin Schulz wrote:
> > > Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com>
> > > ---
> > > Documentation/devicetree/bindings/phy/phy-ocelot-serdes.txt | 40 +++++++-
> > > 1 file changed, 40 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/phy/phy-ocelot-serdes.txt
> > >
> > > diff --git a/Documentation/devicetree/bindings/phy/phy-ocelot-serdes.txt b/Documentation/devicetree/bindings/phy/phy-ocelot-serdes.txt
> > > new file mode 100644
> > > index 0000000..2a88cc3
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/phy/phy-ocelot-serdes.txt
> > > @@ -0,0 +1,40 @@
> > > +Microsemi Ocelot SerDes muxing driver
> > > +-------------------------------------
> > > +
> > > +On Microsemi Ocelot, there is a handful of registers in HSIO address
> > > +space for setting up the SerDes to switch port muxing.
> > > +
> > > +A SerDes X can be "muxed" to work with switch port Y or Z for example.
> > > +One specific SerDes can also be used as a PCIe interface.
> > > +
> > > +Hence, a SerDes represents an interface, be it an Ethernet or a PCIe one.
> > > +
> > > +There are two kinds of SerDes: SERDES1G supports 10/100Mbps in
> > > +half/full-duplex and 1000Mbps in full-duplex mode while SERDES6G supports
> > > +10/100Mbps in half/full-duplex and 1000/2500Mbps in full-duplex mode.
> > > +
> > > +Also, SERDES6G number (aka "macro") 0 is the only interface supporting
> > > +QSGMII.
> > > +
> > > +Required properties:
> > > +
> > > +- compatible: should be "mscc,vsc7514-serdes"
> > > +- #phy-cells : from the generic phy bindings, must be 2.
> > > + The first number defines the input port to use for a given
> > > + SerDes macro. The second defines the macro to use. They are
> > > + defined in dt-bindings/phy/phy-ocelot-serdes.h
> >
> > You need to define what this is a child of.
> >
>
> This is a child of the HSIO syscon on the Microsemi Ocelot. I don't
> expect all Microsemi SoCs that could use this driver to have the SerDes
> node in the HSIO syscon.
>
> Among the latest additions in Documentation/devicetree/bindings/phy I
> couldn't find anything close to my understanding of "define what this is
> a child of", could you elaborate on what you want exactly?
Essentially what you've said here, but specifically what is the
compatible property of the HSIO syscon (the specific one, not
"syscon").
> > > +
> > > +Example:
> > > +
> > > + serdes: serdes {
> > > + compatible = "mscc,vsc7514-serdes";
> > > + #phy-cells = <2>;
> >
> > However, if there are no other resources associated with this, then you
> > don't even need this child node. The parent can be a phy provider and
> > provider of other functions too.
> >
>
> The parent is a syscon with multiple features (SerDes, PLL
> configuration, temp sensor, SyncE, ...) so I'm not sure it's possible to
> do what you're asking me to. For now, there is only a SerDes node but
> ultimately there'll be more than one I guess.
There's no reason you can't have:
syscon {
compatible = "some-soc-syscon-block";
#clock-cells = <1>;
#phy-cells = <2>;
...
};
As it stands, you only have a child node because you want to
instantiate some driver. A single node can be multiple providers and
DT is not the only way to instantiate drivers.
This could change if your sub-nodes need child nodes as well (e.g.
pinctrl) or have their own resources such as clocks, interrupts, etc.
But with an incomplete binding, I can't really tell you what makes
sense.
Rob
^ permalink raw reply
* Re: Marvell phy errata origins?
From: GokulChand Casheekar (gcasheek) @ 2018-10-01 10:26 UTC (permalink / raw)
To: Daniel Walker (danielwa), Harini Katakam, Andrew Lunn
Cc: Florian Fainelli, afleming@freescale.com, Harini Katakam,
netdev@vger.kernel.org, HEMANT RAMDASI (hramdasi),
Julius Hemanth Pitti -X (jpitti - MONTA VISTA SOFTWARE INC at Cisco)
In-Reply-To: <e394e72c-c6a6-457a-40dd-d84cca534d21@cisco.com>
Sorry - I was sick and out of office.
The phy_id reads 0x01410cc1.
Regards,
Gokul.
On 26/09/18, 9:05 PM, "Daniel Walker (danielwa)" <danielwa@cisco.com> wrote:
On 09/25/2018 10:42 PM, Harini Katakam wrote:
> Hi,
> On Tue, Sep 25, 2018 at 11:00 PM Harini Katakam <harinik@xilinx.com> wrote:
>>
>> Hi Daniel,
>>
>> On Tue, Sep 25, 2018 at 9:10 PM Andrew Lunn <andrew@lunn.ch> wrote:
>>>
>>>> I hope this this thread isn't too old to bring back to life. So it seems
>>>> that Harini found that m88e1111 did not need this errata, and Cisco
>>>> previously found that Harini's patch fixed m88e1112, we included it
>>>> internally for that reason
>>>>
>>>> Now I'm getting reports that this errata fixes issues we're seeing on
>>>> m88e1111. We see an interrupt storm without the errata, despite the errata
>>>> not being defined in the datasheet.
>>>
>>> Is everybody actually using interrupts? It could be in one system
>>> phylib is polling.
>>>
>>
>> Yes, we weren't using interrupts; we used phy poll.
>>
>> As I recall, the register and page combination was reserved and
>> the access seemed to fail.
>> It will be useful if we can the errata description or version details.
>> I'll check if I can get any more information.
>
> One of the PHY parts used was "88E1111-B2-bab1i000"
I doubt I can find this level of detail .. We have many of these
machines in the field so they may have different part numbers.
I may have been given some incorrect details on the issue. I'm not
currently sure this errata code is related. I'll let you know when I
have more information.
Daniel
^ permalink raw reply
* [PATCH net-next 0/4] dpaa2-eth: Add support for Rx flow classification
From: Ioana Radulescu @ 2018-10-01 10:44 UTC (permalink / raw)
To: netdev, davem; +Cc: ioana.ciornei
The Management Complex (MC) firmware initially allowed the
configuration of a single key to be used both for Rx flow hashing
and flow classification. This prevented us from supporting
Rx flow classification independently of the hash key configuration.
Newer firmware versions expose separate commands for
configuring the two types of keys, so we can use them to
introduce Rx classification support. For frames that don't match
any classification rule, we fall back to statistical distribution
based on the current hash key.
The first patch in this set updates the Rx hashing code to use
the new firmware API for key config. Subsequent patches introduce
the firmware API for configuring the classification and actual
support for adding and deleting rules via ethtool.
Ioana Radulescu (4):
dpaa2-eth: Use new API for Rx flow hashing
dpaa2-eth: Rename structure
dpaa2-eth: Configure Rx flow classification key
dpaa2-eth: Add ethtool support for flow classification
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 214 ++++++++++---
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h | 29 +-
.../net/ethernet/freescale/dpaa2/dpaa2-ethtool.c | 331 +++++++++++++++++++++
drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h | 51 ++++
drivers/net/ethernet/freescale/dpaa2/dpni.c | 152 ++++++++++
drivers/net/ethernet/freescale/dpaa2/dpni.h | 97 ++++++
6 files changed, 839 insertions(+), 35 deletions(-)
--
2.7.4
^ permalink raw reply
* [PATCH net-next 2/4] dpaa2-eth: Rename structure
From: Ioana Radulescu @ 2018-10-01 10:44 UTC (permalink / raw)
To: netdev, davem; +Cc: ioana.ciornei
In-Reply-To: <1538390698-22073-1-git-send-email-ruxandra.radulescu@nxp.com>
Since the array of supported header fields will be used for
Rx flow classification as well, rename it from "hash_fields" to
the more inclusive "dist_fields".
Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com>
---
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 12 ++++++------
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h | 2 +-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index c72d209..df755d6 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -2004,7 +2004,7 @@ static int setup_tx_flow(struct dpaa2_eth_priv *priv,
}
/* Supported header fields for Rx hash distribution key */
-static const struct dpaa2_eth_hash_fields hash_fields[] = {
+static const struct dpaa2_eth_dist_fields dist_fields[] = {
{
/* L2 header */
.rxnfc_field = RXH_L2DA,
@@ -2110,11 +2110,11 @@ int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
memset(&cls_cfg, 0, sizeof(cls_cfg));
- for (i = 0; i < ARRAY_SIZE(hash_fields); i++) {
+ for (i = 0; i < ARRAY_SIZE(dist_fields); i++) {
struct dpkg_extract *key =
&cls_cfg.extracts[cls_cfg.num_extracts];
- if (!(flags & hash_fields[i].rxnfc_field))
+ if (!(flags & dist_fields[i].rxnfc_field))
continue;
if (cls_cfg.num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) {
@@ -2123,12 +2123,12 @@ int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
}
key->type = DPKG_EXTRACT_FROM_HDR;
- key->extract.from_hdr.prot = hash_fields[i].cls_prot;
+ key->extract.from_hdr.prot = dist_fields[i].cls_prot;
key->extract.from_hdr.type = DPKG_FULL_FIELD;
- key->extract.from_hdr.field = hash_fields[i].cls_field;
+ key->extract.from_hdr.field = dist_fields[i].cls_field;
cls_cfg.num_extracts++;
- rx_hash_fields |= hash_fields[i].rxnfc_field;
+ rx_hash_fields |= dist_fields[i].rxnfc_field;
}
dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL);
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
index 9c8fec2..926ec9b 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
@@ -290,7 +290,7 @@ struct dpaa2_eth_channel {
struct dpaa2_eth_ch_stats stats;
};
-struct dpaa2_eth_hash_fields {
+struct dpaa2_eth_dist_fields {
u64 rxnfc_field;
enum net_prot cls_prot;
int cls_field;
--
2.7.4
^ permalink raw reply related
* [PATCH net-next 1/4] dpaa2-eth: Use new API for Rx flow hashing
From: Ioana Radulescu @ 2018-10-01 10:44 UTC (permalink / raw)
To: netdev, davem; +Cc: ioana.ciornei
In-Reply-To: <1538390698-22073-1-git-send-email-ruxandra.radulescu@nxp.com>
The Management Complex (MC) firmware initially allowed the
configuration of a single key to be used both for Rx flow hashing
and flow classification. This prevented us from supporting
Rx flow classification through ethtool.
Starting with version 10.7.0, the Management Complex(MC) offers
a new set of APIs for separate configuration of Rx hashing and
classification keys.
Update the Rx flow hashing support to use the new API, if available.
Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com>
---
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 73 ++++++++++++++++++------
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h | 10 ++++
drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h | 12 ++++
drivers/net/ethernet/freescale/dpaa2/dpni.c | 32 +++++++++++
drivers/net/ethernet/freescale/dpaa2/dpni.h | 34 +++++++++++
5 files changed, 142 insertions(+), 19 deletions(-)
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index c282d5c..c72d209 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -2049,6 +2049,46 @@ static const struct dpaa2_eth_hash_fields hash_fields[] = {
},
};
+/* Configure the Rx hash key using the legacy API */
+static int config_legacy_hash_key(struct dpaa2_eth_priv *priv, dma_addr_t key)
+{
+ struct device *dev = priv->net_dev->dev.parent;
+ struct dpni_rx_tc_dist_cfg dist_cfg;
+ int err;
+
+ memset(&dist_cfg, 0, sizeof(dist_cfg));
+
+ dist_cfg.key_cfg_iova = key;
+ dist_cfg.dist_size = dpaa2_eth_queue_count(priv);
+ dist_cfg.dist_mode = DPNI_DIST_MODE_HASH;
+
+ err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, 0, &dist_cfg);
+ if (err)
+ dev_err(dev, "dpni_set_rx_tc_dist failed\n");
+
+ return err;
+}
+
+/* Configure the Rx hash key using the new API */
+static int config_hash_key(struct dpaa2_eth_priv *priv, dma_addr_t key)
+{
+ struct device *dev = priv->net_dev->dev.parent;
+ struct dpni_rx_dist_cfg dist_cfg;
+ int err;
+
+ memset(&dist_cfg, 0, sizeof(dist_cfg));
+
+ dist_cfg.key_cfg_iova = key;
+ dist_cfg.dist_size = dpaa2_eth_queue_count(priv);
+ dist_cfg.enable = 1;
+
+ err = dpni_set_rx_hash_dist(priv->mc_io, 0, priv->mc_token, &dist_cfg);
+ if (err)
+ dev_err(dev, "dpni_set_rx_hash_dist failed\n");
+
+ return err;
+}
+
/* Set RX hash options
* flags is a combination of RXH_ bits
*/
@@ -2057,8 +2097,8 @@ int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
struct device *dev = net_dev->dev.parent;
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
struct dpkg_profile_cfg cls_cfg;
- struct dpni_rx_tc_dist_cfg dist_cfg;
u32 rx_hash_fields = 0;
+ dma_addr_t key_iova;
u8 *dma_mem;
int i;
int err = 0;
@@ -2098,34 +2138,29 @@ int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
err = dpni_prepare_key_cfg(&cls_cfg, dma_mem);
if (err) {
dev_err(dev, "dpni_prepare_key_cfg error %d\n", err);
- goto err_prep_key;
+ goto free_key;
}
- memset(&dist_cfg, 0, sizeof(dist_cfg));
-
/* Prepare for setting the rx dist */
- dist_cfg.key_cfg_iova = dma_map_single(dev, dma_mem,
- DPAA2_CLASSIFIER_DMA_SIZE,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, dist_cfg.key_cfg_iova)) {
+ key_iova = dma_map_single(dev, dma_mem, DPAA2_CLASSIFIER_DMA_SIZE,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, key_iova)) {
dev_err(dev, "DMA mapping failed\n");
err = -ENOMEM;
- goto err_dma_map;
+ goto free_key;
}
- dist_cfg.dist_size = dpaa2_eth_queue_count(priv);
- dist_cfg.dist_mode = DPNI_DIST_MODE_HASH;
-
- err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, 0, &dist_cfg);
- dma_unmap_single(dev, dist_cfg.key_cfg_iova,
- DPAA2_CLASSIFIER_DMA_SIZE, DMA_TO_DEVICE);
- if (err)
- dev_err(dev, "dpni_set_rx_tc_dist() error %d\n", err);
+ if (dpaa2_eth_has_legacy_dist(priv))
+ err = config_legacy_hash_key(priv, key_iova);
else
+ err = config_hash_key(priv, key_iova);
+
+ dma_unmap_single(dev, key_iova, DPAA2_CLASSIFIER_DMA_SIZE,
+ DMA_TO_DEVICE);
+ if (!err)
priv->rx_hash_fields = rx_hash_fields;
-err_dma_map:
-err_prep_key:
+free_key:
kfree(dma_mem);
return err;
}
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
index 93bc412..9c8fec2 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
@@ -367,6 +367,16 @@ static inline int dpaa2_eth_cmp_dpni_ver(struct dpaa2_eth_priv *priv,
return priv->dpni_ver_major - ver_major;
}
+/* Minimum firmware version that supports a more flexible API
+ * for configuring the Rx flow hash key
+ */
+#define DPNI_RX_DIST_KEY_VER_MAJOR 7
+#define DPNI_RX_DIST_KEY_VER_MINOR 5
+
+#define dpaa2_eth_has_legacy_dist(priv) \
+ (dpaa2_eth_cmp_dpni_ver((priv), DPNI_RX_DIST_KEY_VER_MAJOR, \
+ DPNI_RX_DIST_KEY_VER_MINOR) < 0)
+
/* Hardware only sees DPAA2_ETH_RX_BUF_SIZE, but the skb built around
* the buffer also needs space for its shared info struct, and we need
* to allocate enough to accommodate hardware alignment restrictions
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h
index 83698ab..a5285c7 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h
@@ -82,6 +82,8 @@
#define DPNI_CMDID_GET_OFFLOAD DPNI_CMD(0x26B)
#define DPNI_CMDID_SET_OFFLOAD DPNI_CMD(0x26C)
+#define DPNI_CMDID_SET_RX_HASH_DIST DPNI_CMD(0x274)
+
/* Macros for accessing command fields smaller than 1byte */
#define DPNI_MASK(field) \
GENMASK(DPNI_##field##_SHIFT + DPNI_##field##_SIZE - 1, \
@@ -515,4 +517,14 @@ struct dpni_rsp_get_api_version {
__le16 minor;
};
+#define DPNI_RX_HASH_DIST_ENABLE_SHIFT 0
+#define DPNI_RX_HASH_DIST_ENABLE_SIZE 1
+struct dpni_cmd_set_rx_hash_dist {
+ __le16 dist_size;
+ u8 enable;
+ u8 tc;
+ __le32 pad;
+ __le64 key_cfg_iova;
+};
+
#endif /* _FSL_DPNI_CMD_H */
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.c b/drivers/net/ethernet/freescale/dpaa2/dpni.c
index d6ac267..a5c71fa 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpni.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpni.c
@@ -1598,3 +1598,35 @@ int dpni_get_api_version(struct fsl_mc_io *mc_io,
return 0;
}
+
+/**
+ * dpni_set_rx_hash_dist() - Set Rx hash distribution
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPNI object
+ * @cfg: Distribution configuration
+ * If cfg.enable is set to 1 the packets will be classified using a hash
+ * function based on the key received in cfg.key_cfg_iova parameter.
+ * If cfg.enable is set to 0 the packets will be sent to the default queue
+ */
+int dpni_set_rx_hash_dist(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ const struct dpni_rx_dist_cfg *cfg)
+{
+ struct dpni_cmd_set_rx_hash_dist *cmd_params;
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_HASH_DIST,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpni_cmd_set_rx_hash_dist *)cmd.params;
+ cmd_params->dist_size = cpu_to_le16(cfg->dist_size);
+ dpni_set_field(cmd_params->enable, RX_HASH_DIST_ENABLE, cfg->enable);
+ cmd_params->tc = cfg->tc;
+ cmd_params->key_cfg_iova = cpu_to_le64(cfg->key_cfg_iova);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.h b/drivers/net/ethernet/freescale/dpaa2/dpni.h
index b378a00..1664b77 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpni.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpni.h
@@ -629,6 +629,40 @@ int dpni_set_rx_tc_dist(struct fsl_mc_io *mc_io,
const struct dpni_rx_tc_dist_cfg *cfg);
/**
+ * When used for fs_miss_flow_id in function dpni_set_rx_dist,
+ * will signal to dpni to drop all unclassified frames
+ */
+#define DPNI_FS_MISS_DROP ((uint16_t)-1)
+
+/**
+ * struct dpni_rx_dist_cfg - Rx distribution configuration
+ * @dist_size: distribution size
+ * @key_cfg_iova: I/O virtual address of 256 bytes DMA-able memory filled with
+ * the extractions to be used for the distribution key by calling
+ * dpni_prepare_key_cfg(); relevant only when enable!=0 otherwise
+ * it can be '0'
+ * @enable: enable/disable the distribution.
+ * @tc: TC id for which distribution is set
+ * @fs_miss_flow_id: when packet misses all rules from flow steering table and
+ * hash is disabled it will be put into this queue id; use
+ * DPNI_FS_MISS_DROP to drop frames. The value of this field is
+ * used only when flow steering distribution is enabled and hash
+ * distribution is disabled
+ */
+struct dpni_rx_dist_cfg {
+ u16 dist_size;
+ u64 key_cfg_iova;
+ u8 enable;
+ u8 tc;
+ u16 fs_miss_flow_id;
+};
+
+int dpni_set_rx_hash_dist(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ const struct dpni_rx_dist_cfg *cfg);
+
+/**
* enum dpni_dest - DPNI destination types
* @DPNI_DEST_NONE: Unassigned destination; The queue is set in parked mode and
* does not generate FQDAN notifications; user is expected to
--
2.7.4
^ permalink raw reply related
* [PATCH net-next 3/4] dpaa2-eth: Configure Rx flow classification key
From: Ioana Radulescu @ 2018-10-01 10:44 UTC (permalink / raw)
To: netdev, davem; +Cc: ioana.ciornei
In-Reply-To: <1538390698-22073-1-git-send-email-ruxandra.radulescu@nxp.com>
For firmware versions that support it, configure an Rx flow
classification key at probe time.
Hardware expects all rules in the classification table to share
the same key. So we setup a key containing all supported fields
at driver init and when a user adds classification rules through
ethtool, we will just mask out the unused header fields.
Since the key composition process is the same for flow
classification and hashing, reuse existing code where possible.
Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com>
---
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 99 ++++++++++++++++++++----
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h | 6 ++
drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h | 12 +++
drivers/net/ethernet/freescale/dpaa2/dpni.c | 42 ++++++++++
drivers/net/ethernet/freescale/dpaa2/dpni.h | 5 ++
5 files changed, 148 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index df755d6..953d29f 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -2089,10 +2089,31 @@ static int config_hash_key(struct dpaa2_eth_priv *priv, dma_addr_t key)
return err;
}
-/* Set RX hash options
+/* Configure the Rx flow classification key */
+static int config_cls_key(struct dpaa2_eth_priv *priv, dma_addr_t key)
+{
+ struct device *dev = priv->net_dev->dev.parent;
+ struct dpni_rx_dist_cfg dist_cfg;
+ int err;
+
+ memset(&dist_cfg, 0, sizeof(dist_cfg));
+
+ dist_cfg.key_cfg_iova = key;
+ dist_cfg.dist_size = dpaa2_eth_queue_count(priv);
+ dist_cfg.enable = 1;
+
+ err = dpni_set_rx_fs_dist(priv->mc_io, 0, priv->mc_token, &dist_cfg);
+ if (err)
+ dev_err(dev, "dpni_set_rx_fs_dist failed\n");
+
+ return err;
+}
+
+/* Set Rx distribution (hash or flow classification) key
* flags is a combination of RXH_ bits
*/
-int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
+int dpaa2_eth_set_dist_key(struct net_device *net_dev,
+ enum dpaa2_eth_rx_dist type, u64 flags)
{
struct device *dev = net_dev->dev.parent;
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
@@ -2103,19 +2124,20 @@ int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
int i;
int err = 0;
- if (!dpaa2_eth_hash_enabled(priv)) {
- dev_dbg(dev, "Hashing support is not enabled\n");
- return -EOPNOTSUPP;
- }
-
memset(&cls_cfg, 0, sizeof(cls_cfg));
for (i = 0; i < ARRAY_SIZE(dist_fields); i++) {
struct dpkg_extract *key =
&cls_cfg.extracts[cls_cfg.num_extracts];
- if (!(flags & dist_fields[i].rxnfc_field))
- continue;
+ /* For Rx hashing key we set only the selected fields.
+ * For Rx flow classification key we set all supported fields
+ */
+ if (type == DPAA2_ETH_RX_DIST_HASH) {
+ if (!(flags & dist_fields[i].rxnfc_field))
+ continue;
+ rx_hash_fields |= dist_fields[i].rxnfc_field;
+ }
if (cls_cfg.num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) {
dev_err(dev, "error adding key extraction rule, too many rules?\n");
@@ -2127,8 +2149,6 @@ int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
key->extract.from_hdr.type = DPKG_FULL_FIELD;
key->extract.from_hdr.field = dist_fields[i].cls_field;
cls_cfg.num_extracts++;
-
- rx_hash_fields |= dist_fields[i].rxnfc_field;
}
dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL);
@@ -2150,14 +2170,18 @@ int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
goto free_key;
}
- if (dpaa2_eth_has_legacy_dist(priv))
- err = config_legacy_hash_key(priv, key_iova);
- else
- err = config_hash_key(priv, key_iova);
+ if (type == DPAA2_ETH_RX_DIST_HASH) {
+ if (dpaa2_eth_has_legacy_dist(priv))
+ err = config_legacy_hash_key(priv, key_iova);
+ else
+ err = config_hash_key(priv, key_iova);
+ } else {
+ err = config_cls_key(priv, key_iova);
+ }
dma_unmap_single(dev, key_iova, DPAA2_CLASSIFIER_DMA_SIZE,
DMA_TO_DEVICE);
- if (!err)
+ if (!err && type == DPAA2_ETH_RX_DIST_HASH)
priv->rx_hash_fields = rx_hash_fields;
free_key:
@@ -2165,6 +2189,42 @@ int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
return err;
}
+int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags)
+{
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+
+ if (!dpaa2_eth_hash_enabled(priv))
+ return -EOPNOTSUPP;
+
+ return dpaa2_eth_set_dist_key(net_dev, DPAA2_ETH_RX_DIST_HASH, flags);
+}
+
+static int dpaa2_eth_set_cls(struct dpaa2_eth_priv *priv)
+{
+ struct device *dev = priv->net_dev->dev.parent;
+
+ /* Check if we actually support Rx flow classification */
+ if (dpaa2_eth_has_legacy_dist(priv)) {
+ dev_dbg(dev, "Rx cls not supported by current MC version\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (priv->dpni_attrs.options & DPNI_OPT_NO_FS ||
+ !(priv->dpni_attrs.options & DPNI_OPT_HAS_KEY_MASKING)) {
+ dev_dbg(dev, "Rx cls disabled in DPNI options\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!dpaa2_eth_hash_enabled(priv)) {
+ dev_dbg(dev, "Rx cls disabled for single queue DPNIs\n");
+ return -EOPNOTSUPP;
+ }
+
+ priv->rx_cls_enabled = 1;
+
+ return dpaa2_eth_set_dist_key(priv->net_dev, DPAA2_ETH_RX_DIST_CLS, 0);
+}
+
/* Bind the DPNI to its needed objects and resources: buffer pool, DPIOs,
* frame queues and channels
*/
@@ -2194,6 +2254,13 @@ static int bind_dpni(struct dpaa2_eth_priv *priv)
if (err && err != -EOPNOTSUPP)
dev_err(dev, "Failed to configure hashing\n");
+ /* Configure the flow classification key; it includes all
+ * supported header fields and cannot be modified at runtime
+ */
+ err = dpaa2_eth_set_cls(priv);
+ if (err && err != -EOPNOTSUPP)
+ dev_err(dev, "Failed to configure Rx classification key\n");
+
/* Configure handling of error frames */
err_cfg.errors = DPAA2_FAS_RX_ERR_MASK;
err_cfg.set_frame_annotation = 1;
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
index 926ec9b..b67be5c 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
@@ -340,6 +340,7 @@ struct dpaa2_eth_priv {
/* enabled ethtool hashing bits */
u64 rx_hash_fields;
+ u8 rx_cls_enabled;
};
#define DPAA2_RXH_SUPPORTED (RXH_L2DA | RXH_VLAN | RXH_L3_PROTO \
@@ -377,6 +378,11 @@ static inline int dpaa2_eth_cmp_dpni_ver(struct dpaa2_eth_priv *priv,
(dpaa2_eth_cmp_dpni_ver((priv), DPNI_RX_DIST_KEY_VER_MAJOR, \
DPNI_RX_DIST_KEY_VER_MINOR) < 0)
+enum dpaa2_eth_rx_dist {
+ DPAA2_ETH_RX_DIST_HASH,
+ DPAA2_ETH_RX_DIST_CLS
+};
+
/* Hardware only sees DPAA2_ETH_RX_BUF_SIZE, but the skb built around
* the buffer also needs space for its shared info struct, and we need
* to allocate enough to accommodate hardware alignment restrictions
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h
index a5285c7..52fde3e 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h
@@ -82,6 +82,7 @@
#define DPNI_CMDID_GET_OFFLOAD DPNI_CMD(0x26B)
#define DPNI_CMDID_SET_OFFLOAD DPNI_CMD(0x26C)
+#define DPNI_CMDID_SET_RX_FS_DIST DPNI_CMD(0x273)
#define DPNI_CMDID_SET_RX_HASH_DIST DPNI_CMD(0x274)
/* Macros for accessing command fields smaller than 1byte */
@@ -517,6 +518,17 @@ struct dpni_rsp_get_api_version {
__le16 minor;
};
+#define DPNI_RX_FS_DIST_ENABLE_SHIFT 0
+#define DPNI_RX_FS_DIST_ENABLE_SIZE 1
+struct dpni_cmd_set_rx_fs_dist {
+ __le16 dist_size;
+ u8 enable;
+ u8 tc;
+ __le16 miss_flow_id;
+ __le16 pad;
+ __le64 key_cfg_iova;
+};
+
#define DPNI_RX_HASH_DIST_ENABLE_SHIFT 0
#define DPNI_RX_HASH_DIST_ENABLE_SIZE 1
struct dpni_cmd_set_rx_hash_dist {
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.c b/drivers/net/ethernet/freescale/dpaa2/dpni.c
index a5c71fa..be7fd2b 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpni.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpni.c
@@ -1600,6 +1600,48 @@ int dpni_get_api_version(struct fsl_mc_io *mc_io,
}
/**
+ * dpni_set_rx_fs_dist() - Set Rx flow steering distribution
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPNI object
+ * @cfg: Distribution configuration
+ *
+ * If the FS is already enabled with a previous call the classification
+ * key will be changed but all the table rules are kept. If the
+ * existing rules do not match the key the results will not be
+ * predictable. It is the user responsibility to keep key integrity.
+ * If cfg.enable is set to 1 the command will create a flow steering table
+ * and will classify packets according to this table. The packets that
+ * miss all the table rules will be classified according to settings
+ * made in dpni_set_rx_hash_dist()
+ * If cfg.enable is set to 0 the command will clear flow steering table.
+ * The packets will be classified according to settings made in
+ * dpni_set_rx_hash_dist()
+ */
+int dpni_set_rx_fs_dist(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ const struct dpni_rx_dist_cfg *cfg)
+{
+ struct dpni_cmd_set_rx_fs_dist *cmd_params;
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_FS_DIST,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpni_cmd_set_rx_fs_dist *)cmd.params;
+ cmd_params->dist_size = cpu_to_le16(cfg->dist_size);
+ dpni_set_field(cmd_params->enable, RX_FS_DIST_ENABLE, cfg->enable);
+ cmd_params->tc = cfg->tc;
+ cmd_params->miss_flow_id = cpu_to_le16(cfg->fs_miss_flow_id);
+ cmd_params->key_cfg_iova = cpu_to_le64(cfg->key_cfg_iova);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
* dpni_set_rx_hash_dist() - Set Rx hash distribution
* @mc_io: Pointer to MC portal's I/O object
* @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.h b/drivers/net/ethernet/freescale/dpaa2/dpni.h
index 1664b77..ad7d848 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpni.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpni.h
@@ -657,6 +657,11 @@ struct dpni_rx_dist_cfg {
u16 fs_miss_flow_id;
};
+int dpni_set_rx_fs_dist(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ const struct dpni_rx_dist_cfg *cfg);
+
int dpni_set_rx_hash_dist(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
--
2.7.4
^ permalink raw reply related
* [PATCH net-next 4/4] dpaa2-eth: Add ethtool support for flow classification
From: Ioana Radulescu @ 2018-10-01 10:44 UTC (permalink / raw)
To: netdev, davem; +Cc: ioana.ciornei
In-Reply-To: <1538390698-22073-1-git-send-email-ruxandra.radulescu@nxp.com>
Add support for inserting and deleting Rx flow classification
rules through ethtool.
We support classification based on some header fields for
flow-types ether, ip4, tcp4, udp4 and sctp4.
Rx queues are core affine, so the action argument effectively
selects on which cpu the matching frame will be processed.
Discarding the frame is also supported.
Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com>
---
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 44 +++
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h | 11 +
.../net/ethernet/freescale/dpaa2/dpaa2-ethtool.c | 331 +++++++++++++++++++++
drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h | 27 ++
drivers/net/ethernet/freescale/dpaa2/dpni.c | 78 +++++
drivers/net/ethernet/freescale/dpaa2/dpni.h | 58 ++++
6 files changed, 549 insertions(+)
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index 953d29f..108c137 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -1897,6 +1897,11 @@ static int setup_dpni(struct fsl_mc_device *ls_dev)
if (err)
goto close;
+ priv->cls_rules = devm_kzalloc(dev, sizeof(struct dpaa2_eth_cls_rule) *
+ dpaa2_eth_fs_count(priv), GFP_KERNEL);
+ if (!priv->cls_rules)
+ goto close;
+
return 0;
close:
@@ -2012,6 +2017,18 @@ static const struct dpaa2_eth_dist_fields dist_fields[] = {
.cls_field = NH_FLD_ETH_DA,
.size = 6,
}, {
+ .cls_prot = NET_PROT_ETH,
+ .cls_field = NH_FLD_ETH_SA,
+ .size = 6,
+ }, {
+ /* This is the last ethertype field parsed:
+ * depending on frame format, it can be the MAC ethertype
+ * or the VLAN etype.
+ */
+ .cls_prot = NET_PROT_ETH,
+ .cls_field = NH_FLD_ETH_TYPE,
+ .size = 2,
+ }, {
/* VLAN header */
.rxnfc_field = RXH_VLAN,
.cls_prot = NET_PROT_VLAN,
@@ -2109,6 +2126,33 @@ static int config_cls_key(struct dpaa2_eth_priv *priv, dma_addr_t key)
return err;
}
+/* Size of the Rx flow classification key */
+int dpaa2_eth_cls_key_size(void)
+{
+ int i, size = 0;
+
+ for (i = 0; i < ARRAY_SIZE(dist_fields); i++)
+ size += dist_fields[i].size;
+
+ return size;
+}
+
+/* Offset of header field in Rx classification key */
+int dpaa2_eth_cls_fld_off(int prot, int field)
+{
+ int i, off = 0;
+
+ for (i = 0; i < ARRAY_SIZE(dist_fields); i++) {
+ if (dist_fields[i].cls_prot == prot &&
+ dist_fields[i].cls_field == field)
+ return off;
+ off += dist_fields[i].size;
+ }
+
+ WARN_ONCE(1, "Unsupported header field used for Rx flow cls\n");
+ return 0;
+}
+
/* Set Rx distribution (hash or flow classification) key
* flags is a combination of RXH_ bits
*/
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
index b67be5c..7a7a3e7 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
@@ -297,6 +297,11 @@ struct dpaa2_eth_dist_fields {
int size;
};
+struct dpaa2_eth_cls_rule {
+ struct ethtool_rx_flow_spec fs;
+ u8 in_use;
+};
+
/* Driver private data */
struct dpaa2_eth_priv {
struct net_device *net_dev;
@@ -340,6 +345,7 @@ struct dpaa2_eth_priv {
/* enabled ethtool hashing bits */
u64 rx_hash_fields;
+ struct dpaa2_eth_cls_rule *cls_rules;
u8 rx_cls_enabled;
};
@@ -378,6 +384,9 @@ static inline int dpaa2_eth_cmp_dpni_ver(struct dpaa2_eth_priv *priv,
(dpaa2_eth_cmp_dpni_ver((priv), DPNI_RX_DIST_KEY_VER_MAJOR, \
DPNI_RX_DIST_KEY_VER_MINOR) < 0)
+#define dpaa2_eth_fs_count(priv) \
+ ((priv)->dpni_attrs.fs_entries)
+
enum dpaa2_eth_rx_dist {
DPAA2_ETH_RX_DIST_HASH,
DPAA2_ETH_RX_DIST_CLS
@@ -426,5 +435,7 @@ static int dpaa2_eth_queue_count(struct dpaa2_eth_priv *priv)
}
int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags);
+int dpaa2_eth_cls_key_size(void);
+int dpaa2_eth_cls_fld_off(int prot, int field);
#endif /* __DPAA2_H */
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
index ce0d94d..26bd5a2 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
@@ -224,10 +224,310 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev,
*(data + i++) = cdan;
}
+static int prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask,
+ void *key, void *mask)
+{
+ int off;
+
+ if (eth_mask->h_proto) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE);
+ *(__be16 *)(key + off) = eth_value->h_proto;
+ *(__be16 *)(mask + off) = eth_mask->h_proto;
+ }
+
+ if (!is_zero_ether_addr(eth_mask->h_source)) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_SA);
+ ether_addr_copy(key + off, eth_value->h_source);
+ ether_addr_copy(mask + off, eth_mask->h_source);
+ }
+
+ if (!is_zero_ether_addr(eth_mask->h_dest)) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_DA);
+ ether_addr_copy(key + off, eth_value->h_dest);
+ ether_addr_copy(mask + off, eth_mask->h_dest);
+ }
+
+ return 0;
+}
+
+static int prep_uip_rule(struct ethtool_usrip4_spec *uip_value,
+ struct ethtool_usrip4_spec *uip_mask,
+ void *key, void *mask)
+{
+ int off;
+ u32 tmp_value, tmp_mask;
+
+ if (uip_mask->tos || uip_mask->ip_ver)
+ return -EOPNOTSUPP;
+
+ if (uip_mask->ip4src) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_SRC);
+ *(__be32 *)(key + off) = uip_value->ip4src;
+ *(__be32 *)(mask + off) = uip_mask->ip4src;
+ }
+
+ if (uip_mask->ip4dst) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_DST);
+ *(__be32 *)(key + off) = uip_value->ip4dst;
+ *(__be32 *)(mask + off) = uip_mask->ip4dst;
+ }
+
+ if (uip_mask->proto) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_PROTO);
+ *(u8 *)(key + off) = uip_value->proto;
+ *(u8 *)(mask + off) = uip_mask->proto;
+ }
+
+ if (uip_mask->l4_4_bytes) {
+ tmp_value = be32_to_cpu(uip_value->l4_4_bytes);
+ tmp_mask = be32_to_cpu(uip_mask->l4_4_bytes);
+
+ off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_SRC);
+ *(__be16 *)(key + off) = htons(tmp_value >> 16);
+ *(__be16 *)(mask + off) = htons(tmp_mask >> 16);
+
+ off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_DST);
+ *(__be16 *)(key + off) = htons(tmp_value & 0xFFFF);
+ *(__be16 *)(mask + off) = htons(tmp_mask & 0xFFFF);
+ }
+
+ /* Only apply the rule for IPv4 frames */
+ off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE);
+ *(__be16 *)(key + off) = htons(ETH_P_IP);
+ *(__be16 *)(mask + off) = htons(0xFFFF);
+
+ return 0;
+}
+
+static int prep_l4_rule(struct ethtool_tcpip4_spec *l4_value,
+ struct ethtool_tcpip4_spec *l4_mask,
+ void *key, void *mask, u8 l4_proto)
+{
+ int off;
+
+ if (l4_mask->tos)
+ return -EOPNOTSUPP;
+
+ if (l4_mask->ip4src) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_SRC);
+ *(__be32 *)(key + off) = l4_value->ip4src;
+ *(__be32 *)(mask + off) = l4_mask->ip4src;
+ }
+
+ if (l4_mask->ip4dst) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_DST);
+ *(__be32 *)(key + off) = l4_value->ip4dst;
+ *(__be32 *)(mask + off) = l4_mask->ip4dst;
+ }
+
+ if (l4_mask->psrc) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_SRC);
+ *(__be16 *)(key + off) = l4_value->psrc;
+ *(__be16 *)(mask + off) = l4_mask->psrc;
+ }
+
+ if (l4_mask->pdst) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_DST);
+ *(__be16 *)(key + off) = l4_value->pdst;
+ *(__be16 *)(mask + off) = l4_mask->pdst;
+ }
+
+ /* Only apply the rule for IPv4 frames with the specified L4 proto */
+ off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE);
+ *(__be16 *)(key + off) = htons(ETH_P_IP);
+ *(__be16 *)(mask + off) = htons(0xFFFF);
+
+ off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_PROTO);
+ *(u8 *)(key + off) = l4_proto;
+ *(u8 *)(mask + off) = 0xFF;
+
+ return 0;
+}
+
+static int prep_ext_rule(struct ethtool_flow_ext *ext_value,
+ struct ethtool_flow_ext *ext_mask,
+ void *key, void *mask)
+{
+ int off;
+
+ if (ext_mask->vlan_etype)
+ return -EOPNOTSUPP;
+
+ if (ext_mask->vlan_tci) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_VLAN, NH_FLD_VLAN_TCI);
+ *(__be16 *)(key + off) = ext_value->vlan_tci;
+ *(__be16 *)(mask + off) = ext_mask->vlan_tci;
+ }
+
+ return 0;
+}
+
+static int prep_mac_ext_rule(struct ethtool_flow_ext *ext_value,
+ struct ethtool_flow_ext *ext_mask,
+ void *key, void *mask)
+{
+ int off;
+
+ if (!is_zero_ether_addr(ext_mask->h_dest)) {
+ off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_DA);
+ ether_addr_copy(key + off, ext_value->h_dest);
+ ether_addr_copy(mask + off, ext_mask->h_dest);
+ }
+
+ return 0;
+}
+
+static int prep_cls_rule(struct ethtool_rx_flow_spec *fs, void *key, void *mask)
+{
+ int err;
+
+ switch (fs->flow_type & 0xFF) {
+ case ETHER_FLOW:
+ err = prep_eth_rule(&fs->h_u.ether_spec, &fs->m_u.ether_spec,
+ key, mask);
+ break;
+ case IP_USER_FLOW:
+ err = prep_uip_rule(&fs->h_u.usr_ip4_spec,
+ &fs->m_u.usr_ip4_spec, key, mask);
+ break;
+ case TCP_V4_FLOW:
+ err = prep_l4_rule(&fs->h_u.tcp_ip4_spec, &fs->m_u.tcp_ip4_spec,
+ key, mask, IPPROTO_TCP);
+ break;
+ case UDP_V4_FLOW:
+ err = prep_l4_rule(&fs->h_u.udp_ip4_spec, &fs->m_u.udp_ip4_spec,
+ key, mask, IPPROTO_UDP);
+ break;
+ case SCTP_V4_FLOW:
+ err = prep_l4_rule(&fs->h_u.sctp_ip4_spec,
+ &fs->m_u.sctp_ip4_spec, key, mask,
+ IPPROTO_SCTP);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (err)
+ return err;
+
+ if (fs->flow_type & FLOW_EXT) {
+ err = prep_ext_rule(&fs->h_ext, &fs->m_ext, key, mask);
+ if (err)
+ return err;
+ }
+
+ if (fs->flow_type & FLOW_MAC_EXT) {
+ err = prep_mac_ext_rule(&fs->h_ext, &fs->m_ext, key, mask);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int do_cls_rule(struct net_device *net_dev,
+ struct ethtool_rx_flow_spec *fs,
+ bool add)
+{
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+ struct device *dev = net_dev->dev.parent;
+ struct dpni_rule_cfg rule_cfg = { 0 };
+ struct dpni_fs_action_cfg fs_act = { 0 };
+ dma_addr_t key_iova;
+ void *key_buf;
+ int err;
+
+ if (fs->ring_cookie != RX_CLS_FLOW_DISC &&
+ fs->ring_cookie >= dpaa2_eth_queue_count(priv))
+ return -EINVAL;
+
+ rule_cfg.key_size = dpaa2_eth_cls_key_size();
+
+ /* allocate twice the key size, for the actual key and for mask */
+ key_buf = kzalloc(rule_cfg.key_size * 2, GFP_KERNEL);
+ if (!key_buf)
+ return -ENOMEM;
+
+ /* Fill the key and mask memory areas */
+ err = prep_cls_rule(fs, key_buf, key_buf + rule_cfg.key_size);
+ if (err)
+ goto free_mem;
+
+ key_iova = dma_map_single(dev, key_buf, rule_cfg.key_size * 2,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, key_iova)) {
+ err = -ENOMEM;
+ goto free_mem;
+ }
+
+ rule_cfg.key_iova = key_iova;
+ rule_cfg.mask_iova = key_iova + rule_cfg.key_size;
+
+ if (add) {
+ if (fs->ring_cookie == RX_CLS_FLOW_DISC)
+ fs_act.options |= DPNI_FS_OPT_DISCARD;
+ else
+ fs_act.flow_id = fs->ring_cookie;
+ err = dpni_add_fs_entry(priv->mc_io, 0, priv->mc_token, 0,
+ fs->location, &rule_cfg, &fs_act);
+ } else {
+ err = dpni_remove_fs_entry(priv->mc_io, 0, priv->mc_token, 0,
+ &rule_cfg);
+ }
+
+ dma_unmap_single(dev, key_iova, rule_cfg.key_size * 2, DMA_TO_DEVICE);
+
+free_mem:
+ kfree(key_buf);
+
+ return err;
+}
+
+static int update_cls_rule(struct net_device *net_dev,
+ struct ethtool_rx_flow_spec *new_fs,
+ int location)
+{
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+ struct dpaa2_eth_cls_rule *rule;
+ int err = -EINVAL;
+
+ if (!priv->rx_cls_enabled)
+ return -EOPNOTSUPP;
+
+ if (location >= dpaa2_eth_fs_count(priv))
+ return -EINVAL;
+
+ rule = &priv->cls_rules[location];
+
+ /* If a rule is present at the specified location, delete it. */
+ if (rule->in_use) {
+ err = do_cls_rule(net_dev, &rule->fs, false);
+ if (err)
+ return err;
+
+ rule->in_use = 0;
+ }
+
+ /* If no new entry to add, return here */
+ if (!new_fs)
+ return err;
+
+ err = do_cls_rule(net_dev, new_fs, true);
+ if (err)
+ return err;
+
+ rule->in_use = 1;
+ rule->fs = *new_fs;
+
+ return 0;
+}
+
static int dpaa2_eth_get_rxnfc(struct net_device *net_dev,
struct ethtool_rxnfc *rxnfc, u32 *rule_locs)
{
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+ int max_rules = dpaa2_eth_fs_count(priv);
+ int i, j = 0;
switch (rxnfc->cmd) {
case ETHTOOL_GRXFH:
@@ -240,6 +540,31 @@ static int dpaa2_eth_get_rxnfc(struct net_device *net_dev,
case ETHTOOL_GRXRINGS:
rxnfc->data = dpaa2_eth_queue_count(priv);
break;
+ case ETHTOOL_GRXCLSRLCNT:
+ rxnfc->rule_cnt = 0;
+ for (i = 0; i < max_rules; i++)
+ if (priv->cls_rules[i].in_use)
+ rxnfc->rule_cnt++;
+ rxnfc->data = max_rules;
+ break;
+ case ETHTOOL_GRXCLSRULE:
+ if (rxnfc->fs.location >= max_rules)
+ return -EINVAL;
+ if (!priv->cls_rules[rxnfc->fs.location].in_use)
+ return -EINVAL;
+ rxnfc->fs = priv->cls_rules[rxnfc->fs.location].fs;
+ break;
+ case ETHTOOL_GRXCLSRLALL:
+ for (i = 0; i < max_rules; i++) {
+ if (!priv->cls_rules[i].in_use)
+ continue;
+ if (j == rxnfc->rule_cnt)
+ return -EMSGSIZE;
+ rule_locs[j++] = i;
+ }
+ rxnfc->rule_cnt = j;
+ rxnfc->data = max_rules;
+ break;
default:
return -EOPNOTSUPP;
}
@@ -258,6 +583,12 @@ static int dpaa2_eth_set_rxnfc(struct net_device *net_dev,
return -EOPNOTSUPP;
err = dpaa2_eth_set_hash(net_dev, rxnfc->data);
break;
+ case ETHTOOL_SRXCLSRLINS:
+ err = update_cls_rule(net_dev, &rxnfc->fs, rxnfc->fs.location);
+ break;
+ case ETHTOOL_SRXCLSRLDEL:
+ err = update_cls_rule(net_dev, NULL, rxnfc->fs.location);
+ break;
default:
err = -EOPNOTSUPP;
}
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h
index 52fde3e..7b44d7d 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h
@@ -539,4 +539,31 @@ struct dpni_cmd_set_rx_hash_dist {
__le64 key_cfg_iova;
};
+struct dpni_cmd_add_fs_entry {
+ /* cmd word 0 */
+ __le16 options;
+ u8 tc_id;
+ u8 key_size;
+ __le16 index;
+ __le16 flow_id;
+ /* cmd word 1 */
+ __le64 key_iova;
+ /* cmd word 2 */
+ __le64 mask_iova;
+ /* cmd word 3 */
+ __le64 flc;
+};
+
+struct dpni_cmd_remove_fs_entry {
+ /* cmd word 0 */
+ __le16 pad0;
+ u8 tc_id;
+ u8 key_size;
+ __le32 pad1;
+ /* cmd word 1 */
+ __le64 key_iova;
+ /* cmd word 2 */
+ __le64 mask_iova;
+};
+
#endif /* _FSL_DPNI_CMD_H */
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.c b/drivers/net/ethernet/freescale/dpaa2/dpni.c
index be7fd2b..220dfc8 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpni.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpni.c
@@ -1672,3 +1672,81 @@ int dpni_set_rx_hash_dist(struct fsl_mc_io *mc_io,
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
+
+/**
+ * dpni_add_fs_entry() - Add Flow Steering entry for a specific traffic class
+ * (to select a flow ID)
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPNI object
+ * @tc_id: Traffic class selection (0-7)
+ * @index: Location in the FS table where to insert the entry.
+ * Only relevant if MASKING is enabled for FS
+ * classification on this DPNI, it is ignored for exact match.
+ * @cfg: Flow steering rule to add
+ * @action: Action to be taken as result of a classification hit
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpni_add_fs_entry(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 tc_id,
+ u16 index,
+ const struct dpni_rule_cfg *cfg,
+ const struct dpni_fs_action_cfg *action)
+{
+ struct dpni_cmd_add_fs_entry *cmd_params;
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_FS_ENT,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpni_cmd_add_fs_entry *)cmd.params;
+ cmd_params->tc_id = tc_id;
+ cmd_params->key_size = cfg->key_size;
+ cmd_params->index = cpu_to_le16(index);
+ cmd_params->key_iova = cpu_to_le64(cfg->key_iova);
+ cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova);
+ cmd_params->options = cpu_to_le16(action->options);
+ cmd_params->flow_id = cpu_to_le16(action->flow_id);
+ cmd_params->flc = cpu_to_le64(action->flc);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpni_remove_fs_entry() - Remove Flow Steering entry from a specific
+ * traffic class
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPNI object
+ * @tc_id: Traffic class selection (0-7)
+ * @cfg: Flow steering rule to remove
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpni_remove_fs_entry(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 tc_id,
+ const struct dpni_rule_cfg *cfg)
+{
+ struct dpni_cmd_remove_fs_entry *cmd_params;
+ struct fsl_mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_FS_ENT,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpni_cmd_remove_fs_entry *)cmd.params;
+ cmd_params->tc_id = tc_id;
+ cmd_params->key_size = cfg->key_size;
+ cmd_params->key_iova = cpu_to_le64(cfg->key_iova);
+ cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.h b/drivers/net/ethernet/freescale/dpaa2/dpni.h
index ad7d848..a521242 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpni.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpni.h
@@ -855,6 +855,64 @@ struct dpni_rule_cfg {
u8 key_size;
};
+/**
+ * Discard matching traffic. If set, this takes precedence over any other
+ * configuration and matching traffic is always discarded.
+ */
+ #define DPNI_FS_OPT_DISCARD 0x1
+
+/**
+ * Set FLC value. If set, flc member of struct dpni_fs_action_cfg is used to
+ * override the FLC value set per queue.
+ * For more details check the Frame Descriptor section in the hardware
+ * documentation.
+ */
+#define DPNI_FS_OPT_SET_FLC 0x2
+
+/**
+ * Indicates whether the 6 lowest significant bits of FLC are used for stash
+ * control. If set, the 6 least significant bits in value are interpreted as
+ * follows:
+ * - bits 0-1: indicates the number of 64 byte units of context that are
+ * stashed. FLC value is interpreted as a memory address in this case,
+ * excluding the 6 LS bits.
+ * - bits 2-3: indicates the number of 64 byte units of frame annotation
+ * to be stashed. Annotation is placed at FD[ADDR].
+ * - bits 4-5: indicates the number of 64 byte units of frame data to be
+ * stashed. Frame data is placed at FD[ADDR] + FD[OFFSET].
+ * This flag is ignored if DPNI_FS_OPT_SET_FLC is not specified.
+ */
+#define DPNI_FS_OPT_SET_STASH_CONTROL 0x4
+
+/**
+ * struct dpni_fs_action_cfg - Action configuration for table look-up
+ * @flc: FLC value for traffic matching this rule. Please check the
+ * Frame Descriptor section in the hardware documentation for
+ * more information.
+ * @flow_id: Identifies the Rx queue used for matching traffic. Supported
+ * values are in range 0 to num_queue-1.
+ * @options: Any combination of DPNI_FS_OPT_ values.
+ */
+struct dpni_fs_action_cfg {
+ u64 flc;
+ u16 flow_id;
+ u16 options;
+};
+
+int dpni_add_fs_entry(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 tc_id,
+ u16 index,
+ const struct dpni_rule_cfg *cfg,
+ const struct dpni_fs_action_cfg *action);
+
+int dpni_remove_fs_entry(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u8 tc_id,
+ const struct dpni_rule_cfg *cfg);
+
int dpni_get_api_version(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 *major_ver,
--
2.7.4
^ permalink raw reply related
* [PATCH 0/3] bpf: allow zero-initialising hash map seed
From: Lorenz Bauer @ 2018-10-01 10:45 UTC (permalink / raw)
To: ast, daniel; +Cc: netdev, linux-api, Lorenz Bauer
This patch set adds a new flag BPF_F_ZERO_SEED, which allows
forcing the seed used by hash maps to zero. This makes
it possible to write deterministic tests.
Based on an off-list conversation with Alexei Starovoitov and
Daniel Borkmann.
Lorenz Bauer (3):
bpf: allow zero-initializing hash map seed
tools: sync linux/bpf.h
tools: add selftest for BPF_F_ZERO_SEED
include/uapi/linux/bpf.h | 2 +
kernel/bpf/hashtab.c | 8 ++-
tools/include/uapi/linux/bpf.h | 2 +
tools/testing/selftests/bpf/test_maps.c | 67 +++++++++++++++++++++----
4 files changed, 66 insertions(+), 13 deletions(-)
--
2.17.1
^ permalink raw reply
* [PATCH 1/3] bpf: allow zero-initializing hash map seed
From: Lorenz Bauer @ 2018-10-01 10:45 UTC (permalink / raw)
To: ast, daniel; +Cc: netdev, linux-api, Lorenz Bauer
In-Reply-To: <20181001104509.24211-1-lmb@cloudflare.com>
Add a new flag BPF_F_ZERO_SEED, which forces a hash map
to initialize the seed to zero.
---
include/uapi/linux/bpf.h | 2 ++
kernel/bpf/hashtab.c | 8 ++++++--
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index aa5ccd2385ed..9d15c8f179ac 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -252,6 +252,8 @@ enum bpf_attach_type {
#define BPF_F_NO_COMMON_LRU (1U << 1)
/* Specify numa node during map creation */
#define BPF_F_NUMA_NODE (1U << 2)
+/* Zero-initialize hash function seed */
+#define BPF_F_ZERO_SEED (1U << 6)
/* flags for BPF_PROG_QUERY */
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 2c1790288138..a79e123dae62 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -23,7 +23,7 @@
#define HTAB_CREATE_FLAG_MASK \
(BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE | \
- BPF_F_RDONLY | BPF_F_WRONLY)
+ BPF_F_RDONLY | BPF_F_WRONLY | BPF_F_ZERO_SEED)
struct bucket {
struct hlist_nulls_head head;
@@ -373,7 +373,11 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
if (!htab->buckets)
goto free_htab;
- htab->hashrnd = get_random_int();
+ if (htab->map.map_flags & BPF_F_ZERO_SEED)
+ htab->hashrnd = 0;
+ else
+ htab->hashrnd = get_random_int();
+
for (i = 0; i < htab->n_buckets; i++) {
INIT_HLIST_NULLS_HEAD(&htab->buckets[i].head, i);
raw_spin_lock_init(&htab->buckets[i].lock);
--
2.17.1
^ 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