* Re: [PATCH v3 net-next 19/19] ionic: Add basic devlink interface
From: Shannon Nelson @ 2019-07-08 19:58 UTC (permalink / raw)
To: Jiri Pirko; +Cc: netdev
In-Reply-To: <20190708193454.GF2282@nanopsycho.orion>
On 7/8/19 12:34 PM, Jiri Pirko wrote:
> Mon, Jul 08, 2019 at 09:25:32PM CEST, snelson@pensando.io wrote:
>> Add a devlink interface for access to information that isn't
>> normally available through ethtool or the iplink interface.
>>
>> Example:
>> $ ./devlink -j -p dev info pci/0000:b6:00.0
>> {
>> "info": {
>> "pci/0000:b6:00.0": {
>> "driver": "ionic",
>> "serial_number": "FLM18420073",
>> "versions": {
>> "fixed": {
>> "fw_version": "0.11.0-50",
>> "fw_status": "0x1",
>> "fw_heartbeat": "0x716ce",
>> "asic_type": "0x0",
>> "asic_rev": "0x0"
>> }
>> }
>> }
>> }
>> }
>>
>> Signed-off-by: Shannon Nelson <snelson@pensando.io>
>> ---
>> drivers/net/ethernet/pensando/ionic/Makefile | 2 +-
>> drivers/net/ethernet/pensando/ionic/ionic.h | 1 +
>> .../ethernet/pensando/ionic/ionic_bus_pci.c | 7 ++
>> .../ethernet/pensando/ionic/ionic_devlink.c | 89 +++++++++++++++++++
>> .../ethernet/pensando/ionic/ionic_devlink.h | 12 +++
>> 5 files changed, 110 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_devlink.c
>> create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_devlink.h
>>
>> diff --git a/drivers/net/ethernet/pensando/ionic/Makefile b/drivers/net/ethernet/pensando/ionic/Makefile
>> index 4f3cfbf36c23..ce187c7b33a8 100644
>> --- a/drivers/net/ethernet/pensando/ionic/Makefile
>> +++ b/drivers/net/ethernet/pensando/ionic/Makefile
>> @@ -5,4 +5,4 @@ obj-$(CONFIG_IONIC) := ionic.o
>>
>> ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o ionic_ethtool.o \
>> ionic_lif.o ionic_rx_filter.o ionic_txrx.o ionic_debugfs.o \
>> - ionic_stats.o
>> + ionic_stats.o ionic_devlink.o
>> diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
>> index cd08166f73a9..a0034bc5b4a1 100644
>> --- a/drivers/net/ethernet/pensando/ionic/ionic.h
>> +++ b/drivers/net/ethernet/pensando/ionic/ionic.h
>> @@ -44,6 +44,7 @@ struct ionic {
>> DECLARE_BITMAP(intrs, INTR_CTRL_REGS_MAX);
>> struct work_struct nb_work;
>> struct notifier_block nb;
>> + struct devlink *dl;
>> };
>>
>> struct ionic_admin_ctx {
>> diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
>> index 98c12b770c7f..a8c99254489f 100644
>> --- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
>> +++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
>> @@ -10,6 +10,7 @@
>> #include "ionic_bus.h"
>> #include "ionic_lif.h"
>> #include "ionic_debugfs.h"
>> +#include "ionic_devlink.h"
>>
>> /* Supported devices */
>> static const struct pci_device_id ionic_id_table[] = {
>> @@ -212,9 +213,14 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>> goto err_out_deinit_lifs;
>> }
>>
>> + err = ionic_devlink_register(ionic);
>> + if (err)
>> + dev_err(dev, "Cannot register devlink (ignored): %d\n", err);
>> +
>> return 0;
>>
>> err_out_deinit_lifs:
>> + ionic_devlink_unregister(ionic);
>> ionic_lifs_deinit(ionic);
>> err_out_free_lifs:
>> ionic_lifs_free(ionic);
>> @@ -247,6 +253,7 @@ static void ionic_remove(struct pci_dev *pdev)
>> struct ionic *ionic = pci_get_drvdata(pdev);
>>
>> if (ionic) {
>> + ionic_devlink_unregister(ionic);
>> ionic_lifs_unregister(ionic);
>> ionic_lifs_deinit(ionic);
>> ionic_lifs_free(ionic);
>> diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
>> new file mode 100644
>> index 000000000000..fbbfcdde292f
>> --- /dev/null
>> +++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
>> @@ -0,0 +1,89 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
>> +
>> +#include <linux/module.h>
>> +#include <linux/netdevice.h>
>> +
>> +#include "ionic.h"
>> +#include "ionic_bus.h"
>> +#include "ionic_lif.h"
>> +#include "ionic_devlink.h"
>> +
>> +struct ionic_devlink {
>> + struct ionic *ionic;
>> +};
>> +
>> +static int ionic_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
>> + struct netlink_ext_ack *extack)
>> +{
>> + struct ionic *ionic = *(struct ionic **)devlink_priv(dl);
>> + struct ionic_dev *idev = &ionic->idev;
>> + char buf[16];
>> + u32 val;
>> +
>> + devlink_info_driver_name_put(req, DRV_NAME);
>> +
>> + devlink_info_version_fixed_put(req, "fw_version",
>> + idev->dev_info.fw_version);
>> +
>> + val = ioread8(&idev->dev_info_regs->fw_status);
>> + snprintf(buf, sizeof(buf), "0x%x", val);
>> + devlink_info_version_fixed_put(req, "fw_status", buf);
>> +
>> + val = ioread32(&idev->dev_info_regs->fw_heartbeat);
>> + snprintf(buf, sizeof(buf), "0x%x", val);
>> + devlink_info_version_fixed_put(req, "fw_heartbeat", buf);
>> +
>> + snprintf(buf, sizeof(buf), "0x%x", idev->dev_info.asic_type);
>> + devlink_info_version_fixed_put(req, "asic_type", buf);
>> +
>> + snprintf(buf, sizeof(buf), "0x%x", idev->dev_info.asic_rev);
>> + devlink_info_version_fixed_put(req, "asic_rev", buf);
>> +
>> + devlink_info_serial_number_put(req, idev->dev_info.serial_num);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct devlink_ops ionic_dl_ops = {
>> + .info_get = ionic_dl_info_get,
>> +};
>> +
>> +int ionic_devlink_register(struct ionic *ionic)
>> +{
>> + struct devlink *dl;
>> + struct ionic **ip;
>> + int err;
>> +
>> + dl = devlink_alloc(&ionic_dl_ops, sizeof(struct ionic *));
> Oups. Something is wrong with your flow. The devlink alloc is allocating
> the structure that holds private data (per-device data) for you. This is
> misuse :/
>
> You are missing one parent device struct apparently.
>
> Oh, I think I see something like it. The unused "struct ionic_devlink".
If I'm not mistaken, the alloc is only allocating enough for a pointer,
not the whole per device struct, and a few lines down from here the
pointer to the new devlink struct is assigned to ionic->dl. This was
based on what I found in the qed driver's qed_devlink_register(), and it
all seems to work.
That unused struct ionic_devlink does need to go away, it was
superfluous after working out a better typecast off of devlink_priv().
I'll remove the unused struct ionic_devlink, but I think the rest is okay.
sln
>
>
>> + if (!dl) {
>> + dev_warn(ionic->dev, "devlink_alloc failed");
>> + return -ENOMEM;
>> + }
>> +
>> + ip = (struct ionic **)devlink_priv(dl);
>> + *ip = ionic;
>> + ionic->dl = dl;
>> +
>> + err = devlink_register(dl, ionic->dev);
>> + if (err) {
>> + dev_warn(ionic->dev, "devlink_register failed: %d\n", err);
>> + goto err_dl_free;
>> + }
>> +
>> + return 0;
>> +
>> +err_dl_free:
>> + ionic->dl = NULL;
>> + devlink_free(dl);
>> + return err;
>> +}
>> +
>> +void ionic_devlink_unregister(struct ionic *ionic)
>> +{
>> + if (!ionic->dl)
>> + return;
>> +
>> + devlink_unregister(ionic->dl);
>> + devlink_free(ionic->dl);
>> +}
>> diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.h b/drivers/net/ethernet/pensando/ionic/ionic_devlink.h
>> new file mode 100644
>> index 000000000000..35528884e29f
>> --- /dev/null
>> +++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.h
>> @@ -0,0 +1,12 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
>> +
>> +#ifndef _IONIC_DEVLINK_H_
>> +#define _IONIC_DEVLINK_H_
>> +
>> +#include <net/devlink.h>
>> +
>> +int ionic_devlink_register(struct ionic *ionic);
>> +void ionic_devlink_unregister(struct ionic *ionic);
>> +
>> +#endif /* _IONIC_DEVLINK_H_ */
>> --
>> 2.17.1
>>
^ permalink raw reply
* Re: [PATCH v3 bpf-next 4/9] libbpf: add kprobe/uprobe attach API
From: Andrii Nakryiko @ 2019-07-08 19:55 UTC (permalink / raw)
To: Matt Hart
Cc: Andrii Nakryiko, Alexei Starovoitov, bpf, Daniel Borkmann,
Kernel Team, Networking, Stanislav Fomichev
In-Reply-To: <CAH+k93G=qGLfEKe+3dSZPKhmxrc8JiPqDppGa-yLSwaQYRJU=Q@mail.gmail.com>
On Mon, Jul 8, 2019 at 12:27 PM Matt Hart <matthew.hart@linaro.org> wrote:
>
> On Mon, 8 Jul 2019 at 18:58, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> >
> > On Mon, Jul 8, 2019 at 8:11 AM Matt Hart <matthew.hart@linaro.org> wrote:
> > >
> > > Hi all,
> > >
> > > I bisected a perf build error on ARMv7 to this patch:
> > > libbpf.c: In function ‘perf_event_open_probe’:
> > > libbpf.c:4112:17: error: cast from pointer to integer of different
> > > size [-Werror=pointer-to-int-cast]
> > > attr.config1 = (uint64_t)(void *)name; /* kprobe_func or uprobe_path */
> > > ^
> > >
> > > Is this a known issue?
> >
> > No, thanks for reporting!
> >
> > It should be
> >
> > attr.config1 = (uint64_t)(uintptr_t)(void *)name;
> >
> > to avoid warning on 32-bit architectures.
>
> Tested with manual change and can confirm perf now builds without errors.
Thanks for testing!
I'll add Tested-by: Matt Hart <matthew.hart@linaro.org> when posting a fix.
>
> >
> > I'll post a fix later today, but if you could verify this fixes
> > warning for you, I'd really appreciate that! Thanks!
^ permalink raw reply
* Re: [PATCH v3 6/7] dt-bindings: net: realtek: Add property to configure LED mode
From: Andrew Lunn @ 2019-07-08 19:48 UTC (permalink / raw)
To: Matthias Kaehlcke
Cc: David S . Miller, Rob Herring, Mark Rutland, Florian Fainelli,
Heiner Kallweit, netdev, devicetree, linux-kernel,
Douglas Anderson
In-Reply-To: <20190708192459.187984-7-mka@chromium.org>
On Mon, Jul 08, 2019 at 12:24:58PM -0700, Matthias Kaehlcke wrote:
> The LED behavior of some Realtek PHYs is configurable. Add the
> property 'realtek,led-modes' to specify the configuration of the
> LEDs.
>
> Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
Hi Matthias
Humm. I thought you were going to drop this and the next patch?
Andrew
^ permalink raw reply
* Re: [PATCH v3 1/7] dt-bindings: net: Add bindings for Realtek PHYs
From: Andrew Lunn @ 2019-07-08 19:46 UTC (permalink / raw)
To: Matthias Kaehlcke
Cc: David S . Miller, Rob Herring, Mark Rutland, Florian Fainelli,
Heiner Kallweit, netdev, devicetree, linux-kernel,
Douglas Anderson
In-Reply-To: <20190708192459.187984-2-mka@chromium.org>
On Mon, Jul 08, 2019 at 12:24:53PM -0700, Matthias Kaehlcke wrote:
> Add the 'realtek,eee-led-mode-disable' property to disable EEE
> LED mode on Realtek PHYs that support it.
>
> Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
> ---
> TODO: adapt PHY core to deal with optional compatible strings
Yes. Does this even work at the moment? I would expect
of_mdiobus_child_is_phy() to return false, indicating the device is
not actually a PHY.
Andrew
^ permalink raw reply
* Re: [PATCH] [net-next] macb: fix build warning for !CONFIG_OF
From: David Miller @ 2019-07-08 19:43 UTC (permalink / raw)
To: arnd
Cc: nicolas.ferre, palmer, paul.walmsley, yash.shah, harini.katakam,
claudiu.beznea, netdev, linux-kernel, linux-riscv
In-Reply-To: <20190708124840.3616530-1-arnd@arndb.de>
From: Arnd Bergmann <arnd@arndb.de>
Date: Mon, 8 Jul 2019 14:48:23 +0200
> When CONFIG_OF is disabled, we get a harmless warning about the
> newly added variable:
>
> drivers/net/ethernet/cadence/macb_main.c:48:39: error: 'mgmt' defined but not used [-Werror=unused-variable]
> static struct sifive_fu540_macb_mgmt *mgmt;
>
> Move the variable closer to its use inside of the #ifdef.
>
> Fixes: c218ad559020 ("macb: Add support for SiFive FU540-C000")
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Applied, thanks Arnd.
^ permalink raw reply
* Re: [PATCH] [net-next] gve: fix unused variable/label warnings
From: David Miller @ 2019-07-08 19:41 UTC (permalink / raw)
To: arnd; +Cc: csully, sagis, jonolson, willemb, lrizzo, netdev, linux-kernel
In-Reply-To: <20190708124350.3470436-1-arnd@arndb.de>
From: Arnd Bergmann <arnd@arndb.de>
Date: Mon, 8 Jul 2019 14:43:39 +0200
> On unusual page sizes, we get harmless warnings:
>
> drivers/net/ethernet/google/gve/gve_rx.c:283:6: error: unused variable 'pagecount' [-Werror,-Wunused-variable]
> drivers/net/ethernet/google/gve/gve_rx.c:336:1: error: unused label 'have_skb' [-Werror,-Wunused-label]
>
> Change the preprocessor #if to regular if() to avoid this.
>
> Fixes: f5cedc84a30d ("gve: Add transmit and receive support")
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Applied.
^ permalink raw reply
* Re: [PATCH net] net: stmmac: Re-work the queue selection for TSO packets
From: David Miller @ 2019-07-08 19:41 UTC (permalink / raw)
To: Jose.Abreu
Cc: netdev, Joao.Pinto, peppe.cavallaro, alexandre.torgue,
mcoquelin.stm32, linux-stm32, linux-arm-kernel, linux-kernel, ben
In-Reply-To: <36018491f47206728e04d67a9e6263635e64f721.1562588640.git.joabreu@synopsys.com>
From: Jose Abreu <Jose.Abreu@synopsys.com>
Date: Mon, 8 Jul 2019 14:26:28 +0200
> Ben Hutchings says:
> "This is the wrong place to change the queue mapping.
> stmmac_xmit() is called with a specific TX queue locked,
> and accessing a different TX queue results in a data race
> for all of that queue's state.
>
> I think this commit should be reverted upstream and in all
> stable branches. Instead, the driver should implement the
> ndo_select_queue operation and override the queue mapping there."
>
> Fixes: c5acdbee22a1 ("net: stmmac: Send TSO packets always from Queue 0")
> Suggested-by: Ben Hutchings <ben@decadent.org.uk>
> Signed-off-by: Jose Abreu <joabreu@synopsys.com>
Applied and queued up for -stable.
^ permalink raw reply
* possible deadlock in rtnl_lock (6)
From: syzbot @ 2019-07-08 19:37 UTC (permalink / raw)
To: ast, bjorn.topel, bpf, christian, daniel, davem, dsahern,
edumazet, hawk, i.maximets, idosch, jakub.kicinski, johannes.berg,
john.fastabend, jonathan.lemon, kafai, linux-kernel,
magnus.karlsson, netdev, petrm, roopa, songliubraving,
syzkaller-bugs, xdp-newbies, yhs
Hello,
syzbot found the following crash on:
HEAD commit: 537de0c8 ipv4: Fix NULL pointer dereference in ipv4_neigh_..
git tree: net
console output: https://syzkaller.appspot.com/x/log.txt?x=14521cc3a00000
kernel config: https://syzkaller.appspot.com/x/.config?x=90f5d2d9c1e7421c
dashboard link: https://syzkaller.appspot.com/bug?extid=174ce29c2308dec5bc68
compiler: gcc (GCC) 9.0.0 20181231 (experimental)
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=1777debba00000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=16969b53a00000
The bug was bisected to:
commit 455302d1c9ae9318660aaeb9748a01ff414c9741
Author: Ilya Maximets <i.maximets@samsung.com>
Date: Fri Jun 28 08:04:07 2019 +0000
xdp: fix hang while unregistering device bound to xdp socket
bisection log: https://syzkaller.appspot.com/x/bisect.txt?x=1179943da00000
final crash: https://syzkaller.appspot.com/x/report.txt?x=1379943da00000
console output: https://syzkaller.appspot.com/x/log.txt?x=1579943da00000
IMPORTANT: if you fix the bug, please add the following tag to the commit:
Reported-by: syzbot+174ce29c2308dec5bc68@syzkaller.appspotmail.com
Fixes: 455302d1c9ae ("xdp: fix hang while unregistering device bound to xdp
socket")
======================================================
WARNING: possible circular locking dependency detected
5.2.0-rc6+ #76 Not tainted
------------------------------------------------------
syz-executor613/9114 is trying to acquire lock:
000000002c564901 (rtnl_mutex){+.+.}, at: rtnl_lock+0x17/0x20
net/core/rtnetlink.c:72
but task is already holding lock:
0000000039d6ee9b (&xs->mutex){+.+.}, at: xsk_bind+0x16a/0xe70
net/xdp/xsk.c:422
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #2 (&xs->mutex){+.+.}:
__mutex_lock_common kernel/locking/mutex.c:926 [inline]
__mutex_lock+0xf7/0x1310 kernel/locking/mutex.c:1073
mutex_lock_nested+0x16/0x20 kernel/locking/mutex.c:1088
xsk_notifier+0x149/0x290 net/xdp/xsk.c:730
notifier_call_chain+0xc2/0x230 kernel/notifier.c:95
__raw_notifier_call_chain kernel/notifier.c:396 [inline]
raw_notifier_call_chain+0x2e/0x40 kernel/notifier.c:403
call_netdevice_notifiers_info+0x3f/0x90 net/core/dev.c:1749
call_netdevice_notifiers_extack net/core/dev.c:1761 [inline]
call_netdevice_notifiers net/core/dev.c:1775 [inline]
rollback_registered_many+0x9b9/0xfc0 net/core/dev.c:8206
rollback_registered+0x109/0x1d0 net/core/dev.c:8248
unregister_netdevice_queue net/core/dev.c:9295 [inline]
unregister_netdevice_queue+0x1ee/0x2c0 net/core/dev.c:9288
br_dev_delete+0x145/0x1a0 net/bridge/br_if.c:383
br_del_bridge+0xd7/0x120 net/bridge/br_if.c:483
br_ioctl_deviceless_stub+0x2a4/0x7b0 net/bridge/br_ioctl.c:376
sock_ioctl+0x44b/0x780 net/socket.c:1141
vfs_ioctl fs/ioctl.c:46 [inline]
file_ioctl fs/ioctl.c:509 [inline]
do_vfs_ioctl+0xd5f/0x1380 fs/ioctl.c:696
ksys_ioctl+0xab/0xd0 fs/ioctl.c:713
__do_sys_ioctl fs/ioctl.c:720 [inline]
__se_sys_ioctl fs/ioctl.c:718 [inline]
__x64_sys_ioctl+0x73/0xb0 fs/ioctl.c:718
do_syscall_64+0xfd/0x680 arch/x86/entry/common.c:301
entry_SYSCALL_64_after_hwframe+0x49/0xbe
-> #1 (&net->xdp.lock){+.+.}:
__mutex_lock_common kernel/locking/mutex.c:926 [inline]
__mutex_lock+0xf7/0x1310 kernel/locking/mutex.c:1073
mutex_lock_nested+0x16/0x20 kernel/locking/mutex.c:1088
xsk_notifier+0xa7/0x290 net/xdp/xsk.c:726
notifier_call_chain+0xc2/0x230 kernel/notifier.c:95
__raw_notifier_call_chain kernel/notifier.c:396 [inline]
raw_notifier_call_chain+0x2e/0x40 kernel/notifier.c:403
call_netdevice_notifiers_info+0x3f/0x90 net/core/dev.c:1749
call_netdevice_notifiers_extack net/core/dev.c:1761 [inline]
call_netdevice_notifiers net/core/dev.c:1775 [inline]
rollback_registered_many+0x9b9/0xfc0 net/core/dev.c:8206
rollback_registered+0x109/0x1d0 net/core/dev.c:8248
unregister_netdevice_queue net/core/dev.c:9295 [inline]
unregister_netdevice_queue+0x1ee/0x2c0 net/core/dev.c:9288
br_dev_delete+0x145/0x1a0 net/bridge/br_if.c:383
br_del_bridge+0xd7/0x120 net/bridge/br_if.c:483
br_ioctl_deviceless_stub+0x2a4/0x7b0 net/bridge/br_ioctl.c:376
sock_ioctl+0x44b/0x780 net/socket.c:1141
vfs_ioctl fs/ioctl.c:46 [inline]
file_ioctl fs/ioctl.c:509 [inline]
do_vfs_ioctl+0xd5f/0x1380 fs/ioctl.c:696
ksys_ioctl+0xab/0xd0 fs/ioctl.c:713
__do_sys_ioctl fs/ioctl.c:720 [inline]
__se_sys_ioctl fs/ioctl.c:718 [inline]
__x64_sys_ioctl+0x73/0xb0 fs/ioctl.c:718
do_syscall_64+0xfd/0x680 arch/x86/entry/common.c:301
entry_SYSCALL_64_after_hwframe+0x49/0xbe
-> #0 (rtnl_mutex){+.+.}:
lock_acquire+0x16f/0x3f0 kernel/locking/lockdep.c:4303
__mutex_lock_common kernel/locking/mutex.c:926 [inline]
__mutex_lock+0xf7/0x1310 kernel/locking/mutex.c:1073
mutex_lock_nested+0x16/0x20 kernel/locking/mutex.c:1088
rtnl_lock+0x17/0x20 net/core/rtnetlink.c:72
xdp_umem_assign_dev+0xbe/0x8b0 net/xdp/xdp_umem.c:96
xsk_bind+0x4d7/0xe70 net/xdp/xsk.c:488
__sys_bind+0x239/0x290 net/socket.c:1653
__do_sys_bind net/socket.c:1664 [inline]
__se_sys_bind net/socket.c:1662 [inline]
__x64_sys_bind+0x73/0xb0 net/socket.c:1662
do_syscall_64+0xfd/0x680 arch/x86/entry/common.c:301
entry_SYSCALL_64_after_hwframe+0x49/0xbe
other info that might help us debug this:
Chain exists of:
rtnl_mutex --> &net->xdp.lock --> &xs->mutex
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock(&xs->mutex);
lock(&net->xdp.lock);
lock(&xs->mutex);
lock(rtnl_mutex);
*** DEADLOCK ***
1 lock held by syz-executor613/9114:
#0: 0000000039d6ee9b (&xs->mutex){+.+.}, at: xsk_bind+0x16a/0xe70
net/xdp/xsk.c:422
stack backtrace:
CPU: 1 PID: 9114 Comm: syz-executor613 Not tainted 5.2.0-rc6+ #76
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+0x172/0x1f0 lib/dump_stack.c:113
print_circular_bug.cold+0x1cc/0x28f kernel/locking/lockdep.c:1565
check_prev_add kernel/locking/lockdep.c:2310 [inline]
check_prevs_add kernel/locking/lockdep.c:2418 [inline]
validate_chain kernel/locking/lockdep.c:2800 [inline]
__lock_acquire+0x3755/0x5490 kernel/locking/lockdep.c:3793
lock_acquire+0x16f/0x3f0 kernel/locking/lockdep.c:4303
__mutex_lock_common kernel/locking/mutex.c:926 [inline]
__mutex_lock+0xf7/0x1310 kernel/locking/mutex.c:1073
mutex_lock_nested+0x16/0x20 kernel/locking/mutex.c:1088
rtnl_lock+0x17/0x20 net/core/rtnetlink.c:72
xdp_umem_assign_dev+0xbe/0x8b0 net/xdp/xdp_umem.c:96
xsk_bind+0x4d7/0xe70 net/xdp/xsk.c:488
__sys_bind+0x239/0x290 net/socket.c:1653
__do_sys_bind net/socket.c:1664 [inline]
__se_sys_bind net/socket.c:1662 [inline]
__x64_sys_bind+0x73/0xb0 net/socket.c:1662
do_syscall_64+0xfd/0x680 arch/x86/entry/common.c:301
entry_SYSCALL_64_after_hwframe+0x49/0xbe
RIP: 0033:0x447909
Code: e8 cc e7 ff ff 48 83 c4 18 c3 0f 1f 80 00 00 00 00 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 3b 08 fc ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007fb2a478fd98 EFLAGS: 00000246 ORIG_RAX: 0000000000000031
RAX: ffffffffffffffda RBX: 00000000006dcc58 RCX: 0000000000447909
RDX: 0000000000000010 RSI: 0000000020000040 RDI: 0000000000000005
RBP: 00000000006dcc50 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00000000006dcc5c
R13: 0000003066736362 R14: 0000000000000000 R15: 0000003066736362
---
This bug is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this bug report. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
For information about bisection process see: https://goo.gl/tpsmEJ#bisection
syzbot can test patches for this bug, for details see:
https://goo.gl/tpsmEJ#testing-patches
^ permalink raw reply
* WARNING: held lock freed! (2)
From: syzbot @ 2019-07-08 19:37 UTC (permalink / raw)
To: davem, linux-hams, linux-kernel, netdev, ralf, syzkaller-bugs,
xiyou.wangcong
Hello,
syzbot found the following crash on:
HEAD commit: 9d1bc24b bonding: validate ip header before check IPPROTO_..
git tree: net
console output: https://syzkaller.appspot.com/x/log.txt?x=152fab25a00000
kernel config: https://syzkaller.appspot.com/x/.config?x=e7c31a94f66cc0aa
dashboard link: https://syzkaller.appspot.com/bug?extid=e54ed2cb16c6da22c549
compiler: gcc (GCC) 9.0.0 20181231 (experimental)
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=11ad60bba00000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=165d2453a00000
The bug was bisected to:
commit c8c8218ec5af5d2598381883acbefbf604e56b5e
Author: Cong Wang <xiyou.wangcong@gmail.com>
Date: Thu Jun 27 21:30:58 2019 +0000
netrom: fix a memory leak in nr_rx_frame()
bisection log: https://syzkaller.appspot.com/x/bisect.txt?x=1489854ba00000
final crash: https://syzkaller.appspot.com/x/report.txt?x=1689854ba00000
console output: https://syzkaller.appspot.com/x/log.txt?x=1289854ba00000
IMPORTANT: if you fix the bug, please add the following tag to the commit:
Reported-by: syzbot+e54ed2cb16c6da22c549@syzkaller.appspotmail.com
Fixes: c8c8218ec5af ("netrom: fix a memory leak in nr_rx_frame()")
=========================
WARNING: held lock freed!
5.2.0-rc6+ #75 Not tainted
-------------------------
syz-executor315/8559 is freeing memory ffff88809faed2c0-ffff88809faedabf,
with a lock still held there!
00000000cf45dbdb (sk_lock-AF_NETROM){+.+.}, at: lock_sock
include/net/sock.h:1522 [inline]
00000000cf45dbdb (sk_lock-AF_NETROM){+.+.}, at: nr_release+0x11a/0x3b0
net/netrom/af_netrom.c:522
2 locks held by syz-executor315/8559:
#0: 00000000c0a19dcd (&sb->s_type->i_mutex_key#11){+.+.}, at: inode_lock
include/linux/fs.h:778 [inline]
#0: 00000000c0a19dcd (&sb->s_type->i_mutex_key#11){+.+.}, at:
__sock_release+0x89/0x2a0 net/socket.c:600
#1: 00000000cf45dbdb (sk_lock-AF_NETROM){+.+.}, at: lock_sock
include/net/sock.h:1522 [inline]
#1: 00000000cf45dbdb (sk_lock-AF_NETROM){+.+.}, at: nr_release+0x11a/0x3b0
net/netrom/af_netrom.c:522
stack backtrace:
CPU: 0 PID: 8559 Comm: syz-executor315 Not tainted 5.2.0-rc6+ #75
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+0x172/0x1f0 lib/dump_stack.c:113
print_freed_lock_bug kernel/locking/lockdep.c:5077 [inline]
debug_check_no_locks_freed.cold+0x9d/0xa9 kernel/locking/lockdep.c:5110
kfree+0xb1/0x220 mm/slab.c:3752
sk_prot_free net/core/sock.c:1636 [inline]
__sk_destruct+0x4f7/0x6e0 net/core/sock.c:1722
sk_destruct+0x7b/0x90 net/core/sock.c:1730
__sk_free+0xce/0x300 net/core/sock.c:1741
sk_free+0x42/0x50 net/core/sock.c:1752
sock_put include/net/sock.h:1725 [inline]
nr_destroy_socket+0x3df/0x4a0 net/netrom/af_netrom.c:288
nr_release+0x323/0x3b0 net/netrom/af_netrom.c:530
__sock_release+0xce/0x2a0 net/socket.c:601
sock_close+0x1b/0x30 net/socket.c:1273
__fput+0x2ff/0x890 fs/file_table.c:280
____fput+0x16/0x20 fs/file_table.c:313
task_work_run+0x145/0x1c0 kernel/task_work.c:113
tracehook_notify_resume include/linux/tracehook.h:185 [inline]
exit_to_usermode_loop+0x273/0x2c0 arch/x86/entry/common.c:168
prepare_exit_to_usermode arch/x86/entry/common.c:199 [inline]
syscall_return_slowpath arch/x86/entry/common.c:279 [inline]
do_syscall_64+0x58e/0x680 arch/x86/entry/common.c:304
entry_SYSCALL_64_after_hwframe+0x49/0xbe
RIP: 0033:0x447269
Code: e8 7c 14 03 00 48 83 c4 18 c3 0f 1f 80 00 00 00 00 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 0e fc ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007f653a7bed88 EFLAGS: 00000246 ORIG_RAX: 000000000000002d
RAX: ffffffffffffff95 RBX: 00000000006dcc48 RCX: 0000000000447269
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000003
RBP: 00000000006dcc40 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00000000006dcc4c
R13: 0000003066736362 R14: 002cc7eb47000000 R15: 0000003066736362
==================================================================
BUG: KASAN: use-after-free in debug_spin_lock_before
kernel/locking/spinlock_debug.c:83 [inline]
BUG: KASAN: use-after-free in do_raw_spin_lock+0x28a/0x2e0
kernel/locking/spinlock_debug.c:112
Read of size 4 at addr ffff88809faed34c by task syz-executor315/8559
CPU: 1 PID: 8559 Comm: syz-executor315 Not tainted 5.2.0-rc6+ #75
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+0x172/0x1f0 lib/dump_stack.c:113
print_address_description.cold+0x7c/0x20d mm/kasan/report.c:188
__kasan_report.cold+0x1b/0x40 mm/kasan/report.c:317
kasan_report+0x12/0x20 mm/kasan/common.c:614
__asan_report_load4_noabort+0x14/0x20 mm/kasan/generic_report.c:131
debug_spin_lock_before kernel/locking/spinlock_debug.c:83 [inline]
do_raw_spin_lock+0x28a/0x2e0 kernel/locking/spinlock_debug.c:112
__raw_spin_lock_bh include/linux/spinlock_api_smp.h:136 [inline]
_raw_spin_lock_bh+0x3b/0x50 kernel/locking/spinlock.c:175
spin_lock_bh include/linux/spinlock.h:343 [inline]
release_sock+0x20/0x1c0 net/core/sock.c:2928
nr_release+0x2df/0x3b0 net/netrom/af_netrom.c:553
__sock_release+0xce/0x2a0 net/socket.c:601
sock_close+0x1b/0x30 net/socket.c:1273
__fput+0x2ff/0x890 fs/file_table.c:280
____fput+0x16/0x20 fs/file_table.c:313
task_work_run+0x145/0x1c0 kernel/task_work.c:113
tracehook_notify_resume include/linux/tracehook.h:185 [inline]
exit_to_usermode_loop+0x273/0x2c0 arch/x86/entry/common.c:168
prepare_exit_to_usermode arch/x86/entry/common.c:199 [inline]
syscall_return_slowpath arch/x86/entry/common.c:279 [inline]
do_syscall_64+0x58e/0x680 arch/x86/entry/common.c:304
entry_SYSCALL_64_after_hwframe+0x49/0xbe
RIP: 0033:0x447269
Code: e8 7c 14 03 00 48 83 c4 18 c3 0f 1f 80 00 00 00 00 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 0e fc ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007f653a7bed88 EFLAGS: 00000246 ORIG_RAX: 000000000000002d
RAX: ffffffffffffff95 RBX: 00000000006dcc48 RCX: 0000000000447269
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000003
RBP: 00000000006dcc40 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00000000006dcc4c
R13: 0000003066736362 R14: 002cc7eb47000000 R15: 0000003066736362
Allocated by task 8562:
save_stack+0x23/0x90 mm/kasan/common.c:71
set_track mm/kasan/common.c:79 [inline]
__kasan_kmalloc mm/kasan/common.c:489 [inline]
__kasan_kmalloc.constprop.0+0xcf/0xe0 mm/kasan/common.c:462
kasan_kmalloc+0x9/0x10 mm/kasan/common.c:503
__do_kmalloc mm/slab.c:3660 [inline]
__kmalloc+0x15c/0x740 mm/slab.c:3669
kmalloc include/linux/slab.h:552 [inline]
sk_prot_alloc+0x19c/0x2e0 net/core/sock.c:1599
sk_alloc+0x39/0xf70 net/core/sock.c:1653
nr_make_new net/netrom/af_netrom.c:476 [inline]
nr_rx_frame+0x733/0x1e70 net/netrom/af_netrom.c:959
nr_loopback_timer+0x7b/0x170 net/netrom/nr_loopback.c:59
call_timer_fn+0x193/0x720 kernel/time/timer.c:1322
expire_timers kernel/time/timer.c:1366 [inline]
__run_timers kernel/time/timer.c:1685 [inline]
__run_timers kernel/time/timer.c:1653 [inline]
run_timer_softirq+0x66f/0x1740 kernel/time/timer.c:1698
__do_softirq+0x25c/0x94c kernel/softirq.c:292
Freed by task 8559:
save_stack+0x23/0x90 mm/kasan/common.c:71
set_track mm/kasan/common.c:79 [inline]
__kasan_slab_free+0x102/0x150 mm/kasan/common.c:451
kasan_slab_free+0xe/0x10 mm/kasan/common.c:459
__cache_free mm/slab.c:3432 [inline]
kfree+0xcf/0x220 mm/slab.c:3755
sk_prot_free net/core/sock.c:1636 [inline]
__sk_destruct+0x4f7/0x6e0 net/core/sock.c:1722
sk_destruct+0x7b/0x90 net/core/sock.c:1730
__sk_free+0xce/0x300 net/core/sock.c:1741
sk_free+0x42/0x50 net/core/sock.c:1752
sock_put include/net/sock.h:1725 [inline]
nr_destroy_socket+0x3df/0x4a0 net/netrom/af_netrom.c:288
nr_release+0x323/0x3b0 net/netrom/af_netrom.c:530
__sock_release+0xce/0x2a0 net/socket.c:601
sock_close+0x1b/0x30 net/socket.c:1273
__fput+0x2ff/0x890 fs/file_table.c:280
____fput+0x16/0x20 fs/file_table.c:313
task_work_run+0x145/0x1c0 kernel/task_work.c:113
tracehook_notify_resume include/linux/tracehook.h:185 [inline]
exit_to_usermode_loop+0x273/0x2c0 arch/x86/entry/common.c:168
prepare_exit_to_usermode arch/x86/entry/common.c:199 [inline]
syscall_return_slowpath arch/x86/entry/common.c:279 [inline]
do_syscall_64+0x58e/0x680 arch/x86/entry/common.c:304
entry_SYSCALL_64_after_hwframe+0x49/0xbe
The buggy address belongs to the object at ffff88809faed2c0
which belongs to the cache kmalloc-2k of size 2048
The buggy address is located 140 bytes inside of
2048-byte region [ffff88809faed2c0, ffff88809faedac0)
The buggy address belongs to the page:
page:ffffea00027ebb00 refcount:1 mapcount:0 mapping:ffff8880aa400c40
index:0x0 compound_mapcount: 0
flags: 0x1fffc0000010200(slab|head)
raw: 01fffc0000010200 ffffea000242cd08 ffffea00023df908 ffff8880aa400c40
raw: 0000000000000000 ffff88809faec1c0 0000000100000003 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff88809faed200: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
ffff88809faed280: fc fc fc fc fc fc fc fc fb fb fb fb fb fb fb fb
> ffff88809faed300: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff88809faed380: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff88809faed400: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
---
This bug is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this bug report. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
For information about bisection process see: https://goo.gl/tpsmEJ#bisection
syzbot can test patches for this bug, for details see:
https://goo.gl/tpsmEJ#testing-patches
^ permalink raw reply
* Re: [PATCH v3 net-next 19/19] ionic: Add basic devlink interface
From: Jiri Pirko @ 2019-07-08 19:34 UTC (permalink / raw)
To: Shannon Nelson; +Cc: netdev
In-Reply-To: <20190708192532.27420-20-snelson@pensando.io>
Mon, Jul 08, 2019 at 09:25:32PM CEST, snelson@pensando.io wrote:
>Add a devlink interface for access to information that isn't
>normally available through ethtool or the iplink interface.
>
>Example:
> $ ./devlink -j -p dev info pci/0000:b6:00.0
> {
> "info": {
> "pci/0000:b6:00.0": {
> "driver": "ionic",
> "serial_number": "FLM18420073",
> "versions": {
> "fixed": {
> "fw_version": "0.11.0-50",
> "fw_status": "0x1",
> "fw_heartbeat": "0x716ce",
> "asic_type": "0x0",
> "asic_rev": "0x0"
> }
> }
> }
> }
> }
>
>Signed-off-by: Shannon Nelson <snelson@pensando.io>
>---
> drivers/net/ethernet/pensando/ionic/Makefile | 2 +-
> drivers/net/ethernet/pensando/ionic/ionic.h | 1 +
> .../ethernet/pensando/ionic/ionic_bus_pci.c | 7 ++
> .../ethernet/pensando/ionic/ionic_devlink.c | 89 +++++++++++++++++++
> .../ethernet/pensando/ionic/ionic_devlink.h | 12 +++
> 5 files changed, 110 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_devlink.c
> create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_devlink.h
>
>diff --git a/drivers/net/ethernet/pensando/ionic/Makefile b/drivers/net/ethernet/pensando/ionic/Makefile
>index 4f3cfbf36c23..ce187c7b33a8 100644
>--- a/drivers/net/ethernet/pensando/ionic/Makefile
>+++ b/drivers/net/ethernet/pensando/ionic/Makefile
>@@ -5,4 +5,4 @@ obj-$(CONFIG_IONIC) := ionic.o
>
> ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o ionic_ethtool.o \
> ionic_lif.o ionic_rx_filter.o ionic_txrx.o ionic_debugfs.o \
>- ionic_stats.o
>+ ionic_stats.o ionic_devlink.o
>diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
>index cd08166f73a9..a0034bc5b4a1 100644
>--- a/drivers/net/ethernet/pensando/ionic/ionic.h
>+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
>@@ -44,6 +44,7 @@ struct ionic {
> DECLARE_BITMAP(intrs, INTR_CTRL_REGS_MAX);
> struct work_struct nb_work;
> struct notifier_block nb;
>+ struct devlink *dl;
> };
>
> struct ionic_admin_ctx {
>diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
>index 98c12b770c7f..a8c99254489f 100644
>--- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
>+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
>@@ -10,6 +10,7 @@
> #include "ionic_bus.h"
> #include "ionic_lif.h"
> #include "ionic_debugfs.h"
>+#include "ionic_devlink.h"
>
> /* Supported devices */
> static const struct pci_device_id ionic_id_table[] = {
>@@ -212,9 +213,14 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> goto err_out_deinit_lifs;
> }
>
>+ err = ionic_devlink_register(ionic);
>+ if (err)
>+ dev_err(dev, "Cannot register devlink (ignored): %d\n", err);
>+
> return 0;
>
> err_out_deinit_lifs:
>+ ionic_devlink_unregister(ionic);
> ionic_lifs_deinit(ionic);
> err_out_free_lifs:
> ionic_lifs_free(ionic);
>@@ -247,6 +253,7 @@ static void ionic_remove(struct pci_dev *pdev)
> struct ionic *ionic = pci_get_drvdata(pdev);
>
> if (ionic) {
>+ ionic_devlink_unregister(ionic);
> ionic_lifs_unregister(ionic);
> ionic_lifs_deinit(ionic);
> ionic_lifs_free(ionic);
>diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
>new file mode 100644
>index 000000000000..fbbfcdde292f
>--- /dev/null
>+++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
>@@ -0,0 +1,89 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
>+
>+#include <linux/module.h>
>+#include <linux/netdevice.h>
>+
>+#include "ionic.h"
>+#include "ionic_bus.h"
>+#include "ionic_lif.h"
>+#include "ionic_devlink.h"
>+
>+struct ionic_devlink {
>+ struct ionic *ionic;
>+};
>+
>+static int ionic_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct ionic *ionic = *(struct ionic **)devlink_priv(dl);
>+ struct ionic_dev *idev = &ionic->idev;
>+ char buf[16];
>+ u32 val;
>+
>+ devlink_info_driver_name_put(req, DRV_NAME);
>+
>+ devlink_info_version_fixed_put(req, "fw_version",
>+ idev->dev_info.fw_version);
>+
>+ val = ioread8(&idev->dev_info_regs->fw_status);
>+ snprintf(buf, sizeof(buf), "0x%x", val);
>+ devlink_info_version_fixed_put(req, "fw_status", buf);
>+
>+ val = ioread32(&idev->dev_info_regs->fw_heartbeat);
>+ snprintf(buf, sizeof(buf), "0x%x", val);
>+ devlink_info_version_fixed_put(req, "fw_heartbeat", buf);
>+
>+ snprintf(buf, sizeof(buf), "0x%x", idev->dev_info.asic_type);
>+ devlink_info_version_fixed_put(req, "asic_type", buf);
>+
>+ snprintf(buf, sizeof(buf), "0x%x", idev->dev_info.asic_rev);
>+ devlink_info_version_fixed_put(req, "asic_rev", buf);
>+
>+ devlink_info_serial_number_put(req, idev->dev_info.serial_num);
>+
>+ return 0;
>+}
>+
>+static const struct devlink_ops ionic_dl_ops = {
>+ .info_get = ionic_dl_info_get,
>+};
>+
>+int ionic_devlink_register(struct ionic *ionic)
>+{
>+ struct devlink *dl;
>+ struct ionic **ip;
>+ int err;
>+
>+ dl = devlink_alloc(&ionic_dl_ops, sizeof(struct ionic *));
Oups. Something is wrong with your flow. The devlink alloc is allocating
the structure that holds private data (per-device data) for you. This is
misuse :/
You are missing one parent device struct apparently.
Oh, I think I see something like it. The unused "struct ionic_devlink".
>+ if (!dl) {
>+ dev_warn(ionic->dev, "devlink_alloc failed");
>+ return -ENOMEM;
>+ }
>+
>+ ip = (struct ionic **)devlink_priv(dl);
>+ *ip = ionic;
>+ ionic->dl = dl;
>+
>+ err = devlink_register(dl, ionic->dev);
>+ if (err) {
>+ dev_warn(ionic->dev, "devlink_register failed: %d\n", err);
>+ goto err_dl_free;
>+ }
>+
>+ return 0;
>+
>+err_dl_free:
>+ ionic->dl = NULL;
>+ devlink_free(dl);
>+ return err;
>+}
>+
>+void ionic_devlink_unregister(struct ionic *ionic)
>+{
>+ if (!ionic->dl)
>+ return;
>+
>+ devlink_unregister(ionic->dl);
>+ devlink_free(ionic->dl);
>+}
>diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.h b/drivers/net/ethernet/pensando/ionic/ionic_devlink.h
>new file mode 100644
>index 000000000000..35528884e29f
>--- /dev/null
>+++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.h
>@@ -0,0 +1,12 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
>+
>+#ifndef _IONIC_DEVLINK_H_
>+#define _IONIC_DEVLINK_H_
>+
>+#include <net/devlink.h>
>+
>+int ionic_devlink_register(struct ionic *ionic);
>+void ionic_devlink_unregister(struct ionic *ionic);
>+
>+#endif /* _IONIC_DEVLINK_H_ */
>--
>2.17.1
>
^ permalink raw reply
* Re: [PATCH net-next v6 04/15] ethtool: introduce ethtool netlink interface
From: Jiri Pirko @ 2019-07-08 19:28 UTC (permalink / raw)
To: Michal Kubecek
Cc: netdev, David Miller, Jakub Kicinski, Andrew Lunn,
Florian Fainelli, John Linville, Stephen Hemminger, Johannes Berg,
linux-kernel
In-Reply-To: <20190708192629.GD2282@nanopsycho.orion>
Mon, Jul 08, 2019 at 09:26:29PM CEST, jiri@resnulli.us wrote:
>Mon, Jul 08, 2019 at 07:27:29PM CEST, mkubecek@suse.cz wrote:
>>On Wed, Jul 03, 2019 at 10:41:51AM +0200, Jiri Pirko wrote:
>>> Tue, Jul 02, 2019 at 04:52:41PM CEST, mkubecek@suse.cz wrote:
>>> >On Tue, Jul 02, 2019 at 02:25:21PM +0200, Jiri Pirko wrote:
>>> >> Tue, Jul 02, 2019 at 01:49:59PM CEST, mkubecek@suse.cz wrote:
>>> >> >+
>>> >> >+ ETHTOOL_A_HEADER_DEV_INDEX (u32) device ifindex
>>> >> >+ ETHTOOL_A_HEADER_DEV_NAME (string) device name
>>> >> >+ ETHTOOL_A_HEADER_INFOMASK (u32) info mask
>>> >> >+ ETHTOOL_A_HEADER_GFLAGS (u32) flags common for all requests
>>> >> >+ ETHTOOL_A_HEADER_RFLAGS (u32) request specific flags
>>> >> >+
>>> >> >+ETHTOOL_A_HEADER_DEV_INDEX and ETHTOOL_A_HEADER_DEV_NAME identify the device
>>> >> >+message relates to. One of them is sufficient in requests, if both are used,
>>> >> >+they must identify the same device. Some requests, e.g. global string sets, do
>>> >> >+not require device identification. Most GET requests also allow dump requests
>>> >> >+without device identification to query the same information for all devices
>>> >> >+providing it (each device in a separate message).
>>> >> >+
>>> >> >+Optional info mask allows to ask only for a part of data provided by GET
>>> >>
>>> >> How this "infomask" works? What are the bits related to? Is that request
>>> >> specific?
>>> >
>>> >The interpretation is request specific, the information returned for
>>> >a GET request is divided into multiple parts and client can choose to
>>> >request one of them (usually one). In the code so far, infomask bits
>>> >correspond to top level (nest) attributes but I would rather not make it
>>> >a strict rule.
>>>
>>> Wait, so it is a matter of verbosity? If you have multiple parts and the
>>> user is able to chose one of them, why don't you rather have multiple
>>> get commands, one per bit. This infomask construct seems redundant to me.
>>
>>I thought it was a matter of verbosity because it is a very basic
>>element of the design, it was even advertised in the cover letter among
>>the basic ideas, it has been there since the very beginning and in five
>>previous versions through year and a half, noone did question it. That's
>>why I thought you objected against unclear description, not against the
>>concept as such.
>>
>>There are two reasons for this design. First is to reduce the number of
>>requests needed to get the information. This is not so much a problem of
>>ethtool itself; the only existing commands that would result in multiple
>>request messages would be "ethtool <dev>" and "ethtool -s <dev>". Maybe
>>also "ethtool -x/-X <dev>" but even if the indirection table and hash
>>key have different bits assigned now, they don't have to be split even
>>if we split other commands. It may be bigger problem for daemons wanting
>>to keep track of system configuration which would have to issue many
>>requests whenever a new device appears.
>>
>>Second reason is that with 8-bit genetlink command/message id, the space
>>is not as infinite as it might seem. I counted quickly, right now the
>>full series uses 14 ids for kernel messages, with split you propose it
>>would most likely grow to 44. For full implementation of all ethtool
>>functionality, we could get to ~60 ids. It's still only 1/4 of the
>>available space but it's not clear what the future development will look
>>like. We would certainly need to be careful not to start allocating new
>>commands for single parameters and try to be foreseeing about what can
>>be grouped together. But we will need to do that in any case.
>>
>>On kernel side, splitting existing messages would make some things a bit
>>easier. It would also reduce the number of scenarios where only part of
>>requested information is available or only part of a SET request fails.
>
>Okay, I got your point. So why don't we look at if from the other angle.
>Why don't we have only single get/set command that would be in general
>used to get/set ALL info from/to the kernel. Where we can have these
>bits (perhaps rather varlen bitfield) to for user to indicate which data
>is he interested in? This scales. The other commands would be
>just for action.
>
>Something like RTM_GETLINK/RTM_SETLINK. Makes sense?
+ I think this might safe a lot of complexicity aroung your proposed
inner ops.
>
>
>>
>>Michal
^ permalink raw reply
* Re: [PATCH v3 bpf-next 4/9] libbpf: add kprobe/uprobe attach API
From: Matt Hart @ 2019-07-08 19:27 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: Andrii Nakryiko, Alexei Starovoitov, bpf, Daniel Borkmann,
Kernel Team, Networking, Stanislav Fomichev
In-Reply-To: <CAEf4Bzb-EM41TLAkshQa=nVwiVuYnEYyhVL38gcaG=OaHoJJ6Q@mail.gmail.com>
On Mon, 8 Jul 2019 at 18:58, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
>
> On Mon, Jul 8, 2019 at 8:11 AM Matt Hart <matthew.hart@linaro.org> wrote:
> >
> > Hi all,
> >
> > I bisected a perf build error on ARMv7 to this patch:
> > libbpf.c: In function ‘perf_event_open_probe’:
> > libbpf.c:4112:17: error: cast from pointer to integer of different
> > size [-Werror=pointer-to-int-cast]
> > attr.config1 = (uint64_t)(void *)name; /* kprobe_func or uprobe_path */
> > ^
> >
> > Is this a known issue?
>
> No, thanks for reporting!
>
> It should be
>
> attr.config1 = (uint64_t)(uintptr_t)(void *)name;
>
> to avoid warning on 32-bit architectures.
Tested with manual change and can confirm perf now builds without errors.
>
> I'll post a fix later today, but if you could verify this fixes
> warning for you, I'd really appreciate that! Thanks!
^ permalink raw reply
* Re: [PATCH net-next v6 04/15] ethtool: introduce ethtool netlink interface
From: Jiri Pirko @ 2019-07-08 19:26 UTC (permalink / raw)
To: Michal Kubecek
Cc: netdev, David Miller, Jakub Kicinski, Andrew Lunn,
Florian Fainelli, John Linville, Stephen Hemminger, Johannes Berg,
linux-kernel
In-Reply-To: <20190708172729.GC24474@unicorn.suse.cz>
Mon, Jul 08, 2019 at 07:27:29PM CEST, mkubecek@suse.cz wrote:
>On Wed, Jul 03, 2019 at 10:41:51AM +0200, Jiri Pirko wrote:
>> Tue, Jul 02, 2019 at 04:52:41PM CEST, mkubecek@suse.cz wrote:
>> >On Tue, Jul 02, 2019 at 02:25:21PM +0200, Jiri Pirko wrote:
>> >> Tue, Jul 02, 2019 at 01:49:59PM CEST, mkubecek@suse.cz wrote:
>> >> >+
>> >> >+ ETHTOOL_A_HEADER_DEV_INDEX (u32) device ifindex
>> >> >+ ETHTOOL_A_HEADER_DEV_NAME (string) device name
>> >> >+ ETHTOOL_A_HEADER_INFOMASK (u32) info mask
>> >> >+ ETHTOOL_A_HEADER_GFLAGS (u32) flags common for all requests
>> >> >+ ETHTOOL_A_HEADER_RFLAGS (u32) request specific flags
>> >> >+
>> >> >+ETHTOOL_A_HEADER_DEV_INDEX and ETHTOOL_A_HEADER_DEV_NAME identify the device
>> >> >+message relates to. One of them is sufficient in requests, if both are used,
>> >> >+they must identify the same device. Some requests, e.g. global string sets, do
>> >> >+not require device identification. Most GET requests also allow dump requests
>> >> >+without device identification to query the same information for all devices
>> >> >+providing it (each device in a separate message).
>> >> >+
>> >> >+Optional info mask allows to ask only for a part of data provided by GET
>> >>
>> >> How this "infomask" works? What are the bits related to? Is that request
>> >> specific?
>> >
>> >The interpretation is request specific, the information returned for
>> >a GET request is divided into multiple parts and client can choose to
>> >request one of them (usually one). In the code so far, infomask bits
>> >correspond to top level (nest) attributes but I would rather not make it
>> >a strict rule.
>>
>> Wait, so it is a matter of verbosity? If you have multiple parts and the
>> user is able to chose one of them, why don't you rather have multiple
>> get commands, one per bit. This infomask construct seems redundant to me.
>
>I thought it was a matter of verbosity because it is a very basic
>element of the design, it was even advertised in the cover letter among
>the basic ideas, it has been there since the very beginning and in five
>previous versions through year and a half, noone did question it. That's
>why I thought you objected against unclear description, not against the
>concept as such.
>
>There are two reasons for this design. First is to reduce the number of
>requests needed to get the information. This is not so much a problem of
>ethtool itself; the only existing commands that would result in multiple
>request messages would be "ethtool <dev>" and "ethtool -s <dev>". Maybe
>also "ethtool -x/-X <dev>" but even if the indirection table and hash
>key have different bits assigned now, they don't have to be split even
>if we split other commands. It may be bigger problem for daemons wanting
>to keep track of system configuration which would have to issue many
>requests whenever a new device appears.
>
>Second reason is that with 8-bit genetlink command/message id, the space
>is not as infinite as it might seem. I counted quickly, right now the
>full series uses 14 ids for kernel messages, with split you propose it
>would most likely grow to 44. For full implementation of all ethtool
>functionality, we could get to ~60 ids. It's still only 1/4 of the
>available space but it's not clear what the future development will look
>like. We would certainly need to be careful not to start allocating new
>commands for single parameters and try to be foreseeing about what can
>be grouped together. But we will need to do that in any case.
>
>On kernel side, splitting existing messages would make some things a bit
>easier. It would also reduce the number of scenarios where only part of
>requested information is available or only part of a SET request fails.
Okay, I got your point. So why don't we look at if from the other angle.
Why don't we have only single get/set command that would be in general
used to get/set ALL info from/to the kernel. Where we can have these
bits (perhaps rather varlen bitfield) to for user to indicate which data
is he interested in? This scales. The other commands would be
just for action.
Something like RTM_GETLINK/RTM_SETLINK. Makes sense?
>
>Michal
^ permalink raw reply
* [PATCH v3 1/7] dt-bindings: net: Add bindings for Realtek PHYs
From: Matthias Kaehlcke @ 2019-07-08 19:24 UTC (permalink / raw)
To: David S . Miller, Rob Herring, Mark Rutland, Andrew Lunn,
Florian Fainelli, Heiner Kallweit
Cc: netdev, devicetree, linux-kernel, Douglas Anderson,
Matthias Kaehlcke
In-Reply-To: <20190708192459.187984-1-mka@chromium.org>
Add the 'realtek,eee-led-mode-disable' property to disable EEE
LED mode on Realtek PHYs that support it.
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
---
TODO: adapt PHY core to deal with optional compatible strings
Changes in v3:
- added entry for compatible string
- added compatible string to example
- mention that the new property is only available for RTL8211E
Changes in v2:
- document 'realtek,eee-led-mode-disable' instead of
'realtek,enable-ssc' in the initial version
---
.../devicetree/bindings/net/realtek.txt | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/realtek.txt
diff --git a/Documentation/devicetree/bindings/net/realtek.txt b/Documentation/devicetree/bindings/net/realtek.txt
new file mode 100644
index 000000000000..db0333f23fec
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/realtek.txt
@@ -0,0 +1,31 @@
+Realtek PHY properties.
+
+This document describes properties of Realtek PHYs.
+
+Optional properties:
+- compatible: should be one of the following:
+ "realtek,rtl8201cp", "realtek,rtl8201f", "realtek,rtl8211",
+ "realtek,rtl8211b", "realtek,rtl8211c", "realtek,rtl8211dn",
+ "realtek,rtl8211e", "realtek,rtl8211f", "rtl8366rb"
+
+ the property is required if any of the properties are specified that
+ are only supported for certain Realtek PHYs.
+
+- realtek,eee-led-mode-disable: Disable EEE LED mode on this port.
+
+ Only supported for "realtek,rtl8211e".
+
+
+Example:
+
+mdio0 {
+ compatible = "snps,dwmac-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethphy: ethernet-phy@1 {
+ compatible = "realtek,rtl8211e";
+ reg = <1>;
+ realtek,eee-led-mode-disable;
+ };
+};
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH v3 net-next 01/19] ionic: Add basic framework for IONIC Network device driver
From: Shannon Nelson @ 2019-07-08 19:25 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190708192532.27420-1-snelson@pensando.io>
This patch adds a basic driver framework for the Pensando IONIC
network device. There is no functionality right now other than
the ability to load and unload.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
.../networking/device_drivers/index.rst | 1 +
.../device_drivers/pensando/ionic.rst | 64 +++++++++++++++++++
MAINTAINERS | 8 +++
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/pensando/Kconfig | 32 ++++++++++
drivers/net/ethernet/pensando/Makefile | 6 ++
drivers/net/ethernet/pensando/ionic/Makefile | 6 ++
drivers/net/ethernet/pensando/ionic/ionic.h | 25 ++++++++
.../net/ethernet/pensando/ionic/ionic_bus.h | 10 +++
.../ethernet/pensando/ionic/ionic_bus_pci.c | 58 +++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_main.c | 29 +++++++++
12 files changed, 241 insertions(+)
create mode 100644 Documentation/networking/device_drivers/pensando/ionic.rst
create mode 100644 drivers/net/ethernet/pensando/Kconfig
create mode 100644 drivers/net/ethernet/pensando/Makefile
create mode 100644 drivers/net/ethernet/pensando/ionic/Makefile
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic.h
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_bus.h
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_main.c
diff --git a/Documentation/networking/device_drivers/index.rst b/Documentation/networking/device_drivers/index.rst
index 2b7fefe72351..57fec66c5419 100644
--- a/Documentation/networking/device_drivers/index.rst
+++ b/Documentation/networking/device_drivers/index.rst
@@ -23,6 +23,7 @@ Contents:
intel/ice
google/gve
mellanox/mlx5
+ pensando/ionic
.. only:: subproject
diff --git a/Documentation/networking/device_drivers/pensando/ionic.rst b/Documentation/networking/device_drivers/pensando/ionic.rst
new file mode 100644
index 000000000000..6b05d1dad533
--- /dev/null
+++ b/Documentation/networking/device_drivers/pensando/ionic.rst
@@ -0,0 +1,64 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+==========================================================
+Linux* Driver for the Pensando(R) Ethernet adapter family
+==========================================================
+
+Pensando Linux Ethernet driver.
+Copyright(c) 2019 Pensando Systems, Inc
+
+Contents
+========
+
+- Identifying the Adapter
+- Special Features
+- Support
+
+Identifying the Adapter
+=======================
+
+To find if one or more Pensando PCI Ethernet devices are installed on the
+host, check for the PCI devices::
+
+ $ lspci -d 1dd8:
+ b5:00.0 Ethernet controller: Device 1dd8:1002
+ b6:00.0 Ethernet controller: Device 1dd8:1002
+
+If such devices are listed as above, then the ionic.ko driver should find
+and configure them for use. There should be log entries in the kernel
+messages such as these::
+
+ $ dmesg | grep ionic
+ ionic Pensando Ethernet NIC Driver, ver 0.11.0-k
+ ionic 0000:b5:00.0 enp181s0: renamed from eth0
+ ionic 0000:b5:00.0: NETDEV_CHANGENAME lif0 enp181s0
+ ionic 0000:b6:00.0 enp182s0: renamed from eth0
+ ionic 0000:b6:00.0: NETDEV_CHANGENAME lif0 enp182s0
+
+Special Features
+================
+
+Extended Debug Statistics
+-------------------------
+Basic network driver statistics are available through ethtool's
+statistics request::
+
+ $ ethtool -S enp181s0
+
+Extended debugging statistics can be enabled with the driver private
+flag "sw-dbg-stats"::
+
+ $ ethtool --show-priv-flags enp181s0
+ Private flags for enp181s0:
+ sw-dbg-stats: off
+ $ ethtool --set-priv-flags enp181s0 sw-dbg-stats on
+
+Support
+=======
+For general Linux networking support, please use the netdev mailing
+list, which is monitored by Pensando personnel::
+ netdev@vger.kernel.org
+
+For more specific support needs, please use the Pensando driver support
+email::
+ drivers@pensando.io
diff --git a/MAINTAINERS b/MAINTAINERS
index 449e7cdb3303..0217242e812b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12364,6 +12364,14 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/peaq-wmi.c
+PENSANDO ETHERNET DRIVERS
+M: Shannon Nelson <snelson@pensando.io>
+M: Pensando Drivers <drivers@pensando.io>
+L: netdev@vger.kernel.org
+S: Supported
+F: Documentation/networking/device_drivers/pensando/ionic.rst
+F: drivers/net/ethernet/pensando/
+
PER-CPU MEMORY ALLOCATOR
M: Dennis Zhou <dennis@kernel.org>
M: Tejun Heo <tj@kernel.org>
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 93a2d4deb27c..2830dc283ce6 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -168,6 +168,7 @@ config ETHOC
source "drivers/net/ethernet/packetengines/Kconfig"
source "drivers/net/ethernet/pasemi/Kconfig"
+source "drivers/net/ethernet/pensando/Kconfig"
source "drivers/net/ethernet/qlogic/Kconfig"
source "drivers/net/ethernet/qualcomm/Kconfig"
source "drivers/net/ethernet/rdc/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index fb9155cffcff..061edd22f507 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -97,3 +97,4 @@ obj-$(CONFIG_NET_VENDOR_WIZNET) += wiznet/
obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/
obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/
obj-$(CONFIG_NET_VENDOR_SYNOPSYS) += synopsys/
+obj-$(CONFIG_NET_VENDOR_PENSANDO) += pensando/
diff --git a/drivers/net/ethernet/pensando/Kconfig b/drivers/net/ethernet/pensando/Kconfig
new file mode 100644
index 000000000000..5ea570be8379
--- /dev/null
+++ b/drivers/net/ethernet/pensando/Kconfig
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2019 Pensando Systems, Inc
+#
+# Pensando device configuration
+#
+
+config NET_VENDOR_PENSANDO
+ bool "Pensando devices"
+ default y
+ help
+ If you have a network (Ethernet) card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Pensando cards. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_PENSANDO
+
+config IONIC
+ tristate "Pensando Ethernet IONIC Support"
+ depends on 64BIT && PCI
+ help
+ This enables the support for the Pensando family of Ethernet
+ adapters. More specific information on this driver can be
+ found in
+ <file:Documentation/networking/device_drivers/pensando/ionic.rst>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called ionic.
+
+endif # NET_VENDOR_PENSANDO
diff --git a/drivers/net/ethernet/pensando/Makefile b/drivers/net/ethernet/pensando/Makefile
new file mode 100644
index 000000000000..21ce7499c122
--- /dev/null
+++ b/drivers/net/ethernet/pensando/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Pensando network device drivers.
+#
+
+obj-$(CONFIG_IONIC) += ionic/
diff --git a/drivers/net/ethernet/pensando/ionic/Makefile b/drivers/net/ethernet/pensando/ionic/Makefile
new file mode 100644
index 000000000000..beb3faeccac1
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright(c) 2017 - 2019 Pensando Systems, Inc
+
+obj-$(CONFIG_IONIC) := ionic.o
+
+ionic-y := ionic_main.o ionic_bus_pci.o
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
new file mode 100644
index 000000000000..18c79adef06c
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#ifndef _IONIC_H_
+#define _IONIC_H_
+
+#define DRV_NAME "ionic"
+#define DRV_DESCRIPTION "Pensando Ethernet NIC Driver"
+#define DRV_VERSION "0.11.0-k"
+
+#define PCI_VENDOR_ID_PENSANDO 0x1dd8
+
+#define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_PF 0x1002
+#define PCI_DEVICE_ID_PENSANDO_IONIC_ETH_VF 0x1003
+
+#define IONIC_SUBDEV_ID_NAPLES_25 0x4000
+#define IONIC_SUBDEV_ID_NAPLES_100_4 0x4001
+#define IONIC_SUBDEV_ID_NAPLES_100_8 0x4002
+
+struct ionic {
+ struct pci_dev *pdev;
+ struct device *dev;
+};
+
+#endif /* _IONIC_H_ */
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus.h b/drivers/net/ethernet/pensando/ionic/ionic_bus.h
new file mode 100644
index 000000000000..94ba0afc6f38
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#ifndef _IONIC_BUS_H_
+#define _IONIC_BUS_H_
+
+int ionic_bus_register_driver(void);
+void ionic_bus_unregister_driver(void);
+
+#endif /* _IONIC_BUS_H_ */
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
new file mode 100644
index 000000000000..3fc3479c85f1
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/pci.h>
+
+#include "ionic.h"
+#include "ionic_bus.h"
+
+/* Supported devices */
+static const struct pci_device_id ionic_id_table[] = {
+ { PCI_VDEVICE(PENSANDO, PCI_DEVICE_ID_PENSANDO_IONIC_ETH_PF) },
+ { PCI_VDEVICE(PENSANDO, PCI_DEVICE_ID_PENSANDO_IONIC_ETH_VF) },
+ { 0, } /* end of table */
+};
+MODULE_DEVICE_TABLE(pci, ionic_id_table);
+
+static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct device *dev = &pdev->dev;
+ struct ionic *ionic;
+
+ ionic = devm_kzalloc(dev, sizeof(*ionic), GFP_KERNEL);
+ if (!ionic)
+ return -ENOMEM;
+
+ ionic->pdev = pdev;
+ pci_set_drvdata(pdev, ionic);
+ ionic->dev = dev;
+
+ return 0;
+}
+
+static void ionic_remove(struct pci_dev *pdev)
+{
+ struct ionic *ionic = pci_get_drvdata(pdev);
+
+ devm_kfree(&pdev->dev, ionic);
+}
+
+static struct pci_driver ionic_driver = {
+ .name = DRV_NAME,
+ .id_table = ionic_id_table,
+ .probe = ionic_probe,
+ .remove = ionic_remove,
+};
+
+int ionic_bus_register_driver(void)
+{
+ return pci_register_driver(&ionic_driver);
+}
+
+void ionic_bus_unregister_driver(void)
+{
+ pci_unregister_driver(&ionic_driver);
+}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
new file mode 100644
index 000000000000..fb49c0ca5095
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/netdevice.h>
+#include <linux/utsname.h>
+
+#include "ionic.h"
+#include "ionic_bus.h"
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR("Pensando Systems, Inc");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+static int __init ionic_init_module(void)
+{
+ pr_info("%s %s, ver %s\n", DRV_NAME, DRV_DESCRIPTION, DRV_VERSION);
+ return ionic_bus_register_driver();
+}
+
+static void __exit ionic_cleanup_module(void)
+{
+ ionic_bus_unregister_driver();
+}
+
+module_init(ionic_init_module);
+module_exit(ionic_cleanup_module);
--
2.17.1
^ permalink raw reply related
* [PATCH v3 net-next 08/19] ionic: Add notifyq support
From: Shannon Nelson @ 2019-07-08 19:25 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190708192532.27420-1-snelson@pensando.io>
The AdminQ is fine for sending messages and requests to the NIC,
but we also need to have events published from the NIC to the
driver. The NotifyQ handles this for us, using the same interrupt
as AdminQ.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
.../ethernet/pensando/ionic/ionic_debugfs.c | 16 ++
.../net/ethernet/pensando/ionic/ionic_lif.c | 181 +++++++++++++++++-
.../net/ethernet/pensando/ionic/ionic_lif.h | 4 +
3 files changed, 200 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
index b5e77d35ce6f..cc13198a16f9 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
@@ -130,6 +130,7 @@ int ionic_debugfs_add_qcq(struct lif *lif, struct qcq *qcq)
struct debugfs_blob_wrapper *desc_blob;
struct device *dev = lif->ionic->dev;
struct intr *intr = &qcq->intr;
+ struct dentry *stats_dentry;
struct queue *q = &qcq->q;
struct cq *cq = &qcq->cq;
@@ -223,6 +224,21 @@ int ionic_debugfs_add_qcq(struct lif *lif, struct qcq *qcq)
intr_ctrl_regset);
}
+ if (qcq->flags & QCQ_F_NOTIFYQ) {
+ stats_dentry = debugfs_create_dir("notifyblock", qcq_dentry);
+ if (IS_ERR_OR_NULL(stats_dentry))
+ return PTR_ERR(stats_dentry);
+
+ debugfs_create_u64("eid", 0400, stats_dentry,
+ (u64 *)&lif->info->status.eid);
+ debugfs_create_u16("link_status", 0400, stats_dentry,
+ (u16 *)&lif->info->status.link_status);
+ debugfs_create_u32("link_speed", 0400, stats_dentry,
+ (u32 *)&lif->info->status.link_speed);
+ debugfs_create_u16("link_down_count", 0400, stats_dentry,
+ (u16 *)&lif->info->status.link_down_count);
+ }
+
return 0;
}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 19c046502a26..01f9665611d4 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -12,6 +12,8 @@
#include "ionic_lif.h"
#include "ionic_debugfs.h"
+static int ionic_notifyq_clean(struct lif *lif, int budget);
+
static bool ionic_adminq_service(struct cq *cq, struct cq_info *cq_info)
{
struct admin_comp *comp = cq_info->cq_desc;
@@ -26,7 +28,90 @@ static bool ionic_adminq_service(struct cq *cq, struct cq_info *cq_info)
static int ionic_adminq_napi(struct napi_struct *napi, int budget)
{
- return ionic_napi(napi, budget, ionic_adminq_service, NULL, NULL);
+ struct lif *lif = napi_to_cq(napi)->lif;
+ int n_work = 0;
+ int a_work = 0;
+
+ if (likely(lif->notifyqcq && lif->notifyqcq->flags & QCQ_F_INITED))
+ n_work = ionic_notifyq_clean(lif, budget);
+ a_work = ionic_napi(napi, budget, ionic_adminq_service, NULL, NULL);
+
+ return max(n_work, a_work);
+}
+
+static bool ionic_notifyq_service(struct cq *cq, struct cq_info *cq_info)
+{
+ union notifyq_comp *comp = cq_info->cq_desc;
+ struct net_device *netdev;
+ struct queue *q;
+ struct lif *lif;
+ u64 eid;
+
+ q = cq->bound_q;
+ lif = q->info[0].cb_arg;
+ netdev = lif->netdev;
+ eid = le64_to_cpu(comp->event.eid);
+
+ /* Have we run out of new completions to process? */
+ if (eid <= lif->last_eid)
+ return false;
+
+ lif->last_eid = eid;
+
+ dev_dbg(lif->ionic->dev, "notifyq event:\n");
+ dynamic_hex_dump("event ", DUMP_PREFIX_OFFSET, 16, 1,
+ comp, sizeof(*comp), true);
+
+ switch (le16_to_cpu(comp->event.ecode)) {
+ case EVENT_OPCODE_LINK_CHANGE:
+ netdev_info(netdev, "Notifyq EVENT_OPCODE_LINK_CHANGE eid=%lld\n",
+ eid);
+ netdev_info(netdev,
+ " link_status=%d link_speed=%d\n",
+ le16_to_cpu(comp->link_change.link_status),
+ le32_to_cpu(comp->link_change.link_speed));
+ break;
+ case EVENT_OPCODE_RESET:
+ netdev_info(netdev, "Notifyq EVENT_OPCODE_RESET eid=%lld\n",
+ eid);
+ netdev_info(netdev, " reset_code=%d state=%d\n",
+ comp->reset.reset_code,
+ comp->reset.state);
+ break;
+ case EVENT_OPCODE_LOG:
+ netdev_info(netdev, "Notifyq EVENT_OPCODE_LOG eid=%lld\n", eid);
+ print_hex_dump(KERN_INFO, "notifyq ", DUMP_PREFIX_OFFSET, 16, 1,
+ comp->log.data, sizeof(comp->log.data), true);
+ break;
+ default:
+ netdev_warn(netdev, "Notifyq unknown event ecode=%d eid=%lld\n",
+ comp->event.ecode, eid);
+ break;
+ }
+
+ return true;
+}
+
+static int ionic_notifyq_clean(struct lif *lif, int budget)
+{
+ struct ionic_dev *idev = &lif->ionic->idev;
+ struct cq *cq = &lif->notifyqcq->cq;
+ u32 work_done;
+
+ work_done = ionic_cq_service(cq, budget, ionic_notifyq_service,
+ NULL, NULL);
+ if (work_done)
+ ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index,
+ work_done, IONIC_INTR_CRED_RESET_COALESCE);
+
+ /* If we ran out of budget, there are more events
+ * to process and napi will reschedule us soon
+ */
+ if (work_done == budget)
+ goto return_to_napi;
+
+return_to_napi:
+ return work_done;
}
static irqreturn_t ionic_isr(int irq, void *data)
@@ -62,6 +147,17 @@ static void ionic_intr_free(struct lif *lif, int index)
clear_bit(index, lif->ionic->intrs);
}
+static void ionic_link_qcq_interrupts(struct qcq *src_qcq, struct qcq *n_qcq)
+{
+ if (WARN_ON(n_qcq->flags & QCQ_F_INTR)) {
+ ionic_intr_free(n_qcq->cq.lif, n_qcq->intr.index);
+ n_qcq->flags &= ~QCQ_F_INTR;
+ }
+
+ n_qcq->intr.vector = src_qcq->intr.vector;
+ n_qcq->intr.index = src_qcq->intr.index;
+}
+
static int ionic_qcq_alloc(struct lif *lif, unsigned int type,
unsigned int index,
const char *name, unsigned int flags,
@@ -236,11 +332,36 @@ static int ionic_qcqs_alloc(struct lif *lif)
if (err)
return err;
+ if (lif->ionic->nnqs_per_lif) {
+ flags = QCQ_F_NOTIFYQ;
+ err = ionic_qcq_alloc(lif, IONIC_QTYPE_NOTIFYQ, 0, "notifyq",
+ flags, IONIC_NOTIFYQ_LENGTH,
+ sizeof(struct notifyq_cmd),
+ sizeof(union notifyq_comp),
+ 0, lif->kern_pid, &lif->notifyqcq);
+ if (err)
+ goto err_out_free_adminqcq;
+
+ /* Let the notifyq ride on the adminq interrupt */
+ ionic_link_qcq_interrupts(lif->adminqcq, lif->notifyqcq);
+ }
+
return 0;
+
+err_out_free_adminqcq:
+ ionic_qcq_free(lif, lif->adminqcq);
+ lif->adminqcq = NULL;
+
+ return err;
}
static void ionic_qcqs_free(struct lif *lif)
{
+ if (lif->notifyqcq) {
+ ionic_qcq_free(lif, lif->notifyqcq);
+ lif->notifyqcq = NULL;
+ }
+
if (lif->adminqcq) {
ionic_qcq_free(lif, lif->adminqcq);
lif->adminqcq = NULL;
@@ -400,6 +521,7 @@ static void ionic_lif_deinit(struct lif *lif)
clear_bit(LIF_INITED, lif->state);
napi_disable(&lif->adminqcq->napi);
+ ionic_lif_qcq_deinit(lif, lif->notifyqcq);
ionic_lif_qcq_deinit(lif, lif->adminqcq);
ionic_lif_reset(lif);
@@ -484,6 +606,55 @@ static int ionic_lif_adminq_init(struct lif *lif)
return 0;
}
+static int ionic_lif_notifyq_init(struct lif *lif)
+{
+ struct device *dev = lif->ionic->dev;
+ struct qcq *qcq = lif->notifyqcq;
+ struct queue *q = &qcq->q;
+ int err;
+
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.q_init = {
+ .opcode = CMD_OPCODE_Q_INIT,
+ .lif_index = cpu_to_le16(lif->index),
+ .type = q->type,
+ .index = cpu_to_le32(q->index),
+ .flags = cpu_to_le16(IONIC_QINIT_F_IRQ |
+ IONIC_QINIT_F_ENA),
+ .intr_index = cpu_to_le16(lif->adminqcq->intr.index),
+ .pid = cpu_to_le16(q->pid),
+ .ring_size = ilog2(q->num_descs),
+ .ring_base = cpu_to_le64(q->base_pa),
+ }
+ };
+
+ dev_dbg(dev, "notifyq_init.pid %d\n", ctx.cmd.q_init.pid);
+ dev_dbg(dev, "notifyq_init.index %d\n", ctx.cmd.q_init.index);
+ dev_dbg(dev, "notifyq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base);
+ dev_dbg(dev, "notifyq_init.ring_size %d\n", ctx.cmd.q_init.ring_size);
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ q->hw_type = ctx.comp.q_init.hw_type;
+ q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index);
+ q->dbval = IONIC_DBELL_QID(q->hw_index);
+
+ dev_dbg(dev, "notifyq->hw_type %d\n", q->hw_type);
+ dev_dbg(dev, "notifyq->hw_index %d\n", q->hw_index);
+
+ /* preset the callback info */
+ q->info[0].cb_arg = lif;
+
+ qcq->flags |= QCQ_F_INITED;
+
+ ionic_debugfs_add_qcq(lif, qcq);
+
+ return 0;
+}
+
static int ionic_lif_init(struct lif *lif)
{
struct ionic_dev *idev = &lif->ionic->idev;
@@ -534,10 +705,18 @@ static int ionic_lif_init(struct lif *lif)
if (err)
goto err_out_adminq_deinit;
+ if (lif->ionic->nnqs_per_lif) {
+ err = ionic_lif_notifyq_init(lif);
+ if (err)
+ goto err_out_notifyq_deinit;
+ }
+
set_bit(LIF_INITED, lif->state);
return 0;
+err_out_notifyq_deinit:
+ ionic_lif_qcq_deinit(lif, lif->notifyqcq);
err_out_adminq_deinit:
ionic_lif_qcq_deinit(lif, lif->adminqcq);
ionic_lif_reset(lif);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 28ab92b43a64..80eec0778f40 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -7,6 +7,7 @@
#include <linux/pci.h>
#define IONIC_ADMINQ_LENGTH 16 /* must be a power of two */
+#define IONIC_NOTIFYQ_LENGTH 64 /* must be a power of two */
#define GET_NAPI_CNTR_IDX(work_done) (work_done)
#define MAX_NUM_NAPI_CNTR (NAPI_POLL_WEIGHT + 1)
@@ -26,6 +27,7 @@ struct rx_stats {
#define QCQ_F_INITED BIT(0)
#define QCQ_F_SG BIT(1)
#define QCQ_F_INTR BIT(2)
+#define QCQ_F_NOTIFYQ BIT(5)
struct napi_stats {
u64 poll_count;
@@ -78,6 +80,8 @@ struct lif {
u64 __iomem *kern_dbpage;
spinlock_t adminq_lock; /* lock for AdminQ operations */
struct qcq *adminqcq;
+ struct qcq *notifyqcq;
+ u64 last_eid;
unsigned int neqs;
unsigned int nxqs;
--
2.17.1
^ permalink raw reply related
* [PATCH v3 net-next 09/19] ionic: Add the basic NDO callbacks for netdev support
From: Shannon Nelson @ 2019-07-08 19:25 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190708192532.27420-1-snelson@pensando.io>
Set up the initial NDO structure and callbacks for netdev
to use, and register the netdev. This will allow us to do
a few basic operations on the device, but no traffic yet.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/ionic.h | 1 +
.../ethernet/pensando/ionic/ionic_bus_pci.c | 9 +
.../net/ethernet/pensando/ionic/ionic_dev.h | 2 +
.../net/ethernet/pensando/ionic/ionic_lif.c | 348 ++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_lif.h | 5 +
5 files changed, 365 insertions(+)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index 87ab13aee89e..d7eee79b2a10 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -34,6 +34,7 @@ struct ionic {
unsigned int num_bars;
struct identity ident;
struct list_head lifs;
+ struct lif *master_lif;
unsigned int nnqs_per_lif;
unsigned int neqs_per_lif;
unsigned int ntxqs_per_lif;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
index 59d1ae7ce532..98c12b770c7f 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
@@ -206,8 +206,16 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_free_lifs;
}
+ err = ionic_lifs_register(ionic);
+ if (err) {
+ dev_err(dev, "Cannot register LIFs: %d, aborting\n", err);
+ goto err_out_deinit_lifs;
+ }
+
return 0;
+err_out_deinit_lifs:
+ ionic_lifs_deinit(ionic);
err_out_free_lifs:
ionic_lifs_free(ionic);
err_out_free_irqs:
@@ -239,6 +247,7 @@ static void ionic_remove(struct pci_dev *pdev)
struct ionic *ionic = pci_get_drvdata(pdev);
if (ionic) {
+ ionic_lifs_unregister(ionic);
ionic_lifs_deinit(ionic);
ionic_lifs_free(ionic);
ionic_bus_free_irq_vectors(ionic);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
index 7014acd70b98..d44220c1d430 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
@@ -10,6 +10,8 @@
#include "ionic_if.h"
#include "ionic_regs.h"
+#define IONIC_MIN_MTU ETH_MIN_MTU
+#define IONIC_MAX_MTU 9194
#define IONIC_LIFS_MAX 1024
struct ionic_dev_bar {
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 01f9665611d4..005b1d908fa1 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -12,8 +12,74 @@
#include "ionic_lif.h"
#include "ionic_debugfs.h"
+static int ionic_set_nic_features(struct lif *lif, netdev_features_t features);
static int ionic_notifyq_clean(struct lif *lif, int budget);
+int ionic_open(struct net_device *netdev)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ netif_carrier_off(netdev);
+
+ set_bit(LIF_UP, lif->state);
+
+ if (netif_carrier_ok(netdev))
+ netif_tx_wake_all_queues(netdev);
+
+ return 0;
+}
+
+static int ionic_lif_stop(struct lif *lif)
+{
+ struct net_device *ndev = lif->netdev;
+ int err = 0;
+
+ if (!test_bit(LIF_UP, lif->state)) {
+ dev_dbg(lif->ionic->dev, "%s: %s state=DOWN\n",
+ __func__, lif->name);
+ return 0;
+ }
+ dev_dbg(lif->ionic->dev, "%s: %s state=UP\n", __func__, lif->name);
+ clear_bit(LIF_UP, lif->state);
+
+ /* carrier off before disabling queues to avoid watchdog timeout */
+ netif_carrier_off(ndev);
+ netif_tx_stop_all_queues(ndev);
+ netif_tx_disable(ndev);
+ synchronize_rcu();
+
+ return err;
+}
+
+int ionic_stop(struct net_device *netdev)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ return ionic_lif_stop(lif);
+}
+
+int ionic_reset_queues(struct lif *lif)
+{
+ bool running;
+ int err = 0;
+
+ /* Put off the next watchdog timeout */
+ netif_trans_update(lif->netdev);
+
+ while (test_and_set_bit(LIF_QUEUE_RESET, lif->state))
+ usleep_range(100, 200);
+
+ running = netif_running(lif->netdev);
+ if (running)
+ err = ionic_stop(lif->netdev);
+ if (!err && running)
+ ionic_open(lif->netdev);
+
+ clear_bit(LIF_QUEUE_RESET, lif->state);
+
+ return err;
+}
+
static bool ionic_adminq_service(struct cq *cq, struct cq_info *cq_info)
{
struct admin_comp *comp = cq_info->cq_desc;
@@ -114,6 +180,81 @@ static int ionic_notifyq_clean(struct lif *lif, int budget)
return work_done;
}
+static int ionic_set_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ struct lif *lif = netdev_priv(netdev);
+ int err;
+
+ netdev_dbg(netdev, "%s: lif->features=0x%08llx new_features=0x%08llx\n",
+ __func__, (u64)lif->netdev->features, (u64)features);
+
+ err = ionic_set_nic_features(lif, features);
+
+ return err;
+}
+
+static int ionic_set_mac_address(struct net_device *netdev, void *sa)
+{
+ netdev_info(netdev, "%s: stubbed\n", __func__);
+ return 0;
+}
+
+static int ionic_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.lif_setattr = {
+ .opcode = CMD_OPCODE_LIF_SETATTR,
+ .index = cpu_to_le16(lif->index),
+ .attr = IONIC_LIF_ATTR_MTU,
+ .mtu = cpu_to_le32(new_mtu),
+ },
+ };
+ int err;
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ netdev->mtu = new_mtu;
+ err = ionic_reset_queues(lif);
+
+ return err;
+}
+
+static void ionic_tx_timeout(struct net_device *netdev)
+{
+ netdev_info(netdev, "%s: stubbed\n", __func__);
+}
+
+static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
+ u16 vid)
+{
+ netdev_info(netdev, "%s: stubbed\n", __func__);
+ return 0;
+}
+
+static int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto,
+ u16 vid)
+{
+ netdev_info(netdev, "%s: stubbed\n", __func__);
+ return 0;
+}
+
+static const struct net_device_ops ionic_netdev_ops = {
+ .ndo_open = ionic_open,
+ .ndo_stop = ionic_stop,
+ .ndo_set_features = ionic_set_features,
+ .ndo_set_mac_address = ionic_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_tx_timeout = ionic_tx_timeout,
+ .ndo_change_mtu = ionic_change_mtu,
+ .ndo_vlan_rx_add_vid = ionic_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = ionic_vlan_rx_kill_vid,
+};
+
static irqreturn_t ionic_isr(int irq, void *data)
{
struct napi_struct *napi = data;
@@ -388,6 +529,12 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
lif = netdev_priv(netdev);
lif->netdev = netdev;
+ ionic->master_lif = lif;
+ netdev->netdev_ops = &ionic_netdev_ops;
+
+ netdev->watchdog_timeo = 2 * HZ;
+ netdev->min_mtu = IONIC_MIN_MTU;
+ netdev->max_mtu = IONIC_MAX_MTU;
lif->neqs = ionic->neqs_per_lif;
lif->nxqs = ionic->ntxqs_per_lif;
@@ -655,6 +802,177 @@ static int ionic_lif_notifyq_init(struct lif *lif)
return 0;
}
+static __le64 ionic_netdev_features_to_nic(netdev_features_t features)
+{
+ u64 wanted = 0;
+
+ if (features & NETIF_F_HW_VLAN_CTAG_TX)
+ wanted |= ETH_HW_VLAN_TX_TAG;
+ if (features & NETIF_F_HW_VLAN_CTAG_RX)
+ wanted |= ETH_HW_VLAN_RX_STRIP;
+ if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
+ wanted |= ETH_HW_VLAN_RX_FILTER;
+ if (features & NETIF_F_RXHASH)
+ wanted |= ETH_HW_RX_HASH;
+ if (features & NETIF_F_RXCSUM)
+ wanted |= ETH_HW_RX_CSUM;
+ if (features & NETIF_F_SG)
+ wanted |= ETH_HW_TX_SG;
+ if (features & NETIF_F_HW_CSUM)
+ wanted |= ETH_HW_TX_CSUM;
+ if (features & NETIF_F_TSO)
+ wanted |= ETH_HW_TSO;
+ if (features & NETIF_F_TSO6)
+ wanted |= ETH_HW_TSO_IPV6;
+ if (features & NETIF_F_TSO_ECN)
+ wanted |= ETH_HW_TSO_ECN;
+ if (features & NETIF_F_GSO_GRE)
+ wanted |= ETH_HW_TSO_GRE;
+ if (features & NETIF_F_GSO_GRE_CSUM)
+ wanted |= ETH_HW_TSO_GRE_CSUM;
+ if (features & NETIF_F_GSO_IPXIP4)
+ wanted |= ETH_HW_TSO_IPXIP4;
+ if (features & NETIF_F_GSO_IPXIP6)
+ wanted |= ETH_HW_TSO_IPXIP6;
+ if (features & NETIF_F_GSO_UDP_TUNNEL)
+ wanted |= ETH_HW_TSO_UDP;
+ if (features & NETIF_F_GSO_UDP_TUNNEL_CSUM)
+ wanted |= ETH_HW_TSO_UDP_CSUM;
+
+ return cpu_to_le64(wanted);
+}
+
+static int ionic_set_nic_features(struct lif *lif, netdev_features_t features)
+{
+ struct device *dev = lif->ionic->dev;
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.lif_setattr = {
+ .opcode = CMD_OPCODE_LIF_SETATTR,
+ .index = cpu_to_le16(lif->index),
+ .attr = IONIC_LIF_ATTR_FEATURES,
+ },
+ };
+ u64 vlan_flags = ETH_HW_VLAN_TX_TAG |
+ ETH_HW_VLAN_RX_STRIP |
+ ETH_HW_VLAN_RX_FILTER;
+ int err;
+
+ ctx.cmd.lif_setattr.features = ionic_netdev_features_to_nic(features);
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ lif->hw_features = le64_to_cpu(ctx.cmd.lif_setattr.features &
+ ctx.comp.lif_setattr.features);
+
+ if ((vlan_flags & features) &&
+ !(vlan_flags & le64_to_cpu(ctx.comp.lif_setattr.features)))
+ dev_info_once(lif->ionic->dev, "NIC is not supporting vlan offload, likely in SmartNIC mode\n");
+
+ if (lif->hw_features & ETH_HW_VLAN_TX_TAG)
+ dev_dbg(dev, "feature ETH_HW_VLAN_TX_TAG\n");
+ if (lif->hw_features & ETH_HW_VLAN_RX_STRIP)
+ dev_dbg(dev, "feature ETH_HW_VLAN_RX_STRIP\n");
+ if (lif->hw_features & ETH_HW_VLAN_RX_FILTER)
+ dev_dbg(dev, "feature ETH_HW_VLAN_RX_FILTER\n");
+ if (lif->hw_features & ETH_HW_RX_HASH)
+ dev_dbg(dev, "feature ETH_HW_RX_HASH\n");
+ if (lif->hw_features & ETH_HW_TX_SG)
+ dev_dbg(dev, "feature ETH_HW_TX_SG\n");
+ if (lif->hw_features & ETH_HW_TX_CSUM)
+ dev_dbg(dev, "feature ETH_HW_TX_CSUM\n");
+ if (lif->hw_features & ETH_HW_RX_CSUM)
+ dev_dbg(dev, "feature ETH_HW_RX_CSUM\n");
+ if (lif->hw_features & ETH_HW_TSO)
+ dev_dbg(dev, "feature ETH_HW_TSO\n");
+ if (lif->hw_features & ETH_HW_TSO_IPV6)
+ dev_dbg(dev, "feature ETH_HW_TSO_IPV6\n");
+ if (lif->hw_features & ETH_HW_TSO_ECN)
+ dev_dbg(dev, "feature ETH_HW_TSO_ECN\n");
+ if (lif->hw_features & ETH_HW_TSO_GRE)
+ dev_dbg(dev, "feature ETH_HW_TSO_GRE\n");
+ if (lif->hw_features & ETH_HW_TSO_GRE_CSUM)
+ dev_dbg(dev, "feature ETH_HW_TSO_GRE_CSUM\n");
+ if (lif->hw_features & ETH_HW_TSO_IPXIP4)
+ dev_dbg(dev, "feature ETH_HW_TSO_IPXIP4\n");
+ if (lif->hw_features & ETH_HW_TSO_IPXIP6)
+ dev_dbg(dev, "feature ETH_HW_TSO_IPXIP6\n");
+ if (lif->hw_features & ETH_HW_TSO_UDP)
+ dev_dbg(dev, "feature ETH_HW_TSO_UDP\n");
+ if (lif->hw_features & ETH_HW_TSO_UDP_CSUM)
+ dev_dbg(dev, "feature ETH_HW_TSO_UDP_CSUM\n");
+
+ return 0;
+}
+
+static int ionic_init_nic_features(struct lif *lif)
+{
+ struct net_device *netdev = lif->netdev;
+ netdev_features_t features;
+ int err;
+
+ /* set up what we expect to support by default */
+ features = NETIF_F_HW_VLAN_CTAG_TX |
+ NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_RXHASH |
+ NETIF_F_SG |
+ NETIF_F_HW_CSUM |
+ NETIF_F_RXCSUM |
+ NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_TSO_ECN;
+
+ err = ionic_set_nic_features(lif, features);
+ if (err)
+ return err;
+
+ /* tell the netdev what we actually can support */
+ netdev->features |= NETIF_F_HIGHDMA;
+
+ if (lif->hw_features & ETH_HW_VLAN_TX_TAG)
+ netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;
+ if (lif->hw_features & ETH_HW_VLAN_RX_STRIP)
+ netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
+ if (lif->hw_features & ETH_HW_VLAN_RX_FILTER)
+ netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ if (lif->hw_features & ETH_HW_RX_HASH)
+ netdev->hw_features |= NETIF_F_RXHASH;
+ if (lif->hw_features & ETH_HW_TX_SG)
+ netdev->hw_features |= NETIF_F_SG;
+
+ if (lif->hw_features & ETH_HW_TX_CSUM)
+ netdev->hw_enc_features |= NETIF_F_HW_CSUM;
+ if (lif->hw_features & ETH_HW_RX_CSUM)
+ netdev->hw_enc_features |= NETIF_F_RXCSUM;
+ if (lif->hw_features & ETH_HW_TSO)
+ netdev->hw_enc_features |= NETIF_F_TSO;
+ if (lif->hw_features & ETH_HW_TSO_IPV6)
+ netdev->hw_enc_features |= NETIF_F_TSO6;
+ if (lif->hw_features & ETH_HW_TSO_ECN)
+ netdev->hw_enc_features |= NETIF_F_TSO_ECN;
+ if (lif->hw_features & ETH_HW_TSO_GRE)
+ netdev->hw_enc_features |= NETIF_F_GSO_GRE;
+ if (lif->hw_features & ETH_HW_TSO_GRE_CSUM)
+ netdev->hw_enc_features |= NETIF_F_GSO_GRE_CSUM;
+ if (lif->hw_features & ETH_HW_TSO_IPXIP4)
+ netdev->hw_enc_features |= NETIF_F_GSO_IPXIP4;
+ if (lif->hw_features & ETH_HW_TSO_IPXIP6)
+ netdev->hw_enc_features |= NETIF_F_GSO_IPXIP6;
+ if (lif->hw_features & ETH_HW_TSO_UDP)
+ netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL;
+ if (lif->hw_features & ETH_HW_TSO_UDP_CSUM)
+ netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;
+
+ netdev->hw_features |= netdev->hw_enc_features;
+ netdev->features |= netdev->hw_features;
+
+ netdev->priv_flags |= IFF_UNICAST_FLT;
+
+ return 0;
+}
+
static int ionic_lif_init(struct lif *lif)
{
struct ionic_dev *idev = &lif->ionic->idev;
@@ -711,6 +1029,10 @@ static int ionic_lif_init(struct lif *lif)
goto err_out_notifyq_deinit;
}
+ err = ionic_init_nic_features(lif);
+ if (err)
+ goto err_out_notifyq_deinit;
+
set_bit(LIF_INITED, lif->state);
return 0;
@@ -745,6 +1067,32 @@ int ionic_lifs_init(struct ionic *ionic)
return 0;
}
+int ionic_lifs_register(struct ionic *ionic)
+{
+ int err;
+
+ /* only register LIF0 for now */
+ err = register_netdev(ionic->master_lif->netdev);
+ if (err) {
+ dev_err(ionic->dev, "Cannot register net device, aborting\n");
+ return err;
+ }
+
+ ionic->master_lif->registered = true;
+
+ return 0;
+}
+
+void ionic_lifs_unregister(struct ionic *ionic)
+{
+ /* There is only one lif ever registered in the
+ * current model, so don't bother searching the
+ * ionic->lif for candidates to unregister
+ */
+ if (ionic->master_lif->netdev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(ionic->master_lif->netdev);
+}
+
int ionic_lif_identify(struct ionic *ionic, u8 lif_type,
union lif_identity *lid)
{
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 80eec0778f40..ef3f7340a277 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -61,6 +61,8 @@ struct qcq {
enum lif_state_flags {
LIF_INITED,
+ LIF_UP,
+ LIF_QUEUE_RESET,
/* leave this as last */
LIF_STATE_SIZE
@@ -84,6 +86,7 @@ struct lif {
u64 last_eid;
unsigned int neqs;
unsigned int nxqs;
+ u64 hw_features;
struct lif_info *info;
dma_addr_t info_pa;
@@ -124,6 +127,8 @@ int ionic_lifs_alloc(struct ionic *ionic);
void ionic_lifs_free(struct ionic *ionic);
void ionic_lifs_deinit(struct ionic *ionic);
int ionic_lifs_init(struct ionic *ionic);
+int ionic_lifs_register(struct ionic *ionic);
+void ionic_lifs_unregister(struct ionic *ionic);
int ionic_lif_identify(struct ionic *ionic, u8 lif_type,
union lif_identity *lif_ident);
int ionic_lifs_size(struct ionic *ionic);
--
2.17.1
^ permalink raw reply related
* [PATCH v3 net-next 10/19] ionic: Add management of rx filters
From: Shannon Nelson @ 2019-07-08 19:25 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190708192532.27420-1-snelson@pensando.io>
Set up the infrastructure for managing Rx filters. We can't ask the
hardware for what filters it has, so we keep a local list of filters
that we've pushed into the HW.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/Makefile | 4 +-
.../net/ethernet/pensando/ionic/ionic_lif.c | 6 +
.../net/ethernet/pensando/ionic/ionic_lif.h | 2 +
.../ethernet/pensando/ionic/ionic_rx_filter.c | 142 ++++++++++++++++++
.../ethernet/pensando/ionic/ionic_rx_filter.h | 34 +++++
5 files changed, 186 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_rx_filter.h
diff --git a/drivers/net/ethernet/pensando/ionic/Makefile b/drivers/net/ethernet/pensando/ionic/Makefile
index 04d519d00be6..7d9cdc5f02a1 100644
--- a/drivers/net/ethernet/pensando/ionic/Makefile
+++ b/drivers/net/ethernet/pensando/ionic/Makefile
@@ -3,5 +3,5 @@
obj-$(CONFIG_IONIC) := ionic.o
-ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o ionic_debugfs.o \
- ionic_lif.o
+ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o \
+ ionic_lif.o ionic_rx_filter.o ionic_debugfs.o
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 005b1d908fa1..72ac0fd56b69 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -667,6 +667,8 @@ static void ionic_lif_deinit(struct lif *lif)
clear_bit(LIF_INITED, lif->state);
+ ionic_rx_filters_deinit(lif);
+
napi_disable(&lif->adminqcq->napi);
ionic_lif_qcq_deinit(lif, lif->notifyqcq);
ionic_lif_qcq_deinit(lif, lif->adminqcq);
@@ -1033,6 +1035,10 @@ static int ionic_lif_init(struct lif *lif)
if (err)
goto err_out_notifyq_deinit;
+ err = ionic_rx_filters_init(lif);
+ if (err)
+ goto err_out_notifyq_deinit;
+
set_bit(LIF_INITED, lif->state);
return 0;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index ef3f7340a277..9f112aa69033 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -5,6 +5,7 @@
#define _IONIC_LIF_H_
#include <linux/pci.h>
+#include "ionic_rx_filter.h"
#define IONIC_ADMINQ_LENGTH 16 /* must be a power of two */
#define IONIC_NOTIFYQ_LENGTH 64 /* must be a power of two */
@@ -92,6 +93,7 @@ struct lif {
dma_addr_t info_pa;
u32 info_sz;
+ struct rx_filters rx_filters;
unsigned long *dbid_inuse;
unsigned int dbid_count;
struct dentry *dentry;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c
new file mode 100644
index 000000000000..621e0159aba8
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include "ionic.h"
+#include "ionic_lif.h"
+#include "ionic_rx_filter.h"
+
+void ionic_rx_filter_free(struct lif *lif, struct rx_filter *f)
+{
+ struct device *dev = lif->ionic->dev;
+
+ hlist_del(&f->by_id);
+ hlist_del(&f->by_hash);
+ devm_kfree(dev, f);
+}
+
+int ionic_rx_filter_del(struct lif *lif, struct rx_filter *f)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_filter_del = {
+ .opcode = CMD_OPCODE_RX_FILTER_DEL,
+ .filter_id = cpu_to_le32(f->filter_id),
+ },
+ };
+
+ return ionic_adminq_post_wait(lif, &ctx);
+}
+
+int ionic_rx_filters_init(struct lif *lif)
+{
+ unsigned int i;
+
+ spin_lock_init(&lif->rx_filters.lock);
+
+ for (i = 0; i < RX_FILTER_HLISTS; i++) {
+ INIT_HLIST_HEAD(&lif->rx_filters.by_hash[i]);
+ INIT_HLIST_HEAD(&lif->rx_filters.by_id[i]);
+ }
+
+ return 0;
+}
+
+void ionic_rx_filters_deinit(struct lif *lif)
+{
+ struct hlist_head *head;
+ struct hlist_node *tmp;
+ struct rx_filter *f;
+ unsigned int i;
+
+ for (i = 0; i < RX_FILTER_HLISTS; i++) {
+ head = &lif->rx_filters.by_id[i];
+ hlist_for_each_entry_safe(f, tmp, head, by_id)
+ ionic_rx_filter_free(lif, f);
+ }
+}
+
+int ionic_rx_filter_save(struct lif *lif, u32 flow_id, u16 rxq_index,
+ u32 hash, struct ionic_admin_ctx *ctx)
+{
+ struct device *dev = lif->ionic->dev;
+ struct rx_filter_add_cmd *ac;
+ struct hlist_head *head;
+ struct rx_filter *f;
+ unsigned int key;
+
+ ac = (struct rx_filter_add_cmd *)&ctx->cmd;
+
+ switch (le16_to_cpu(ac->match)) {
+ case RX_FILTER_MATCH_VLAN:
+ key = le16_to_cpu(ac->vlan.vlan) & RX_FILTER_HLISTS_MASK;
+ break;
+ case RX_FILTER_MATCH_MAC:
+ key = *(u32 *)ac->mac.addr & RX_FILTER_HLISTS_MASK;
+ break;
+ case RX_FILTER_MATCH_MAC_VLAN:
+ key = le16_to_cpu(ac->mac_vlan.vlan) & RX_FILTER_HLISTS_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f = devm_kzalloc(dev, sizeof(*f), GFP_KERNEL);
+ if (!f)
+ return -ENOMEM;
+
+ f->flow_id = flow_id;
+ f->filter_id = le32_to_cpu(ctx->comp.rx_filter_add.filter_id);
+ f->rxq_index = rxq_index;
+ memcpy(&f->cmd, ac, sizeof(f->cmd));
+
+ INIT_HLIST_NODE(&f->by_hash);
+ INIT_HLIST_NODE(&f->by_id);
+
+ spin_lock_bh(&lif->rx_filters.lock);
+
+ head = &lif->rx_filters.by_hash[key];
+ hlist_add_head(&f->by_hash, head);
+
+ key = f->filter_id & RX_FILTER_HLISTS_MASK;
+ head = &lif->rx_filters.by_id[key];
+ hlist_add_head(&f->by_id, head);
+
+ spin_unlock_bh(&lif->rx_filters.lock);
+
+ return 0;
+}
+
+struct rx_filter *ionic_rx_filter_by_vlan(struct lif *lif, u16 vid)
+{
+ unsigned int key = vid & RX_FILTER_HLISTS_MASK;
+ struct hlist_head *head = &lif->rx_filters.by_hash[key];
+ struct rx_filter *f;
+
+ hlist_for_each_entry(f, head, by_hash) {
+ if (le16_to_cpu(f->cmd.match) != RX_FILTER_MATCH_VLAN)
+ continue;
+ if (le16_to_cpu(f->cmd.vlan.vlan) == vid)
+ return f;
+ }
+
+ return NULL;
+}
+
+struct rx_filter *ionic_rx_filter_by_addr(struct lif *lif, const u8 *addr)
+{
+ unsigned int key = *(u32 *)addr & RX_FILTER_HLISTS_MASK;
+ struct hlist_head *head = &lif->rx_filters.by_hash[key];
+ struct rx_filter *f;
+
+ hlist_for_each_entry(f, head, by_hash) {
+ if (le16_to_cpu(f->cmd.match) != RX_FILTER_MATCH_MAC)
+ continue;
+ if (memcmp(addr, f->cmd.mac.addr, ETH_ALEN) == 0)
+ return f;
+ }
+
+ return NULL;
+}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.h b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.h
new file mode 100644
index 000000000000..c8688124465a
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#ifndef _IONIC_RX_FILTER_H_
+#define _IONIC_RX_FILTER_H_
+
+#define RXQ_INDEX_ANY (0xFFFF)
+struct rx_filter {
+ u32 flow_id;
+ u32 filter_id;
+ u16 rxq_index;
+ struct rx_filter_add_cmd cmd;
+ struct hlist_node by_hash;
+ struct hlist_node by_id;
+};
+
+#define RX_FILTER_HLISTS BIT(10)
+#define RX_FILTER_HLISTS_MASK (RX_FILTER_HLISTS - 1)
+struct rx_filters {
+ spinlock_t lock; /* filter list lock */
+ struct hlist_head by_hash[RX_FILTER_HLISTS]; /* by skb hash */
+ struct hlist_head by_id[RX_FILTER_HLISTS]; /* by filter_id */
+};
+
+void ionic_rx_filter_free(struct lif *lif, struct rx_filter *f);
+int ionic_rx_filter_del(struct lif *lif, struct rx_filter *f);
+int ionic_rx_filters_init(struct lif *lif);
+void ionic_rx_filters_deinit(struct lif *lif);
+int ionic_rx_filter_save(struct lif *lif, u32 flow_id, u16 rxq_index,
+ u32 hash, struct ionic_admin_ctx *ctx);
+struct rx_filter *ionic_rx_filter_by_vlan(struct lif *lif, u16 vid);
+struct rx_filter *ionic_rx_filter_by_addr(struct lif *lif, const u8 *addr);
+
+#endif /* _IONIC_RX_FILTER_H_ */
--
2.17.1
^ permalink raw reply related
* [PATCH v3 net-next 06/19] ionic: Add basic adminq support
From: Shannon Nelson @ 2019-07-08 19:25 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190708192532.27420-1-snelson@pensando.io>
Most of the NIC configuration happens through the AdminQ message
queue. NAPI is used for basic interrupt handling and message
queue management. These routines are set up to be shared among
different types of queues when used in slow-path handling.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/ionic.h | 3 +
.../net/ethernet/pensando/ionic/ionic_bus.h | 1 +
.../ethernet/pensando/ionic/ionic_bus_pci.c | 5 +
.../ethernet/pensando/ionic/ionic_debugfs.c | 149 ++++++++
.../ethernet/pensando/ionic/ionic_debugfs.h | 4 +
.../net/ethernet/pensando/ionic/ionic_dev.c | 247 +++++++++++++
.../net/ethernet/pensando/ionic/ionic_dev.h | 92 +++++
.../net/ethernet/pensando/ionic/ionic_lif.c | 328 ++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_lif.h | 72 ++++
.../net/ethernet/pensando/ionic/ionic_main.c | 26 ++
10 files changed, 927 insertions(+)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index c86a08752b3b..c8c93e9efb39 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -43,6 +43,9 @@ struct ionic {
DECLARE_BITMAP(intrs, INTR_CTRL_REGS_MAX);
};
+int ionic_napi(struct napi_struct *napi, int budget, ionic_cq_cb cb,
+ ionic_cq_done_cb done_cb, void *done_arg);
+
int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_wait);
int ionic_set_dma_mask(struct ionic *ionic);
int ionic_setup(struct ionic *ionic);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus.h b/drivers/net/ethernet/pensando/ionic/ionic_bus.h
index 6b29e94f81d6..2f4d08c64910 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_bus.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus.h
@@ -4,6 +4,7 @@
#ifndef _IONIC_BUS_H_
#define _IONIC_BUS_H_
+int ionic_bus_get_irq(struct ionic *ionic, unsigned int num);
const char *ionic_bus_info(struct ionic *ionic);
int ionic_bus_alloc_irq_vectors(struct ionic *ionic, unsigned int nintrs);
void ionic_bus_free_irq_vectors(struct ionic *ionic);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
index 838d7c423b2c..59d1ae7ce532 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
@@ -19,6 +19,11 @@ static const struct pci_device_id ionic_id_table[] = {
};
MODULE_DEVICE_TABLE(pci, ionic_id_table);
+int ionic_bus_get_irq(struct ionic *ionic, unsigned int num)
+{
+ return pci_irq_vector(ionic->pdev, num);
+}
+
const char *ionic_bus_info(struct ionic *ionic)
{
return pci_name(ionic->pdev);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
index 6c47466c46ed..b5e77d35ce6f 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
@@ -84,6 +84,148 @@ int ionic_debugfs_add_sizes(struct ionic *ionic)
return 0;
}
+static int q_tail_show(struct seq_file *seq, void *v)
+{
+ struct queue *q = seq->private;
+
+ seq_printf(seq, "%d\n", q->tail->index);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(q_tail);
+
+static int q_head_show(struct seq_file *seq, void *v)
+{
+ struct queue *q = seq->private;
+
+ seq_printf(seq, "%d\n", q->head->index);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(q_head);
+
+static int cq_tail_show(struct seq_file *seq, void *v)
+{
+ struct cq *cq = seq->private;
+
+ seq_printf(seq, "%d\n", cq->tail->index);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(cq_tail);
+
+static const struct debugfs_reg32 intr_ctrl_regs[] = {
+ { .name = "coal_init", .offset = 0, },
+ { .name = "mask", .offset = 4, },
+ { .name = "credits", .offset = 8, },
+ { .name = "mask_on_assert", .offset = 12, },
+ { .name = "coal_timer", .offset = 16, },
+};
+
+int ionic_debugfs_add_qcq(struct lif *lif, struct qcq *qcq)
+{
+ struct dentry *qcq_dentry, *q_dentry, *cq_dentry, *intr_dentry;
+ struct ionic_dev *idev = &lif->ionic->idev;
+ struct debugfs_regset32 *intr_ctrl_regset;
+ struct debugfs_blob_wrapper *desc_blob;
+ struct device *dev = lif->ionic->dev;
+ struct intr *intr = &qcq->intr;
+ struct queue *q = &qcq->q;
+ struct cq *cq = &qcq->cq;
+
+ qcq_dentry = debugfs_create_dir(q->name, lif->dentry);
+ if (IS_ERR_OR_NULL(qcq_dentry))
+ return PTR_ERR(qcq_dentry);
+ qcq->dentry = qcq_dentry;
+
+ debugfs_create_x32("total_size", 0400, qcq_dentry, &qcq->total_size);
+ debugfs_create_x64("base_pa", 0400, qcq_dentry, &qcq->base_pa);
+
+ q_dentry = debugfs_create_dir("q", qcq_dentry);
+ if (IS_ERR_OR_NULL(q_dentry))
+ return PTR_ERR(q_dentry);
+
+ debugfs_create_u32("index", 0400, q_dentry, &q->index);
+ debugfs_create_x64("base_pa", 0400, q_dentry, &q->base_pa);
+ if (qcq->flags & QCQ_F_SG) {
+ debugfs_create_x64("sg_base_pa", 0400, q_dentry,
+ &q->sg_base_pa);
+ debugfs_create_u32("sg_desc_size", 0400, q_dentry,
+ &q->sg_desc_size);
+ }
+ debugfs_create_u32("num_descs", 0400, q_dentry, &q->num_descs);
+ debugfs_create_u32("desc_size", 0400, q_dentry, &q->desc_size);
+ debugfs_create_u32("pid", 0400, q_dentry, &q->pid);
+ debugfs_create_u32("qid", 0400, q_dentry, &q->hw_index);
+ debugfs_create_u32("qtype", 0400, q_dentry, &q->hw_type);
+ debugfs_create_u64("drop", 0400, q_dentry, &q->drop);
+ debugfs_create_u64("stop", 0400, q_dentry, &q->stop);
+ debugfs_create_u64("wake", 0400, q_dentry, &q->wake);
+
+ debugfs_create_file("tail", 0400, q_dentry, q, &q_tail_fops);
+ debugfs_create_file("head", 0400, q_dentry, q, &q_head_fops);
+
+ desc_blob = devm_kzalloc(dev, sizeof(*desc_blob), GFP_KERNEL);
+ if (!desc_blob)
+ return -ENOMEM;
+ desc_blob->data = q->base;
+ desc_blob->size = (unsigned long)q->num_descs * q->desc_size;
+ debugfs_create_blob("desc_blob", 0400, q_dentry, desc_blob);
+
+ if (qcq->flags & QCQ_F_SG) {
+ desc_blob = devm_kzalloc(dev, sizeof(*desc_blob), GFP_KERNEL);
+ if (!desc_blob)
+ return -ENOMEM;
+ desc_blob->data = q->sg_base;
+ desc_blob->size = (unsigned long)q->num_descs * q->sg_desc_size;
+ debugfs_create_blob("sg_desc_blob", 0400, q_dentry,
+ desc_blob);
+ }
+
+ cq_dentry = debugfs_create_dir("cq", qcq_dentry);
+ if (IS_ERR_OR_NULL(cq_dentry))
+ return PTR_ERR(cq_dentry);
+
+ debugfs_create_x64("base_pa", 0400, cq_dentry, &cq->base_pa);
+ debugfs_create_u32("num_descs", 0400, cq_dentry, &cq->num_descs);
+ debugfs_create_u32("desc_size", 0400, cq_dentry, &cq->desc_size);
+ debugfs_create_u8("done_color", 0400, cq_dentry,
+ (u8 *)&cq->done_color);
+
+ debugfs_create_file("tail", 0400, cq_dentry, cq, &cq_tail_fops);
+
+ desc_blob = devm_kzalloc(dev, sizeof(*desc_blob), GFP_KERNEL);
+ if (!desc_blob)
+ return -ENOMEM;
+ desc_blob->data = cq->base;
+ desc_blob->size = (unsigned long)cq->num_descs * cq->desc_size;
+ debugfs_create_blob("desc_blob", 0400, cq_dentry, desc_blob);
+
+ if (qcq->flags & QCQ_F_INTR) {
+ intr_dentry = debugfs_create_dir("intr", qcq_dentry);
+ if (IS_ERR_OR_NULL(intr_dentry))
+ return PTR_ERR(intr_dentry);
+
+ debugfs_create_u32("index", 0400, intr_dentry,
+ &intr->index);
+ debugfs_create_u32("vector", 0400, intr_dentry,
+ &intr->vector);
+
+ intr_ctrl_regset = devm_kzalloc(dev, sizeof(*intr_ctrl_regset),
+ GFP_KERNEL);
+ if (!intr_ctrl_regset)
+ return -ENOMEM;
+ intr_ctrl_regset->regs = intr_ctrl_regs;
+ intr_ctrl_regset->nregs = ARRAY_SIZE(intr_ctrl_regs);
+ intr_ctrl_regset->base = &idev->intr_ctrl[intr->index];
+
+ debugfs_create_regset32("intr_ctrl", 0400, intr_dentry,
+ intr_ctrl_regset);
+ }
+
+ return 0;
+}
+
static int netdev_show(struct seq_file *seq, void *v)
{
struct net_device *netdev = seq->private;
@@ -115,4 +257,11 @@ void ionic_debugfs_del_lif(struct lif *lif)
debugfs_remove_recursive(lif->dentry);
lif->dentry = NULL;
}
+
+void ionic_debugfs_del_qcq(struct qcq *qcq)
+{
+ debugfs_remove_recursive(qcq->dentry);
+ qcq->dentry = NULL;
+}
+
#endif
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.h b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.h
index cb00166e7c30..fa5030d48763 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.h
@@ -17,7 +17,9 @@ int ionic_debugfs_add_dev_cmd(struct ionic *ionic);
int ionic_debugfs_add_ident(struct ionic *ionic);
int ionic_debugfs_add_sizes(struct ionic *ionic);
int ionic_debugfs_add_lif(struct lif *lif);
+int ionic_debugfs_add_qcq(struct lif *lif, struct qcq *qcq);
void ionic_debugfs_del_lif(struct lif *lif);
+void ionic_debugfs_del_qcq(struct qcq *qcq);
#else
static inline void ionic_debugfs_create(void) { }
static inline void ionic_debugfs_destroy(void) { }
@@ -28,7 +30,9 @@ static inline int ionic_debugfs_add_dev_cmd(struct ionic *ionic) { return 0; }
static inline int ionic_debugfs_add_ident(struct ionic *ionic) { return 0; }
static inline int ionic_debugfs_add_sizes(struct ionic *ionic) { return 0; }
static inline int ionic_debugfs_add_lif(struct lif *lif) { return 0; }
+static inline int ionic_debugfs_add_qcq(struct lif *lif, struct qcq *qcq) { return 0; }
static inline void ionic_debugfs_del_lif(struct lif *lif) { return 0; }
+static inline void ionic_debugfs_del_qcq(struct qcq *qcq) { return 0; }
#endif
#endif /* _IONIC_DEBUGFS_H_ */
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.c b/drivers/net/ethernet/pensando/ionic/ionic_dev.c
index c0710bf200a5..9694e45b43a6 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_dev.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.c
@@ -282,7 +282,254 @@ void ionic_dev_cmd_lif_reset(struct ionic_dev *idev, u16 lif_index)
ionic_dev_cmd_go(idev, &cmd);
}
+void ionic_dev_cmd_adminq_init(struct ionic_dev *idev, struct qcq *qcq,
+ u16 lif_index, u16 intr_index)
+{
+ struct queue *q = &qcq->q;
+ struct cq *cq = &qcq->cq;
+
+ union dev_cmd cmd = {
+ .q_init.opcode = CMD_OPCODE_Q_INIT,
+ .q_init.lif_index = cpu_to_le16(lif_index),
+ .q_init.type = q->type,
+ .q_init.index = cpu_to_le32(q->index),
+ .q_init.flags = cpu_to_le16(IONIC_QINIT_F_IRQ |
+ IONIC_QINIT_F_ENA),
+ .q_init.pid = cpu_to_le16(q->pid),
+ .q_init.intr_index = cpu_to_le16(intr_index),
+ .q_init.ring_size = ilog2(q->num_descs),
+ .q_init.ring_base = cpu_to_le64(q->base_pa),
+ .q_init.cq_ring_base = cpu_to_le64(cq->base_pa),
+ };
+
+ ionic_dev_cmd_go(idev, &cmd);
+}
+
int ionic_db_page_num(struct lif *lif, int pid)
{
return (lif->hw_index * lif->dbid_count) + pid;
}
+
+int ionic_cq_init(struct lif *lif, struct cq *cq, struct intr *intr,
+ unsigned int num_descs, size_t desc_size)
+{
+ unsigned int ring_size;
+ struct cq_info *cur;
+ unsigned int i;
+
+ if (desc_size == 0 || !is_power_of_2(num_descs))
+ return -EINVAL;
+
+ ring_size = ilog2(num_descs);
+ if (ring_size < 2 || ring_size > 16)
+ return -EINVAL;
+
+ cq->lif = lif;
+ cq->bound_intr = intr;
+ cq->num_descs = num_descs;
+ cq->desc_size = desc_size;
+ cq->tail = cq->info;
+ cq->done_color = 1;
+
+ cur = cq->info;
+
+ for (i = 0; i < num_descs; i++) {
+ if (i + 1 == num_descs) {
+ cur->next = cq->info;
+ cur->last = true;
+ } else {
+ cur->next = cur + 1;
+ }
+ cur->index = i;
+ cur++;
+ }
+
+ return 0;
+}
+
+void ionic_cq_map(struct cq *cq, void *base, dma_addr_t base_pa)
+{
+ struct cq_info *cur;
+ unsigned int i;
+
+ cq->base = base;
+ cq->base_pa = base_pa;
+
+ for (i = 0, cur = cq->info; i < cq->num_descs; i++, cur++)
+ cur->cq_desc = base + (i * cq->desc_size);
+}
+
+void ionic_cq_bind(struct cq *cq, struct queue *q)
+{
+ cq->bound_q = q;
+}
+
+unsigned int ionic_cq_service(struct cq *cq, unsigned int work_to_do,
+ ionic_cq_cb cb, ionic_cq_done_cb done_cb,
+ void *done_arg)
+{
+ unsigned int work_done = 0;
+
+ if (work_to_do == 0)
+ return 0;
+
+ while (cb(cq, cq->tail)) {
+ if (cq->tail->last)
+ cq->done_color = !cq->done_color;
+ cq->tail = cq->tail->next;
+ DEBUG_STATS_CQE_CNT(cq);
+
+ if (++work_done >= work_to_do)
+ break;
+ }
+
+ if (work_done && done_cb)
+ done_cb(done_arg);
+
+ return work_done;
+}
+
+int ionic_q_init(struct lif *lif, struct ionic_dev *idev, struct queue *q,
+ unsigned int index, const char *name, unsigned int num_descs,
+ size_t desc_size, size_t sg_desc_size, unsigned int pid)
+{
+ unsigned int ring_size;
+ struct desc_info *cur;
+ unsigned int i;
+
+ if (desc_size == 0 || !is_power_of_2(num_descs))
+ return -EINVAL;
+
+ ring_size = ilog2(num_descs);
+ if (ring_size < 2 || ring_size > 16)
+ return -EINVAL;
+
+ q->lif = lif;
+ q->idev = idev;
+ q->index = index;
+ q->num_descs = num_descs;
+ q->desc_size = desc_size;
+ q->sg_desc_size = sg_desc_size;
+ q->tail = q->info;
+ q->head = q->tail;
+ q->pid = pid;
+
+ snprintf(q->name, sizeof(q->name), "L%d-%s%u", lif->index, name, index);
+
+ cur = q->info;
+
+ for (i = 0; i < num_descs; i++) {
+ if (i + 1 == num_descs)
+ cur->next = q->info;
+ else
+ cur->next = cur + 1;
+ cur->index = i;
+ cur->left = num_descs - i;
+ cur++;
+ }
+
+ return 0;
+}
+
+void ionic_q_map(struct queue *q, void *base, dma_addr_t base_pa)
+{
+ struct desc_info *cur;
+ unsigned int i;
+
+ q->base = base;
+ q->base_pa = base_pa;
+
+ for (i = 0, cur = q->info; i < q->num_descs; i++, cur++)
+ cur->desc = base + (i * q->desc_size);
+}
+
+void ionic_q_sg_map(struct queue *q, void *base, dma_addr_t base_pa)
+{
+ struct desc_info *cur;
+ unsigned int i;
+
+ q->sg_base = base;
+ q->sg_base_pa = base_pa;
+
+ for (i = 0, cur = q->info; i < q->num_descs; i++, cur++)
+ cur->sg_desc = base + (i * q->sg_desc_size);
+}
+
+void ionic_q_post(struct queue *q, bool ring_doorbell, desc_cb cb,
+ void *cb_arg)
+{
+ struct device *dev = q->lif->ionic->dev;
+ struct lif *lif = q->lif;
+
+ q->head->cb = cb;
+ q->head->cb_arg = cb_arg;
+ q->head = q->head->next;
+
+ dev_dbg(dev, "lif=%d qname=%s qid=%d qtype=%d p_index=%d ringdb=%d\n",
+ q->lif->index, q->name, q->hw_type, q->hw_index,
+ q->head->index, ring_doorbell);
+
+ if (ring_doorbell)
+ ionic_dbell_ring(lif->kern_dbpage, q->hw_type,
+ q->dbval | q->head->index);
+}
+
+unsigned int ionic_q_space_avail(struct queue *q)
+{
+ unsigned int avail = q->tail->index;
+
+ if (q->head->index >= avail)
+ avail += q->head->left - 1;
+ else
+ avail -= q->head->index + 1;
+
+ return avail;
+}
+
+bool ionic_q_has_space(struct queue *q, unsigned int want)
+{
+ return ionic_q_space_avail(q) >= want;
+}
+
+static bool ionic_q_is_posted(struct queue *q, unsigned int pos)
+{
+ unsigned int mask, tail, head;
+
+ mask = q->num_descs - 1;
+ tail = q->tail->index;
+ head = q->head->index;
+
+ return ((pos - tail) & mask) < ((head - tail) & mask);
+}
+
+void ionic_q_service(struct queue *q, struct cq_info *cq_info,
+ unsigned int stop_index)
+{
+ struct desc_info *desc_info;
+ void *cb_arg;
+ desc_cb cb;
+
+ /* check for empty queue */
+ if (q->tail->index == q->head->index)
+ return;
+
+ /* stop index must be for a descriptor that is not yet completed */
+ if (unlikely(!ionic_q_is_posted(q, stop_index)))
+ dev_err(q->lif->ionic->dev,
+ "ionic stop is not posted %s stop %u tail %u head %u\n",
+ q->name, stop_index, q->tail->index, q->head->index);
+
+ do {
+ desc_info = q->tail;
+ q->tail = desc_info->next;
+
+ cb = desc_info->cb;
+ cb_arg = desc_info->cb_arg;
+
+ desc_info->cb = NULL;
+ desc_info->cb_arg = NULL;
+
+ if (cb)
+ cb(q, desc_info, cq_info, cb_arg);
+ } while (desc_info->index != stop_index);
+}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
index 6d30adeab8c5..7014acd70b98 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
@@ -135,6 +135,59 @@ struct ionic_dev {
#define INTR_INDEX_NOT_ASSIGNED -1
#define INTR_NAME_MAX_SZ 32
+struct cq_info {
+ void *cq_desc;
+ struct cq_info *next;
+ unsigned int index;
+ bool last;
+};
+
+struct queue;
+struct qcq;
+struct desc_info;
+
+typedef void (*desc_cb)(struct queue *q, struct desc_info *desc_info,
+ struct cq_info *cq_info, void *cb_arg);
+
+struct desc_info {
+ void *desc;
+ void *sg_desc;
+ struct desc_info *next;
+ unsigned int index;
+ unsigned int left;
+ desc_cb cb;
+ void *cb_arg;
+};
+
+#define QUEUE_NAME_MAX_SZ 32
+
+struct queue {
+ char name[QUEUE_NAME_MAX_SZ];
+ struct ionic_dev *idev;
+ struct lif *lif;
+ unsigned int index;
+ unsigned int type;
+ unsigned int hw_index;
+ unsigned int hw_type;
+ u64 dbval;
+ void *base;
+ void *sg_base;
+ dma_addr_t base_pa;
+ dma_addr_t sg_base_pa;
+ struct desc_info *info;
+ struct desc_info *tail;
+ struct desc_info *head;
+ unsigned int num_descs;
+ unsigned int desc_size;
+ unsigned int sg_desc_size;
+ void *nop_desc;
+ unsigned int pid;
+ u64 dbell_count;
+ u64 drop;
+ u64 stop;
+ u64 wake;
+};
+
struct intr {
char name[INTR_NAME_MAX_SZ];
unsigned int index;
@@ -144,6 +197,20 @@ struct intr {
cpumask_t affinity_mask;
};
+struct cq {
+ void *base;
+ dma_addr_t base_pa;
+ struct lif *lif;
+ struct cq_info *info;
+ struct cq_info *tail;
+ struct queue *bound_q;
+ struct intr *bound_intr;
+ u64 compl_count;
+ unsigned int num_descs;
+ unsigned int desc_size;
+ bool done_color;
+};
+
struct ionic;
static inline void ionic_intr_init(struct ionic_dev *idev, struct intr *intr,
@@ -181,7 +248,32 @@ void ionic_dev_cmd_lif_identify(struct ionic_dev *idev, u8 type, u8 ver);
void ionic_dev_cmd_lif_init(struct ionic_dev *idev, u16 lif_index,
dma_addr_t addr);
void ionic_dev_cmd_lif_reset(struct ionic_dev *idev, u16 lif_index);
+void ionic_dev_cmd_adminq_init(struct ionic_dev *idev, struct qcq *qcq,
+ u16 lif_index, u16 intr_index);
int ionic_db_page_num(struct lif *lif, int pid);
+int ionic_cq_init(struct lif *lif, struct cq *cq, struct intr *intr,
+ unsigned int num_descs, size_t desc_size);
+void ionic_cq_map(struct cq *cq, void *base, dma_addr_t base_pa);
+void ionic_cq_bind(struct cq *cq, struct queue *q);
+typedef bool (*ionic_cq_cb)(struct cq *cq, struct cq_info *cq_info);
+typedef void (*ionic_cq_done_cb)(void *done_arg);
+unsigned int ionic_cq_service(struct cq *cq, unsigned int work_to_do,
+ ionic_cq_cb cb, ionic_cq_done_cb done_cb,
+ void *done_arg);
+
+int ionic_q_init(struct lif *lif, struct ionic_dev *idev, struct queue *q,
+ unsigned int index, const char *name, unsigned int num_descs,
+ size_t desc_size, size_t sg_desc_size, unsigned int pid);
+void ionic_q_map(struct queue *q, void *base, dma_addr_t base_pa);
+void ionic_q_sg_map(struct queue *q, void *base, dma_addr_t base_pa);
+void ionic_q_post(struct queue *q, bool ring_doorbell, desc_cb cb,
+ void *cb_arg);
+void ionic_q_rewind(struct queue *q, struct desc_info *start);
+unsigned int ionic_q_space_avail(struct queue *q);
+bool ionic_q_has_space(struct queue *q, unsigned int want);
+void ionic_q_service(struct queue *q, struct cq_info *cq_info,
+ unsigned int stop_index);
+
#endif /* _IONIC_DEV_H_ */
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 65ced9b469c9..19c046502a26 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -12,6 +12,32 @@
#include "ionic_lif.h"
#include "ionic_debugfs.h"
+static bool ionic_adminq_service(struct cq *cq, struct cq_info *cq_info)
+{
+ struct admin_comp *comp = cq_info->cq_desc;
+
+ if (!color_match(comp->color, cq->done_color))
+ return false;
+
+ ionic_q_service(cq->bound_q, cq_info, le16_to_cpu(comp->comp_index));
+
+ return true;
+}
+
+static int ionic_adminq_napi(struct napi_struct *napi, int budget)
+{
+ return ionic_napi(napi, budget, ionic_adminq_service, NULL, NULL);
+}
+
+static irqreturn_t ionic_isr(int irq, void *data)
+{
+ struct napi_struct *napi = data;
+
+ napi_schedule_irqoff(napi);
+
+ return IRQ_HANDLED;
+}
+
static int ionic_intr_alloc(struct lif *lif, struct intr *intr)
{
struct ionic *ionic = lif->ionic;
@@ -36,6 +62,191 @@ static void ionic_intr_free(struct lif *lif, int index)
clear_bit(index, lif->ionic->intrs);
}
+static int ionic_qcq_alloc(struct lif *lif, unsigned int type,
+ unsigned int index,
+ const char *name, unsigned int flags,
+ unsigned int num_descs, unsigned int desc_size,
+ unsigned int cq_desc_size,
+ unsigned int sg_desc_size,
+ unsigned int pid, struct qcq **qcq)
+{
+ struct ionic_dev *idev = &lif->ionic->idev;
+ u32 q_size, cq_size, sg_size, total_size;
+ struct device *dev = lif->ionic->dev;
+ void *q_base, *cq_base, *sg_base;
+ dma_addr_t cq_base_pa = 0;
+ dma_addr_t sg_base_pa = 0;
+ dma_addr_t q_base_pa = 0;
+ struct qcq *new;
+ int err;
+
+ *qcq = NULL;
+
+ q_size = num_descs * desc_size;
+ cq_size = num_descs * cq_desc_size;
+ sg_size = num_descs * sg_desc_size;
+
+ total_size = ALIGN(q_size, PAGE_SIZE) + ALIGN(cq_size, PAGE_SIZE);
+ /* Note: aligning q_size/cq_size is not enough due to cq_base
+ * address aligning as q_base could be not aligned to the page.
+ * Adding PAGE_SIZE.
+ */
+ total_size += PAGE_SIZE;
+ if (flags & QCQ_F_SG) {
+ total_size += ALIGN(sg_size, PAGE_SIZE);
+ total_size += PAGE_SIZE;
+ }
+
+ new = devm_kzalloc(dev, sizeof(*new), GFP_KERNEL);
+ if (!new) {
+ netdev_err(lif->netdev, "Cannot allocate queue structure\n");
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ new->flags = flags;
+
+ new->q.info = devm_kzalloc(dev, sizeof(*new->q.info) * num_descs,
+ GFP_KERNEL);
+ if (!new->q.info) {
+ netdev_err(lif->netdev, "Cannot allocate queue info\n");
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ new->q.type = type;
+
+ err = ionic_q_init(lif, idev, &new->q, index, name, num_descs,
+ desc_size, sg_desc_size, pid);
+ if (err) {
+ netdev_err(lif->netdev, "Cannot initialize queue\n");
+ goto err_out;
+ }
+
+ if (flags & QCQ_F_INTR) {
+ err = ionic_intr_alloc(lif, &new->intr);
+ if (err) {
+ netdev_warn(lif->netdev, "no intr for %s: %d\n",
+ name, err);
+ goto err_out;
+ }
+
+ err = ionic_bus_get_irq(lif->ionic, new->intr.index);
+ if (err < 0) {
+ netdev_warn(lif->netdev, "no vector for %s: %d\n",
+ name, err);
+ goto err_out_free_intr;
+ }
+ new->intr.vector = err;
+ ionic_intr_mask_assert(idev->intr_ctrl, new->intr.index,
+ IONIC_INTR_MASK_SET);
+
+ new->intr.cpu = new->intr.index % num_online_cpus();
+ if (cpu_online(new->intr.cpu))
+ cpumask_set_cpu(new->intr.cpu,
+ &new->intr.affinity_mask);
+ } else {
+ new->intr.index = INTR_INDEX_NOT_ASSIGNED;
+ }
+
+ new->cq.info = devm_kzalloc(dev, sizeof(*new->cq.info) * num_descs,
+ GFP_KERNEL);
+ if (!new->cq.info) {
+ netdev_err(lif->netdev, "Cannot allocate completion queue info\n");
+ err = -ENOMEM;
+ goto err_out_free_intr;
+ }
+
+ err = ionic_cq_init(lif, &new->cq, &new->intr, num_descs, cq_desc_size);
+ if (err) {
+ netdev_err(lif->netdev, "Cannot initialize completion queue\n");
+ goto err_out_free_intr;
+ }
+
+ new->base = dma_alloc_coherent(dev, total_size, &new->base_pa,
+ GFP_KERNEL);
+ if (!new->base) {
+ netdev_err(lif->netdev, "Cannot allocate queue DMA memory\n");
+ err = -ENOMEM;
+ goto err_out_free_intr;
+ }
+
+ new->total_size = total_size;
+
+ q_base = new->base;
+ q_base_pa = new->base_pa;
+
+ cq_base = (void *)ALIGN((uintptr_t)q_base + q_size, PAGE_SIZE);
+ cq_base_pa = ALIGN(q_base_pa + q_size, PAGE_SIZE);
+
+ if (flags & QCQ_F_SG) {
+ sg_base = (void *)ALIGN((uintptr_t)cq_base + cq_size,
+ PAGE_SIZE);
+ sg_base_pa = ALIGN(cq_base_pa + cq_size, PAGE_SIZE);
+ ionic_q_sg_map(&new->q, sg_base, sg_base_pa);
+ }
+
+ ionic_q_map(&new->q, q_base, q_base_pa);
+ ionic_cq_map(&new->cq, cq_base, cq_base_pa);
+ ionic_cq_bind(&new->cq, &new->q);
+
+ *qcq = new;
+
+ return 0;
+
+err_out_free_intr:
+ ionic_intr_free(lif, new->intr.index);
+err_out:
+ dev_err(dev, "qcq alloc of %s%d failed %d\n", name, index, err);
+ return err;
+}
+
+static void ionic_qcq_free(struct lif *lif, struct qcq *qcq)
+{
+ struct device *dev = lif->ionic->dev;
+
+ if (!qcq)
+ return;
+
+ dma_free_coherent(dev, qcq->total_size, qcq->base, qcq->base_pa);
+ qcq->base = NULL;
+ qcq->base_pa = 0;
+
+ if (qcq->flags & QCQ_F_INTR)
+ ionic_intr_free(lif, qcq->intr.index);
+
+ devm_kfree(dev, qcq->cq.info);
+ qcq->cq.info = NULL;
+ devm_kfree(dev, qcq->q.info);
+ qcq->q.info = NULL;
+ devm_kfree(dev, qcq);
+}
+
+static int ionic_qcqs_alloc(struct lif *lif)
+{
+ unsigned int flags;
+ int err;
+
+ flags = QCQ_F_INTR;
+ err = ionic_qcq_alloc(lif, IONIC_QTYPE_ADMINQ, 0, "admin", flags,
+ IONIC_ADMINQ_LENGTH,
+ sizeof(struct admin_cmd),
+ sizeof(struct admin_comp),
+ 0, lif->kern_pid, &lif->adminqcq);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static void ionic_qcqs_free(struct lif *lif)
+{
+ if (lif->adminqcq) {
+ ionic_qcq_free(lif, lif->adminqcq);
+ lif->adminqcq = NULL;
+ }
+}
+
static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
{
struct device *dev = ionic->dev;
@@ -65,6 +276,8 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
snprintf(lif->name, sizeof(lif->name), "lif%u", index);
+ spin_lock_init(&lif->adminq_lock);
+
/* allocate lif info */
lif->info_sz = ALIGN(sizeof(*lif->info), PAGE_SIZE);
lif->info = dma_alloc_coherent(dev, lif->info_sz,
@@ -75,10 +288,19 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
goto err_out_free_netdev;
}
+ /* allocate queues */
+ err = ionic_qcqs_alloc(lif);
+ if (err)
+ goto err_out_free_lif_info;
+
list_add_tail(&lif->list, &ionic->lifs);
return lif;
+err_out_free_lif_info:
+ dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa);
+ lif->info = NULL;
+ lif->info_pa = 0;
err_out_free_netdev:
free_netdev(lif->netdev);
lif = NULL;
@@ -113,6 +335,8 @@ static void ionic_lif_free(struct lif *lif)
{
struct device *dev = lif->ionic->dev;
+ /* free queues */
+ ionic_qcqs_free(lif);
ionic_lif_reset(lif);
/* free lif info */
@@ -144,6 +368,30 @@ void ionic_lifs_free(struct ionic *ionic)
}
}
+static void ionic_lif_qcq_deinit(struct lif *lif, struct qcq *qcq)
+{
+ struct ionic_dev *idev = &lif->ionic->idev;
+ struct device *dev = lif->ionic->dev;
+
+ if (!qcq)
+ return;
+
+ ionic_debugfs_del_qcq(qcq);
+
+ if (!(qcq->flags & QCQ_F_INITED))
+ return;
+
+ if (qcq->flags & QCQ_F_INTR) {
+ ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
+ IONIC_INTR_MASK_SET);
+ synchronize_irq(qcq->intr.vector);
+ devm_free_irq(dev, qcq->intr.vector, &qcq->napi);
+ netif_napi_del(&qcq->napi);
+ }
+
+ qcq->flags &= ~QCQ_F_INITED;
+}
+
static void ionic_lif_deinit(struct lif *lif)
{
if (!test_bit(LIF_INITED, lif->state))
@@ -151,6 +399,9 @@ static void ionic_lif_deinit(struct lif *lif)
clear_bit(LIF_INITED, lif->state);
+ napi_disable(&lif->adminqcq->napi);
+ ionic_lif_qcq_deinit(lif, lif->adminqcq);
+
ionic_lif_reset(lif);
}
@@ -165,6 +416,74 @@ void ionic_lifs_deinit(struct ionic *ionic)
}
}
+static int ionic_request_irq(struct lif *lif, struct qcq *qcq)
+{
+ struct device *dev = lif->ionic->dev;
+ struct intr *intr = &qcq->intr;
+ struct queue *q = &qcq->q;
+ const char *name;
+
+ if (lif->registered)
+ name = lif->netdev->name;
+ else
+ name = dev_name(dev);
+
+ snprintf(intr->name, sizeof(intr->name),
+ "%s-%s-%s", DRV_NAME, name, q->name);
+
+ return devm_request_irq(dev, intr->vector, ionic_isr,
+ 0, intr->name, &qcq->napi);
+}
+
+static int ionic_lif_adminq_init(struct lif *lif)
+{
+ struct device *dev = lif->ionic->dev;
+ struct ionic_dev *idev = &lif->ionic->idev;
+ struct qcq *qcq = lif->adminqcq;
+ struct queue *q = &qcq->q;
+ struct q_init_comp comp;
+ int err;
+
+ mutex_lock(&lif->ionic->dev_cmd_lock);
+ ionic_dev_cmd_adminq_init(idev, qcq, lif->index, qcq->intr.index);
+ err = ionic_dev_cmd_wait(lif->ionic, devcmd_timeout);
+ ionic_dev_cmd_comp(idev, (union dev_cmd_comp *)&comp);
+ mutex_unlock(&lif->ionic->dev_cmd_lock);
+ if (err) {
+ netdev_err(lif->netdev, "adminq init failed %d\n", err);
+ return err;
+ }
+
+ q->hw_type = comp.hw_type;
+ q->hw_index = le32_to_cpu(comp.hw_index);
+ q->dbval = IONIC_DBELL_QID(q->hw_index);
+
+ dev_dbg(dev, "adminq->hw_type %d\n", q->hw_type);
+ dev_dbg(dev, "adminq->hw_index %d\n", q->hw_index);
+
+ netif_napi_add(lif->netdev, &qcq->napi, ionic_adminq_napi,
+ NAPI_POLL_WEIGHT);
+
+ err = ionic_request_irq(lif, qcq);
+ if (err) {
+ netdev_warn(lif->netdev, "adminq irq request failed %d\n", err);
+ netif_napi_del(&qcq->napi);
+ return err;
+ }
+
+ napi_enable(&qcq->napi);
+
+ if (qcq->flags & QCQ_F_INTR)
+ ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
+ IONIC_INTR_MASK_CLEAR);
+
+ qcq->flags |= QCQ_F_INITED;
+
+ ionic_debugfs_add_qcq(lif, qcq);
+
+ return 0;
+}
+
static int ionic_lif_init(struct lif *lif)
{
struct ionic_dev *idev = &lif->ionic->idev;
@@ -211,10 +530,19 @@ static int ionic_lif_init(struct lif *lif)
goto err_out_free_dbid;
}
+ err = ionic_lif_adminq_init(lif);
+ if (err)
+ goto err_out_adminq_deinit;
+
set_bit(LIF_INITED, lif->state);
return 0;
+err_out_adminq_deinit:
+ ionic_lif_qcq_deinit(lif, lif->adminqcq);
+ ionic_lif_reset(lif);
+ ionic_bus_unmap_dbpage(lif->ionic, lif->kern_dbpage);
+ lif->kern_dbpage = NULL;
err_out_free_dbid:
kfree(lif->dbid_inuse);
lif->dbid_inuse = NULL;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 98d0699234e9..28ab92b43a64 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -6,6 +6,57 @@
#include <linux/pci.h>
+#define IONIC_ADMINQ_LENGTH 16 /* must be a power of two */
+
+#define GET_NAPI_CNTR_IDX(work_done) (work_done)
+#define MAX_NUM_NAPI_CNTR (NAPI_POLL_WEIGHT + 1)
+#define GET_SG_CNTR_IDX(num_sg_elems) (num_sg_elems)
+#define MAX_NUM_SG_CNTR (IONIC_TX_MAX_SG_ELEMS + 1)
+
+struct tx_stats {
+ u64 pkts;
+ u64 bytes;
+};
+
+struct rx_stats {
+ u64 pkts;
+ u64 bytes;
+};
+
+#define QCQ_F_INITED BIT(0)
+#define QCQ_F_SG BIT(1)
+#define QCQ_F_INTR BIT(2)
+
+struct napi_stats {
+ u64 poll_count;
+ u64 work_done_cntr[MAX_NUM_NAPI_CNTR];
+};
+
+struct q_stats {
+ union {
+ struct tx_stats tx;
+ struct rx_stats rx;
+ };
+};
+
+struct qcq {
+ void *base;
+ dma_addr_t base_pa;
+ unsigned int total_size;
+ struct queue q;
+ struct cq cq;
+ struct intr intr;
+ struct napi_struct napi;
+ struct napi_stats napi_stats;
+ struct q_stats *stats;
+ unsigned int flags;
+ struct dentry *dentry;
+};
+
+#define q_to_qcq(q) container_of(q, struct qcq, q)
+#define napi_to_qcq(napi) container_of(napi, struct qcq, napi)
+#define napi_to_cq(napi) (&napi_to_qcq(napi)->cq)
+
enum lif_state_flags {
LIF_INITED,
@@ -25,6 +76,8 @@ struct lif {
unsigned int hw_index;
unsigned int kern_pid;
u64 __iomem *kern_dbpage;
+ spinlock_t adminq_lock; /* lock for AdminQ operations */
+ struct qcq *adminqcq;
unsigned int neqs;
unsigned int nxqs;
@@ -71,4 +124,23 @@ int ionic_lif_identify(struct ionic *ionic, u8 lif_type,
union lif_identity *lif_ident);
int ionic_lifs_size(struct ionic *ionic);
+static inline void debug_stats_napi_poll(struct qcq *qcq,
+ unsigned int work_done)
+{
+ u32 napi_cntr_idx;
+
+ qcq->napi_stats.poll_count++;
+
+ napi_cntr_idx = GET_NAPI_CNTR_IDX(work_done);
+ if (napi_cntr_idx > (MAX_NUM_NAPI_CNTR - 1))
+ napi_cntr_idx = MAX_NUM_NAPI_CNTR - 1;
+
+ qcq->napi_stats.work_done_cntr[napi_cntr_idx]++;
+}
+
+#define DEBUG_STATS_CQE_CNT(cq) ((cq)->compl_count++)
+#define DEBUG_STATS_INTR_REARM(intr) ((intr)->rearm_count++)
+#define DEBUG_STATS_NAPI_POLL(qcq, work_done) \
+ debug_stats_napi_poll(qcq, work_done)
+
#endif /* _IONIC_LIF_H_ */
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index b732e7345550..aa73ea81a4b2 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -169,6 +169,32 @@ static const char *ionic_opcode_to_str(enum cmd_opcode opcode)
}
}
+int ionic_napi(struct napi_struct *napi, int budget, ionic_cq_cb cb,
+ ionic_cq_done_cb done_cb, void *done_arg)
+{
+ struct qcq *qcq = napi_to_qcq(napi);
+ struct cq *cq = &qcq->cq;
+ u32 work_done, flags = 0;
+
+ work_done = ionic_cq_service(cq, budget, cb, done_cb, done_arg);
+
+ if (work_done < budget && napi_complete_done(napi, work_done)) {
+ flags |= IONIC_INTR_CRED_UNMASK;
+ DEBUG_STATS_INTR_REARM(cq->bound_intr);
+ }
+
+ if (work_done || flags) {
+ flags |= IONIC_INTR_CRED_RESET_COALESCE;
+ ionic_intr_credits(cq->lif->ionic->idev.intr_ctrl,
+ cq->bound_intr->index,
+ work_done, flags);
+ }
+
+ DEBUG_STATS_NAPI_POLL(qcq, work_done);
+
+ return work_done;
+}
+
int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
{
struct ionic_dev *idev = &ionic->idev;
--
2.17.1
^ permalink raw reply related
* [PATCH v3 net-next 17/19] ionic: Add RSS support
From: Shannon Nelson @ 2019-07-08 19:25 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190708192532.27420-1-snelson@pensando.io>
Add code to manipulate through ethtool the RSS configuration
used by the NIC.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
.../ethernet/pensando/ionic/ionic_ethtool.c | 73 ++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_lif.c | 87 +++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_lif.h | 8 ++
3 files changed, 168 insertions(+)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
index 6785c0cd4689..403882a5e09e 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
@@ -476,6 +476,74 @@ static int ionic_set_channels(struct net_device *netdev,
return 0;
}
+static int ionic_get_rxnfc(struct net_device *netdev,
+ struct ethtool_rxnfc *info, u32 *rules)
+{
+ struct lif *lif = netdev_priv(netdev);
+ int err = 0;
+
+ switch (info->cmd) {
+ case ETHTOOL_GRXRINGS:
+ info->data = lif->nxqs;
+ break;
+ default:
+ netdev_err(netdev, "Command parameter %d is not supported\n",
+ info->cmd);
+ err = -EOPNOTSUPP;
+ }
+
+ return err;
+}
+
+static u32 ionic_get_rxfh_indir_size(struct net_device *netdev)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ return le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
+}
+
+static u32 ionic_get_rxfh_key_size(struct net_device *netdev)
+{
+ return IONIC_RSS_HASH_KEY_SIZE;
+}
+
+static int ionic_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
+ u8 *hfunc)
+{
+ struct lif *lif = netdev_priv(netdev);
+ unsigned int i, tbl_sz;
+
+ if (indir) {
+ tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
+ for (i = 0; i < tbl_sz; i++)
+ indir[i] = lif->rss_ind_tbl[i];
+ }
+
+ if (key)
+ memcpy(key, lif->rss_hash_key, IONIC_RSS_HASH_KEY_SIZE);
+
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+
+ return 0;
+}
+
+static int ionic_set_rxfh(struct net_device *netdev, const u32 *indir,
+ const u8 *key, const u8 hfunc)
+{
+ struct lif *lif = netdev_priv(netdev);
+ int err;
+
+ if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
+ return -EOPNOTSUPP;
+
+ err = ionic_lif_rss_config(lif, lif->rss_types, key, indir);
+ if (err)
+ return err;
+
+ return 0;
+}
+
static u32 ionic_get_priv_flags(struct net_device *netdev)
{
struct lif *lif = netdev_priv(netdev);
@@ -594,6 +662,11 @@ static const struct ethtool_ops ionic_ethtool_ops = {
.get_strings = ionic_get_strings,
.get_ethtool_stats = ionic_get_stats,
.get_sset_count = ionic_get_sset_count,
+ .get_rxnfc = ionic_get_rxnfc,
+ .get_rxfh_indir_size = ionic_get_rxfh_indir_size,
+ .get_rxfh_key_size = ionic_get_rxfh_key_size,
+ .get_rxfh = ionic_get_rxfh,
+ .set_rxfh = ionic_set_rxfh,
.get_priv_flags = ionic_get_priv_flags,
.set_priv_flags = ionic_set_priv_flags,
.get_module_info = ionic_get_module_info,
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 9e032d813269..68a9975e34c6 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -26,6 +26,8 @@ static void ionic_qcq_free(struct lif *lif, struct qcq *qcq);
static int ionic_lif_txqs_init(struct lif *lif);
static int ionic_lif_rxqs_init(struct lif *lif);
static void ionic_lif_qcq_deinit(struct lif *lif, struct qcq *qcq);
+static int ionic_lif_rss_init(struct lif *lif);
+static int ionic_lif_rss_deinit(struct lif *lif);
static int ionic_set_nic_features(struct lif *lif, netdev_features_t features);
static int ionic_notifyq_clean(struct lif *lif, int budget);
@@ -1082,6 +1084,9 @@ static int ionic_txrx_init(struct lif *lif)
if (err)
goto err_out;
+ if (lif->netdev->features & NETIF_F_RXHASH)
+ ionic_lif_rss_init(lif);
+
ionic_set_rx_mode(lif->netdev);
return 0;
@@ -1206,6 +1211,7 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
struct device *dev = ionic->dev;
struct net_device *netdev;
struct lif *lif;
+ int tbl_sz;
int err;
netdev = alloc_etherdev_mqs(sizeof(*lif),
@@ -1260,10 +1266,24 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
if (err)
goto err_out_free_lif_info;
+ /* allocate rss indirection table */
+ tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
+ lif->rss_ind_tbl_sz = sizeof(*lif->rss_ind_tbl) * tbl_sz;
+ lif->rss_ind_tbl = dma_alloc_coherent(dev, lif->rss_ind_tbl_sz,
+ &lif->rss_ind_tbl_pa,
+ GFP_KERNEL);
+
+ if (!lif->rss_ind_tbl) {
+ dev_err(dev, "Failed to allocate rss indirection table, aborting\n");
+ goto err_out_free_qcqs;
+ }
+
list_add_tail(&lif->list, &ionic->lifs);
return lif;
+err_out_free_qcqs:
+ ionic_qcqs_free(lif);
err_out_free_lif_info:
dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa);
lif->info = NULL;
@@ -1302,6 +1322,12 @@ static void ionic_lif_free(struct lif *lif)
{
struct device *dev = lif->ionic->dev;
+ /* free rss indirection table */
+ dma_free_coherent(dev, lif->rss_ind_tbl_sz, lif->rss_ind_tbl,
+ lif->rss_ind_tbl_pa);
+ lif->rss_ind_tbl = NULL;
+ lif->rss_ind_tbl_pa = 0;
+
/* free queues */
ionic_qcqs_free(lif);
ionic_lif_reset(lif);
@@ -1337,6 +1363,66 @@ void ionic_lifs_free(struct ionic *ionic)
}
}
+int ionic_lif_rss_config(struct lif *lif, const u16 types,
+ const u8 *key, const u32 *indir)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.lif_setattr = {
+ .opcode = CMD_OPCODE_LIF_SETATTR,
+ .attr = IONIC_LIF_ATTR_RSS,
+ .rss.types = cpu_to_le16(types),
+ .rss.addr = cpu_to_le64(lif->rss_ind_tbl_pa),
+ },
+ };
+ unsigned int i, tbl_sz;
+
+ lif->rss_types = types;
+
+ if (key)
+ memcpy(lif->rss_hash_key, key, IONIC_RSS_HASH_KEY_SIZE);
+
+ if (indir) {
+ tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
+ for (i = 0; i < tbl_sz; i++)
+ lif->rss_ind_tbl[i] = indir[i];
+ }
+
+ memcpy(ctx.cmd.lif_setattr.rss.key, lif->rss_hash_key,
+ IONIC_RSS_HASH_KEY_SIZE);
+
+ return ionic_adminq_post_wait(lif, &ctx);
+}
+
+static int ionic_lif_rss_init(struct lif *lif)
+{
+ u8 rss_key[IONIC_RSS_HASH_KEY_SIZE];
+ unsigned int tbl_sz;
+ unsigned int i;
+
+ netdev_rss_key_fill(rss_key, IONIC_RSS_HASH_KEY_SIZE);
+
+ lif->rss_types = IONIC_RSS_TYPE_IPV4 |
+ IONIC_RSS_TYPE_IPV4_TCP |
+ IONIC_RSS_TYPE_IPV4_UDP |
+ IONIC_RSS_TYPE_IPV6 |
+ IONIC_RSS_TYPE_IPV6_TCP |
+ IONIC_RSS_TYPE_IPV6_UDP;
+
+ /* Fill indirection table with 'default' values */
+ tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
+ for (i = 0; i < tbl_sz; i++)
+ lif->rss_ind_tbl[i] = ethtool_rxfh_indir_default(i, lif->nxqs);
+
+ return ionic_lif_rss_config(lif, lif->rss_types, rss_key, NULL);
+}
+
+static int ionic_lif_rss_deinit(struct lif *lif)
+{
+ /* Disable RSS on the NIC */
+ return ionic_lif_rss_config(lif, 0x0, NULL, NULL);
+}
+
static void ionic_lif_qcq_deinit(struct lif *lif, struct qcq *qcq)
{
struct ionic_dev *idev = &lif->ionic->idev;
@@ -1369,6 +1455,7 @@ static void ionic_lif_deinit(struct lif *lif)
clear_bit(LIF_INITED, lif->state);
ionic_rx_filters_deinit(lif);
+ ionic_lif_rss_deinit(lif);
napi_disable(&lif->adminqcq->napi);
ionic_lif_qcq_deinit(lif, lif->notifyqcq);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index f93bfa2b4393..0e6908f959f2 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -166,6 +166,12 @@ struct lif {
dma_addr_t info_pa;
u32 info_sz;
+ u16 rss_types;
+ u8 rss_hash_key[IONIC_RSS_HASH_KEY_SIZE];
+ u8 *rss_ind_tbl;
+ dma_addr_t rss_ind_tbl_pa;
+ u32 rss_ind_tbl_sz;
+
struct rx_filters rx_filters;
struct ionic_deferred deferred;
u32 tx_coalesce_usecs;
@@ -215,6 +221,8 @@ void ionic_lifs_unregister(struct ionic *ionic);
int ionic_lif_identify(struct ionic *ionic, u8 lif_type,
union lif_identity *lif_ident);
int ionic_lifs_size(struct ionic *ionic);
+int ionic_lif_rss_config(struct lif *lif, u16 types,
+ const u8 *key, const u32 *indir);
int ionic_open(struct net_device *netdev);
int ionic_stop(struct net_device *netdev);
--
2.17.1
^ permalink raw reply related
* [PATCH v3 net-next 18/19] ionic: Add coalesce and other features
From: Shannon Nelson @ 2019-07-08 19:25 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190708192532.27420-1-snelson@pensando.io>
Interrupt coalescing, tunable copybreak value, and
tx timeout.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/ionic.h | 2 +-
.../ethernet/pensando/ionic/ionic_ethtool.c | 105 ++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_lif.c | 13 ++-
.../net/ethernet/pensando/ionic/ionic_lif.h | 1 +
4 files changed, 119 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index 9b720187b549..cd08166f73a9 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -11,7 +11,7 @@ struct lif;
#define DRV_NAME "ionic"
#define DRV_DESCRIPTION "Pensando Ethernet NIC Driver"
-#define DRV_VERSION "0.11.0-k"
+#define DRV_VERSION "0.11.0-44-k"
#define PCI_VENDOR_ID_PENSANDO 0x1dd8
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
index 403882a5e09e..7e46b5d7cd65 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
@@ -383,6 +383,75 @@ static int ionic_get_coalesce(struct net_device *netdev,
return 0;
}
+static int ionic_set_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *coalesce)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct identity *ident = &lif->ionic->ident;
+ struct ionic_dev *idev = &lif->ionic->idev;
+ u32 tx_coal, rx_coal;
+ struct qcq *qcq;
+ unsigned int i;
+
+ if (coalesce->rx_max_coalesced_frames ||
+ coalesce->rx_coalesce_usecs_irq ||
+ coalesce->rx_max_coalesced_frames_irq ||
+ coalesce->tx_max_coalesced_frames ||
+ coalesce->tx_coalesce_usecs_irq ||
+ coalesce->tx_max_coalesced_frames_irq ||
+ coalesce->stats_block_coalesce_usecs ||
+ coalesce->use_adaptive_rx_coalesce ||
+ coalesce->use_adaptive_tx_coalesce ||
+ coalesce->pkt_rate_low ||
+ coalesce->rx_coalesce_usecs_low ||
+ coalesce->rx_max_coalesced_frames_low ||
+ coalesce->tx_coalesce_usecs_low ||
+ coalesce->tx_max_coalesced_frames_low ||
+ coalesce->pkt_rate_high ||
+ coalesce->rx_coalesce_usecs_high ||
+ coalesce->rx_max_coalesced_frames_high ||
+ coalesce->tx_coalesce_usecs_high ||
+ coalesce->tx_max_coalesced_frames_high ||
+ coalesce->rate_sample_interval)
+ return -EINVAL;
+
+ if (ident->dev.intr_coal_div == 0)
+ return -EIO;
+
+ /* Convert from usecs to device units */
+ tx_coal = coalesce->tx_coalesce_usecs *
+ le32_to_cpu(ident->dev.intr_coal_mult) /
+ le32_to_cpu(ident->dev.intr_coal_div);
+ rx_coal = coalesce->rx_coalesce_usecs *
+ le32_to_cpu(ident->dev.intr_coal_mult) /
+ le32_to_cpu(ident->dev.intr_coal_div);
+
+ if (tx_coal > INTR_CTRL_COAL_MAX || rx_coal > INTR_CTRL_COAL_MAX)
+ return -ERANGE;
+
+ if (coalesce->tx_coalesce_usecs != lif->tx_coalesce_usecs) {
+ for (i = 0; i < lif->nxqs; i++) {
+ qcq = lif->txqcqs[i].qcq;
+ ionic_intr_coal_init(idev->intr_ctrl,
+ qcq->intr.index,
+ tx_coal);
+ }
+ lif->tx_coalesce_usecs = coalesce->tx_coalesce_usecs;
+ }
+
+ if (coalesce->rx_coalesce_usecs != lif->rx_coalesce_usecs) {
+ for (i = 0; i < lif->nxqs; i++) {
+ qcq = lif->rxqcqs[i].qcq;
+ ionic_intr_coal_init(idev->intr_ctrl,
+ qcq->intr.index,
+ rx_coal);
+ }
+ lif->rx_coalesce_usecs = coalesce->rx_coalesce_usecs;
+ }
+
+ return 0;
+}
+
static void ionic_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring)
{
@@ -570,6 +639,39 @@ static int ionic_set_priv_flags(struct net_device *netdev, u32 priv_flags)
return 0;
}
+static int ionic_set_tunable(struct net_device *dev,
+ const struct ethtool_tunable *tuna,
+ const void *data)
+{
+ struct lif *lif = netdev_priv(dev);
+
+ switch (tuna->id) {
+ case ETHTOOL_RX_COPYBREAK:
+ lif->rx_copybreak = *(u32 *)data;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ionic_get_tunable(struct net_device *netdev,
+ const struct ethtool_tunable *tuna, void *data)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ switch (tuna->id) {
+ case ETHTOOL_RX_COPYBREAK:
+ *(u32 *)data = lif->rx_copybreak;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static int ionic_get_module_info(struct net_device *netdev,
struct ethtool_modinfo *modinfo)
@@ -655,6 +757,7 @@ static const struct ethtool_ops ionic_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_link_ksettings = ionic_get_link_ksettings,
.get_coalesce = ionic_get_coalesce,
+ .set_coalesce = ionic_set_coalesce,
.get_ringparam = ionic_get_ringparam,
.set_ringparam = ionic_set_ringparam,
.get_channels = ionic_get_channels,
@@ -669,6 +772,8 @@ static const struct ethtool_ops ionic_ethtool_ops = {
.set_rxfh = ionic_set_rxfh,
.get_priv_flags = ionic_get_priv_flags,
.set_priv_flags = ionic_set_priv_flags,
+ .get_tunable = ionic_get_tunable,
+ .set_tunable = ionic_set_tunable,
.get_module_info = ionic_get_module_info,
.get_module_eeprom = ionic_get_module_eeprom,
.get_pauseparam = ionic_get_pauseparam,
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 68a9975e34c6..8473b065763b 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -744,9 +744,19 @@ static int ionic_change_mtu(struct net_device *netdev, int new_mtu)
return err;
}
+static void ionic_tx_timeout_work(struct work_struct *ws)
+{
+ struct lif *lif = container_of(ws, struct lif, tx_timeout_work);
+
+ netdev_info(lif->netdev, "Tx Timeout recovery\n");
+ ionic_reset_queues(lif);
+}
+
static void ionic_tx_timeout(struct net_device *netdev)
{
- netdev_info(netdev, "%s: stubbed\n", __func__);
+ struct lif *lif = netdev_priv(netdev);
+
+ schedule_work(&lif->tx_timeout_work);
}
static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
@@ -2009,6 +2019,7 @@ static int ionic_lif_init(struct lif *lif)
ionic_link_status_check(lif);
+ INIT_WORK(&lif->tx_timeout_work, ionic_tx_timeout_work);
return 0;
err_out_notifyq_deinit:
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 0e6908f959f2..76cc519acd5a 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -180,6 +180,7 @@ struct lif {
unsigned int dbid_count;
struct dentry *dentry;
u32 flags;
+ struct work_struct tx_timeout_work;
};
#define lif_to_txqcq(lif, i) ((lif)->txqcqs[i].qcq)
--
2.17.1
^ permalink raw reply related
* [PATCH v3 net-next 19/19] ionic: Add basic devlink interface
From: Shannon Nelson @ 2019-07-08 19:25 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190708192532.27420-1-snelson@pensando.io>
Add a devlink interface for access to information that isn't
normally available through ethtool or the iplink interface.
Example:
$ ./devlink -j -p dev info pci/0000:b6:00.0
{
"info": {
"pci/0000:b6:00.0": {
"driver": "ionic",
"serial_number": "FLM18420073",
"versions": {
"fixed": {
"fw_version": "0.11.0-50",
"fw_status": "0x1",
"fw_heartbeat": "0x716ce",
"asic_type": "0x0",
"asic_rev": "0x0"
}
}
}
}
}
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/Makefile | 2 +-
drivers/net/ethernet/pensando/ionic/ionic.h | 1 +
.../ethernet/pensando/ionic/ionic_bus_pci.c | 7 ++
.../ethernet/pensando/ionic/ionic_devlink.c | 89 +++++++++++++++++++
.../ethernet/pensando/ionic/ionic_devlink.h | 12 +++
5 files changed, 110 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_devlink.c
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_devlink.h
diff --git a/drivers/net/ethernet/pensando/ionic/Makefile b/drivers/net/ethernet/pensando/ionic/Makefile
index 4f3cfbf36c23..ce187c7b33a8 100644
--- a/drivers/net/ethernet/pensando/ionic/Makefile
+++ b/drivers/net/ethernet/pensando/ionic/Makefile
@@ -5,4 +5,4 @@ obj-$(CONFIG_IONIC) := ionic.o
ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o ionic_ethtool.o \
ionic_lif.o ionic_rx_filter.o ionic_txrx.o ionic_debugfs.o \
- ionic_stats.o
+ ionic_stats.o ionic_devlink.o
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index cd08166f73a9..a0034bc5b4a1 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -44,6 +44,7 @@ struct ionic {
DECLARE_BITMAP(intrs, INTR_CTRL_REGS_MAX);
struct work_struct nb_work;
struct notifier_block nb;
+ struct devlink *dl;
};
struct ionic_admin_ctx {
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
index 98c12b770c7f..a8c99254489f 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
@@ -10,6 +10,7 @@
#include "ionic_bus.h"
#include "ionic_lif.h"
#include "ionic_debugfs.h"
+#include "ionic_devlink.h"
/* Supported devices */
static const struct pci_device_id ionic_id_table[] = {
@@ -212,9 +213,14 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_deinit_lifs;
}
+ err = ionic_devlink_register(ionic);
+ if (err)
+ dev_err(dev, "Cannot register devlink (ignored): %d\n", err);
+
return 0;
err_out_deinit_lifs:
+ ionic_devlink_unregister(ionic);
ionic_lifs_deinit(ionic);
err_out_free_lifs:
ionic_lifs_free(ionic);
@@ -247,6 +253,7 @@ static void ionic_remove(struct pci_dev *pdev)
struct ionic *ionic = pci_get_drvdata(pdev);
if (ionic) {
+ ionic_devlink_unregister(ionic);
ionic_lifs_unregister(ionic);
ionic_lifs_deinit(ionic);
ionic_lifs_free(ionic);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
new file mode 100644
index 000000000000..fbbfcdde292f
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include "ionic.h"
+#include "ionic_bus.h"
+#include "ionic_lif.h"
+#include "ionic_devlink.h"
+
+struct ionic_devlink {
+ struct ionic *ionic;
+};
+
+static int ionic_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
+ struct netlink_ext_ack *extack)
+{
+ struct ionic *ionic = *(struct ionic **)devlink_priv(dl);
+ struct ionic_dev *idev = &ionic->idev;
+ char buf[16];
+ u32 val;
+
+ devlink_info_driver_name_put(req, DRV_NAME);
+
+ devlink_info_version_fixed_put(req, "fw_version",
+ idev->dev_info.fw_version);
+
+ val = ioread8(&idev->dev_info_regs->fw_status);
+ snprintf(buf, sizeof(buf), "0x%x", val);
+ devlink_info_version_fixed_put(req, "fw_status", buf);
+
+ val = ioread32(&idev->dev_info_regs->fw_heartbeat);
+ snprintf(buf, sizeof(buf), "0x%x", val);
+ devlink_info_version_fixed_put(req, "fw_heartbeat", buf);
+
+ snprintf(buf, sizeof(buf), "0x%x", idev->dev_info.asic_type);
+ devlink_info_version_fixed_put(req, "asic_type", buf);
+
+ snprintf(buf, sizeof(buf), "0x%x", idev->dev_info.asic_rev);
+ devlink_info_version_fixed_put(req, "asic_rev", buf);
+
+ devlink_info_serial_number_put(req, idev->dev_info.serial_num);
+
+ return 0;
+}
+
+static const struct devlink_ops ionic_dl_ops = {
+ .info_get = ionic_dl_info_get,
+};
+
+int ionic_devlink_register(struct ionic *ionic)
+{
+ struct devlink *dl;
+ struct ionic **ip;
+ int err;
+
+ dl = devlink_alloc(&ionic_dl_ops, sizeof(struct ionic *));
+ if (!dl) {
+ dev_warn(ionic->dev, "devlink_alloc failed");
+ return -ENOMEM;
+ }
+
+ ip = (struct ionic **)devlink_priv(dl);
+ *ip = ionic;
+ ionic->dl = dl;
+
+ err = devlink_register(dl, ionic->dev);
+ if (err) {
+ dev_warn(ionic->dev, "devlink_register failed: %d\n", err);
+ goto err_dl_free;
+ }
+
+ return 0;
+
+err_dl_free:
+ ionic->dl = NULL;
+ devlink_free(dl);
+ return err;
+}
+
+void ionic_devlink_unregister(struct ionic *ionic)
+{
+ if (!ionic->dl)
+ return;
+
+ devlink_unregister(ionic->dl);
+ devlink_free(ionic->dl);
+}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.h b/drivers/net/ethernet/pensando/ionic/ionic_devlink.h
new file mode 100644
index 000000000000..35528884e29f
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#ifndef _IONIC_DEVLINK_H_
+#define _IONIC_DEVLINK_H_
+
+#include <net/devlink.h>
+
+int ionic_devlink_register(struct ionic *ionic);
+void ionic_devlink_unregister(struct ionic *ionic);
+
+#endif /* _IONIC_DEVLINK_H_ */
--
2.17.1
^ permalink raw reply related
* [PATCH v3 net-next 16/19] ionic: Add driver stats
From: Shannon Nelson @ 2019-07-08 19:25 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190708192532.27420-1-snelson@pensando.io>
Add in the detailed statistics for ethtool -S that the driver
keeps as it processes packets. Display of the additional
debug statistics can be enabled through the ethtool priv-flags
feature.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/Makefile | 3 +-
.../ethernet/pensando/ionic/ionic_ethtool.c | 101 ++++++
.../net/ethernet/pensando/ionic/ionic_lif.h | 14 +
.../net/ethernet/pensando/ionic/ionic_stats.c | 333 ++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_stats.h | 53 +++
5 files changed, 503 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_stats.c
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_stats.h
diff --git a/drivers/net/ethernet/pensando/ionic/Makefile b/drivers/net/ethernet/pensando/ionic/Makefile
index 0e2dc53f08d4..4f3cfbf36c23 100644
--- a/drivers/net/ethernet/pensando/ionic/Makefile
+++ b/drivers/net/ethernet/pensando/ionic/Makefile
@@ -4,4 +4,5 @@
obj-$(CONFIG_IONIC) := ionic.o
ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o ionic_ethtool.o \
- ionic_lif.o ionic_rx_filter.o ionic_txrx.o ionic_debugfs.o
+ ionic_lif.o ionic_rx_filter.o ionic_txrx.o ionic_debugfs.o \
+ ionic_stats.o
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
index 2bbe5819387b..6785c0cd4689 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
@@ -8,6 +8,76 @@
#include "ionic_bus.h"
#include "ionic_lif.h"
#include "ionic_ethtool.h"
+#include "ionic_stats.h"
+
+static const char ionic_priv_flags_strings[][ETH_GSTRING_LEN] = {
+#define PRIV_F_SW_DBG_STATS BIT(0)
+ "sw-dbg-stats",
+};
+#define PRIV_FLAGS_COUNT ARRAY_SIZE(ionic_priv_flags_strings)
+
+static void ionic_get_stats_strings(struct lif *lif, u8 *buf)
+{
+ u32 i;
+
+ for (i = 0; i < ionic_num_stats_grps; i++)
+ ionic_stats_groups[i].get_strings(lif, &buf);
+}
+
+static void ionic_get_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, u64 *buf)
+{
+ struct lif *lif;
+ u32 i;
+
+ lif = netdev_priv(netdev);
+
+ memset(buf, 0, stats->n_stats * sizeof(*buf));
+ for (i = 0; i < ionic_num_stats_grps; i++)
+ ionic_stats_groups[i].get_values(lif, &buf);
+}
+
+static int ionic_get_stats_count(struct lif *lif)
+{
+ int i, num_stats = 0;
+
+ for (i = 0; i < ionic_num_stats_grps; i++)
+ num_stats += ionic_stats_groups[i].get_count(lif);
+
+ return num_stats;
+}
+
+static int ionic_get_sset_count(struct net_device *netdev, int sset)
+{
+ struct lif *lif = netdev_priv(netdev);
+ int count = 0;
+
+ switch (sset) {
+ case ETH_SS_STATS:
+ count = ionic_get_stats_count(lif);
+ break;
+ case ETH_SS_PRIV_FLAGS:
+ count = PRIV_FLAGS_COUNT;
+ break;
+ }
+ return count;
+}
+
+static void ionic_get_strings(struct net_device *netdev,
+ u32 sset, u8 *buf)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ switch (sset) {
+ case ETH_SS_STATS:
+ ionic_get_stats_strings(lif, buf);
+ break;
+ case ETH_SS_PRIV_FLAGS:
+ memcpy(buf, ionic_priv_flags_strings,
+ PRIV_FLAGS_COUNT * ETH_GSTRING_LEN);
+ break;
+ }
+}
static void ionic_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *drvinfo)
@@ -406,6 +476,32 @@ static int ionic_set_channels(struct net_device *netdev,
return 0;
}
+static u32 ionic_get_priv_flags(struct net_device *netdev)
+{
+ struct lif *lif = netdev_priv(netdev);
+ u32 priv_flags = 0;
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state))
+ priv_flags |= PRIV_F_SW_DBG_STATS;
+
+ return priv_flags;
+}
+
+static int ionic_set_priv_flags(struct net_device *netdev, u32 priv_flags)
+{
+ struct lif *lif = netdev_priv(netdev);
+ u32 flags = lif->flags;
+
+ clear_bit(LIF_SW_DEBUG_STATS, lif->state);
+ if (priv_flags & PRIV_F_SW_DBG_STATS)
+ set_bit(LIF_SW_DEBUG_STATS, lif->state);
+
+ if (flags != lif->flags)
+ lif->flags = flags;
+
+ return 0;
+}
+
static int ionic_get_module_info(struct net_device *netdev,
struct ethtool_modinfo *modinfo)
@@ -495,6 +591,11 @@ static const struct ethtool_ops ionic_ethtool_ops = {
.set_ringparam = ionic_set_ringparam,
.get_channels = ionic_get_channels,
.set_channels = ionic_set_channels,
+ .get_strings = ionic_get_strings,
+ .get_ethtool_stats = ionic_get_stats,
+ .get_sset_count = ionic_get_sset_count,
+ .get_priv_flags = ionic_get_priv_flags,
+ .set_priv_flags = ionic_set_priv_flags,
.get_module_info = ionic_get_module_info,
.get_module_eeprom = ionic_get_module_eeprom,
.get_pauseparam = ionic_get_pauseparam,
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 88d5bf8f58a1..f93bfa2b4393 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -108,8 +108,22 @@ struct ionic_deferred {
struct work_struct work;
};
+struct lif_sw_stats {
+ u64 tx_packets;
+ u64 tx_bytes;
+ u64 rx_packets;
+ u64 rx_bytes;
+ u64 tx_tso;
+ u64 tx_no_csum;
+ u64 tx_csum;
+ u64 rx_csum_none;
+ u64 rx_csum_complete;
+ u64 rx_csum_error;
+};
+
enum lif_state_flags {
LIF_INITED,
+ LIF_SW_DEBUG_STATS,
LIF_UP,
LIF_LINK_CHECK_NEEDED,
LIF_QUEUE_RESET,
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.c b/drivers/net/ethernet/pensando/ionic/ionic_stats.c
new file mode 100644
index 000000000000..978a9f89804a
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/netdevice.h>
+
+#include "ionic.h"
+#include "ionic_lif.h"
+#include "ionic_stats.h"
+
+static const struct ionic_stat_desc ionic_lif_stats_desc[] = {
+ IONIC_LIF_STAT_DESC(tx_packets),
+ IONIC_LIF_STAT_DESC(tx_bytes),
+ IONIC_LIF_STAT_DESC(rx_packets),
+ IONIC_LIF_STAT_DESC(rx_bytes),
+ IONIC_LIF_STAT_DESC(tx_tso),
+ IONIC_LIF_STAT_DESC(tx_no_csum),
+ IONIC_LIF_STAT_DESC(tx_csum),
+ IONIC_LIF_STAT_DESC(rx_csum_none),
+ IONIC_LIF_STAT_DESC(rx_csum_complete),
+ IONIC_LIF_STAT_DESC(rx_csum_error),
+};
+
+static const struct ionic_stat_desc ionic_tx_stats_desc[] = {
+ IONIC_TX_STAT_DESC(pkts),
+ IONIC_TX_STAT_DESC(bytes),
+ IONIC_TX_STAT_DESC(clean),
+ IONIC_TX_STAT_DESC(dma_map_err),
+ IONIC_TX_STAT_DESC(linearize),
+ IONIC_TX_STAT_DESC(frags),
+};
+
+static const struct ionic_stat_desc ionic_rx_stats_desc[] = {
+ IONIC_RX_STAT_DESC(pkts),
+ IONIC_RX_STAT_DESC(bytes),
+ IONIC_RX_STAT_DESC(dma_map_err),
+ IONIC_RX_STAT_DESC(alloc_err),
+ IONIC_RX_STAT_DESC(csum_none),
+ IONIC_RX_STAT_DESC(csum_complete),
+ IONIC_RX_STAT_DESC(csum_error),
+};
+
+static const struct ionic_stat_desc ionic_txq_stats_desc[] = {
+ IONIC_TX_Q_STAT_DESC(stop),
+ IONIC_TX_Q_STAT_DESC(wake),
+ IONIC_TX_Q_STAT_DESC(drop),
+ IONIC_TX_Q_STAT_DESC(dbell_count),
+};
+
+static const struct ionic_stat_desc ionic_dbg_cq_stats_desc[] = {
+ IONIC_CQ_STAT_DESC(compl_count),
+};
+
+static const struct ionic_stat_desc ionic_dbg_intr_stats_desc[] = {
+ IONIC_INTR_STAT_DESC(rearm_count),
+};
+
+static const struct ionic_stat_desc ionic_dbg_napi_stats_desc[] = {
+ IONIC_NAPI_STAT_DESC(poll_count),
+};
+
+#define IONIC_NUM_LIF_STATS ARRAY_SIZE(ionic_lif_stats_desc)
+#define IONIC_NUM_TX_STATS ARRAY_SIZE(ionic_tx_stats_desc)
+#define IONIC_NUM_RX_STATS ARRAY_SIZE(ionic_rx_stats_desc)
+#define IONIC_NUM_TX_Q_STATS ARRAY_SIZE(ionic_txq_stats_desc)
+#define IONIC_NUM_DBG_CQ_STATS ARRAY_SIZE(ionic_dbg_cq_stats_desc)
+#define IONIC_NUM_DBG_INTR_STATS ARRAY_SIZE(ionic_dbg_intr_stats_desc)
+#define IONIC_NUM_DBG_NAPI_STATS ARRAY_SIZE(ionic_dbg_napi_stats_desc)
+
+#define MAX_Q(lif) ((lif)->netdev->real_num_tx_queues)
+
+static void ionic_get_lif_stats(struct lif *lif, struct lif_sw_stats *stats)
+{
+ struct tx_stats *tstats;
+ struct rx_stats *rstats;
+ struct qcq *txqcq;
+ struct qcq *rxqcq;
+ int q_num;
+
+ memset(stats, 0, sizeof(*stats));
+
+ for (q_num = 0; q_num < MAX_Q(lif); q_num++) {
+ txqcq = lif_to_txqcq(lif, q_num);
+ if (txqcq && txqcq->stats) {
+ tstats = &txqcq->stats->tx;
+ stats->tx_packets += tstats->pkts;
+ stats->tx_bytes += tstats->bytes;
+ stats->tx_tso += tstats->tso;
+ stats->tx_no_csum += tstats->no_csum;
+ stats->tx_csum += tstats->csum;
+ }
+
+ rxqcq = lif_to_rxqcq(lif, q_num);
+ if (rxqcq && rxqcq->stats) {
+ rstats = &rxqcq->stats->rx;
+ stats->rx_packets += rstats->pkts;
+ stats->rx_bytes += rstats->bytes;
+ stats->rx_csum_none += rstats->csum_none;
+ stats->rx_csum_complete += rstats->csum_complete;
+ stats->rx_csum_error += rstats->csum_error;
+ }
+ }
+}
+
+static u64 ionic_sw_stats_get_count(struct lif *lif)
+{
+ u64 total = 0;
+
+ /* lif stats */
+ total += IONIC_NUM_LIF_STATS;
+
+ /* tx stats */
+ total += MAX_Q(lif) * IONIC_NUM_TX_STATS;
+
+ /* rx stats */
+ total += MAX_Q(lif) * IONIC_NUM_RX_STATS;
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state)) {
+ /* tx debug stats */
+ total += MAX_Q(lif) * (IONIC_NUM_DBG_CQ_STATS +
+ IONIC_NUM_TX_Q_STATS +
+ IONIC_NUM_DBG_INTR_STATS +
+ IONIC_NUM_DBG_NAPI_STATS +
+ MAX_NUM_NAPI_CNTR +
+ MAX_NUM_SG_CNTR);
+
+ /* rx debug stats */
+ total += MAX_Q(lif) * (IONIC_NUM_DBG_CQ_STATS +
+ IONIC_NUM_DBG_INTR_STATS +
+ IONIC_NUM_DBG_NAPI_STATS +
+ MAX_NUM_NAPI_CNTR);
+ }
+
+ return total;
+}
+
+static void ionic_sw_stats_get_strings(struct lif *lif, u8 **buf)
+{
+ int i, q_num;
+
+ for (i = 0; i < IONIC_NUM_LIF_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN, ionic_lif_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (q_num = 0; q_num < MAX_Q(lif); q_num++) {
+ for (i = 0; i < IONIC_NUM_TX_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN, "tx_%d_%s",
+ q_num, ionic_tx_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state)) {
+ for (i = 0; i < IONIC_NUM_TX_Q_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_%s",
+ q_num,
+ ionic_txq_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_CQ_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_cq_%s",
+ q_num,
+ ionic_dbg_cq_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_INTR_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_intr_%s",
+ q_num,
+ ionic_dbg_intr_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_NAPI_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_napi_%s",
+ q_num,
+ ionic_dbg_napi_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < MAX_NUM_NAPI_CNTR; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_napi_work_done_%d",
+ q_num, i);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < MAX_NUM_SG_CNTR; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_sg_cntr_%d",
+ q_num, i);
+ *buf += ETH_GSTRING_LEN;
+ }
+ }
+ }
+ for (q_num = 0; q_num < MAX_Q(lif); q_num++) {
+ for (i = 0; i < IONIC_NUM_RX_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "rx_%d_%s",
+ q_num, ionic_rx_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state)) {
+ for (i = 0; i < IONIC_NUM_DBG_CQ_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "rxq_%d_cq_%s",
+ q_num,
+ ionic_dbg_cq_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_INTR_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "rxq_%d_intr_%s",
+ q_num,
+ ionic_dbg_intr_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_NAPI_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "rxq_%d_napi_%s",
+ q_num,
+ ionic_dbg_napi_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < MAX_NUM_NAPI_CNTR; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "rxq_%d_napi_work_done_%d",
+ q_num, i);
+ *buf += ETH_GSTRING_LEN;
+ }
+ }
+ }
+}
+
+static void ionic_sw_stats_get_values(struct lif *lif, u64 **buf)
+{
+ struct lif_sw_stats lif_stats;
+ struct qcq *txqcq, *rxqcq;
+ int i, q_num;
+
+ ionic_get_lif_stats(lif, &lif_stats);
+
+ for (i = 0; i < IONIC_NUM_LIF_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&lif_stats, &ionic_lif_stats_desc[i]);
+ (*buf)++;
+ }
+
+ for (q_num = 0; q_num < MAX_Q(lif); q_num++) {
+ txqcq = lif_to_txqcq(lif, q_num);
+
+ for (i = 0; i < IONIC_NUM_TX_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&txqcq->stats->tx,
+ &ionic_tx_stats_desc[i]);
+ (*buf)++;
+ }
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state)) {
+ for (i = 0; i < IONIC_NUM_TX_Q_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&txqcq->q,
+ &ionic_txq_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_CQ_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&txqcq->cq,
+ &ionic_dbg_cq_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_INTR_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&txqcq->intr,
+ &ionic_dbg_intr_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_NAPI_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&txqcq->napi_stats,
+ &ionic_dbg_napi_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < MAX_NUM_NAPI_CNTR; i++) {
+ **buf = txqcq->napi_stats.work_done_cntr[i];
+ (*buf)++;
+ }
+ for (i = 0; i < MAX_NUM_SG_CNTR; i++) {
+ **buf = txqcq->stats->tx.sg_cntr[i];
+ (*buf)++;
+ }
+ }
+ }
+
+ for (q_num = 0; q_num < MAX_Q(lif); q_num++) {
+ rxqcq = lif_to_rxqcq(lif, q_num);
+
+ for (i = 0; i < IONIC_NUM_RX_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&rxqcq->stats->rx,
+ &ionic_rx_stats_desc[i]);
+ (*buf)++;
+ }
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state)) {
+ for (i = 0; i < IONIC_NUM_DBG_CQ_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&rxqcq->cq,
+ &ionic_dbg_cq_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_INTR_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&rxqcq->intr,
+ &ionic_dbg_intr_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_NAPI_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&rxqcq->napi_stats,
+ &ionic_dbg_napi_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < MAX_NUM_NAPI_CNTR; i++) {
+ **buf = rxqcq->napi_stats.work_done_cntr[i];
+ (*buf)++;
+ }
+ }
+ }
+}
+
+const struct ionic_stats_group_intf ionic_stats_groups[] = {
+ /* SW Stats group */
+ {
+ .get_strings = ionic_sw_stats_get_strings,
+ .get_values = ionic_sw_stats_get_values,
+ .get_count = ionic_sw_stats_get_count,
+ },
+ /* Add more stat groups here */
+};
+
+const int ionic_num_stats_grps = ARRAY_SIZE(ionic_stats_groups);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.h b/drivers/net/ethernet/pensando/ionic/ionic_stats.h
new file mode 100644
index 000000000000..efa76694634e
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#ifndef _IONIC_STATS_H_
+#define _IONIC_STATS_H_
+
+#define IONIC_STAT_TO_OFFSET(type, stat_name) (offsetof(type, stat_name))
+
+#define IONIC_STAT_DESC(type, stat_name) { \
+ .name = #stat_name, \
+ .offset = IONIC_STAT_TO_OFFSET(type, stat_name) \
+}
+
+#define IONIC_LIF_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct lif_sw_stats, stat_name)
+
+#define IONIC_TX_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct tx_stats, stat_name)
+
+#define IONIC_RX_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct rx_stats, stat_name)
+
+#define IONIC_TX_Q_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct queue, stat_name)
+
+#define IONIC_CQ_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct cq, stat_name)
+
+#define IONIC_INTR_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct intr, stat_name)
+
+#define IONIC_NAPI_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct napi_stats, stat_name)
+
+/* Interface structure for a particalar stats group */
+struct ionic_stats_group_intf {
+ void (*get_strings)(struct lif *lif, u8 **buf);
+ void (*get_values)(struct lif *lif, u64 **buf);
+ u64 (*get_count)(struct lif *lif);
+};
+
+extern const struct ionic_stats_group_intf ionic_stats_groups[];
+extern const int ionic_num_stats_grps;
+
+#define IONIC_READ_STAT64(base_ptr, desc_ptr) \
+ (*((u64 *)(((u8 *)(base_ptr)) + (desc_ptr)->offset)))
+
+struct ionic_stat_desc {
+ char name[ETH_GSTRING_LEN];
+ u64 offset;
+};
+
+#endif /* _IONIC_STATS_H_ */
--
2.17.1
^ permalink raw reply related
* [PATCH v3 net-next 15/19] ionic: Add netdev-event handling
From: Shannon Nelson @ 2019-07-08 19:25 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190708192532.27420-1-snelson@pensando.io>
When the netdev gets a new name from userland, pass that name
down to the NIC for internal tracking.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/ionic.h | 2 +
.../net/ethernet/pensando/ionic/ionic_lif.c | 65 +++++++++++++++++++
2 files changed, 67 insertions(+)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index d7eee79b2a10..9b720187b549 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -42,6 +42,8 @@ struct ionic {
DECLARE_BITMAP(lifbits, IONIC_LIFS_MAX);
unsigned int nintrs;
DECLARE_BITMAP(intrs, INTR_CTRL_REGS_MAX);
+ struct work_struct nb_work;
+ struct notifier_block nb;
};
struct ionic_admin_ctx {
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 40d3b1cb362a..9e032d813269 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -1954,10 +1954,69 @@ int ionic_lifs_init(struct ionic *ionic)
return 0;
}
+static void ionic_lif_notify_work(struct work_struct *ws)
+{
+}
+
+static void ionic_lif_set_netdev_info(struct lif *lif)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.lif_setattr = {
+ .opcode = CMD_OPCODE_LIF_SETATTR,
+ .index = cpu_to_le16(lif->index),
+ .attr = IONIC_LIF_ATTR_NAME,
+ },
+ };
+
+ strlcpy(ctx.cmd.lif_setattr.name, lif->netdev->name,
+ sizeof(ctx.cmd.lif_setattr.name));
+
+ dev_info(lif->ionic->dev, "NETDEV_CHANGENAME %s %s\n",
+ lif->name, ctx.cmd.lif_setattr.name);
+
+ ionic_adminq_post_wait(lif, &ctx);
+}
+
+static struct lif *ionic_netdev_lif(struct net_device *netdev)
+{
+ if (!netdev || netdev->netdev_ops->ndo_start_xmit != ionic_start_xmit)
+ return NULL;
+
+ return netdev_priv(netdev);
+}
+
+static int ionic_lif_notify(struct notifier_block *nb,
+ unsigned long event, void *info)
+{
+ struct net_device *ndev = netdev_notifier_info_to_dev(info);
+ struct ionic *ionic = container_of(nb, struct ionic, nb);
+ struct lif *lif = ionic_netdev_lif(ndev);
+
+ if (!lif || lif->ionic != ionic)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_CHANGENAME:
+ ionic_lif_set_netdev_info(lif);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
int ionic_lifs_register(struct ionic *ionic)
{
int err;
+ INIT_WORK(&ionic->nb_work, ionic_lif_notify_work);
+
+ ionic->nb.notifier_call = ionic_lif_notify;
+
+ err = register_netdevice_notifier(&ionic->nb);
+ if (err)
+ ionic->nb.notifier_call = NULL;
+
/* only register LIF0 for now */
err = register_netdev(ionic->master_lif->netdev);
if (err) {
@@ -1973,6 +2032,12 @@ int ionic_lifs_register(struct ionic *ionic)
void ionic_lifs_unregister(struct ionic *ionic)
{
+ if (ionic->nb.notifier_call) {
+ unregister_netdevice_notifier(&ionic->nb);
+ cancel_work_sync(&ionic->nb_work);
+ ionic->nb.notifier_call = NULL;
+ }
+
/* There is only one lif ever registered in the
* current model, so don't bother searching the
* ionic->lif for candidates to unregister
--
2.17.1
^ permalink raw reply related
* [PATCH v3 net-next 11/19] ionic: Add Rx filter and rx_mode ndo support
From: Shannon Nelson @ 2019-07-08 19:25 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190708192532.27420-1-snelson@pensando.io>
Add the Rx filtering and rx_mode NDO callbacks. Also add
the deferred work thread handling needed to manage the filter
requests otuside of the netif_addr_lock spinlock.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
.../net/ethernet/pensando/ionic/ionic_lif.c | 389 +++++++++++++++++-
.../net/ethernet/pensando/ionic/ionic_lif.h | 29 ++
2 files changed, 412 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 72ac0fd56b69..efcda1337f91 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -12,9 +12,55 @@
#include "ionic_lif.h"
#include "ionic_debugfs.h"
+static void ionic_lif_rx_mode(struct lif *lif, unsigned int rx_mode);
+static int ionic_lif_addr_add(struct lif *lif, const u8 *addr);
+static int ionic_lif_addr_del(struct lif *lif, const u8 *addr);
+
static int ionic_set_nic_features(struct lif *lif, netdev_features_t features);
static int ionic_notifyq_clean(struct lif *lif, int budget);
+static void ionic_lif_deferred_work(struct work_struct *work)
+{
+ struct lif *lif = container_of(work, struct lif, deferred.work);
+ struct ionic_deferred *def = &lif->deferred;
+ struct ionic_deferred_work *w = NULL;
+
+ spin_lock_bh(&def->lock);
+ if (!list_empty(&def->list)) {
+ w = list_first_entry(&def->list,
+ struct ionic_deferred_work, list);
+ list_del(&w->list);
+ }
+ spin_unlock_bh(&def->lock);
+
+ if (w) {
+ switch (w->type) {
+ case DW_TYPE_RX_MODE:
+ ionic_lif_rx_mode(lif, w->rx_mode);
+ break;
+ case DW_TYPE_RX_ADDR_ADD:
+ ionic_lif_addr_add(lif, w->addr);
+ break;
+ case DW_TYPE_RX_ADDR_DEL:
+ ionic_lif_addr_del(lif, w->addr);
+ break;
+ default:
+ break;
+ }
+ kfree(w);
+ schedule_work(&def->work);
+ }
+}
+
+static void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
+ struct ionic_deferred_work *work)
+{
+ spin_lock_bh(&def->lock);
+ list_add_tail(&work->list, &def->list);
+ spin_unlock_bh(&def->lock);
+ schedule_work(&def->work);
+}
+
int ionic_open(struct net_device *netdev)
{
struct lif *lif = netdev_priv(netdev);
@@ -180,6 +226,235 @@ static int ionic_notifyq_clean(struct lif *lif, int budget)
return work_done;
}
+static int ionic_lif_addr_add(struct lif *lif, const u8 *addr)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_filter_add = {
+ .opcode = CMD_OPCODE_RX_FILTER_ADD,
+ .lif_index = cpu_to_le16(lif->index),
+ .match = cpu_to_le16(RX_FILTER_MATCH_MAC),
+ },
+ };
+ struct rx_filter *f;
+ int err;
+
+ /* don't bother if we already have it */
+ spin_lock_bh(&lif->rx_filters.lock);
+ f = ionic_rx_filter_by_addr(lif, addr);
+ spin_unlock_bh(&lif->rx_filters.lock);
+ if (f)
+ return 0;
+
+ netdev_dbg(lif->netdev, "rx_filter add ADDR %pM (id %d)\n", addr,
+ ctx.comp.rx_filter_add.filter_id);
+
+ memcpy(ctx.cmd.rx_filter_add.mac.addr, addr, ETH_ALEN);
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ return ionic_rx_filter_save(lif, 0, RXQ_INDEX_ANY, 0, &ctx);
+}
+
+static int ionic_lif_addr_del(struct lif *lif, const u8 *addr)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_filter_del = {
+ .opcode = CMD_OPCODE_RX_FILTER_DEL,
+ .lif_index = cpu_to_le16(lif->index),
+ },
+ };
+ struct rx_filter *f;
+ int err;
+
+ spin_lock_bh(&lif->rx_filters.lock);
+ f = ionic_rx_filter_by_addr(lif, addr);
+ if (!f) {
+ spin_unlock_bh(&lif->rx_filters.lock);
+ return -ENOENT;
+ }
+
+ ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
+ ionic_rx_filter_free(lif, f);
+ spin_unlock_bh(&lif->rx_filters.lock);
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ netdev_dbg(lif->netdev, "rx_filter del ADDR %pM (id %d)\n", addr,
+ ctx.cmd.rx_filter_del.filter_id);
+
+ return 0;
+}
+
+static int ionic_lif_addr(struct lif *lif, const u8 *addr, bool add)
+{
+ struct ionic *ionic = lif->ionic;
+ struct ionic_deferred_work *work;
+ unsigned int nmfilters;
+ unsigned int nufilters;
+
+ if (add) {
+ /* Do we have space for this filter? We test the counters
+ * here before checking the need for deferral so that we
+ * can return an overflow error to the stack.
+ */
+ nmfilters = le32_to_cpu(ionic->ident.lif.eth.max_mcast_filters);
+ nufilters = le32_to_cpu(ionic->ident.lif.eth.max_ucast_filters);
+
+ if ((is_multicast_ether_addr(addr) && lif->nmcast < nmfilters))
+ lif->nmcast++;
+ else if (!is_multicast_ether_addr(addr) &&
+ lif->nucast < nufilters)
+ lif->nucast++;
+ else
+ return -ENOSPC;
+ } else {
+ if (is_multicast_ether_addr(addr) && lif->nmcast)
+ lif->nmcast--;
+ else if (!is_multicast_ether_addr(addr) && lif->nucast)
+ lif->nucast--;
+ }
+
+ if (in_interrupt()) {
+ work = kzalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work) {
+ netdev_err(lif->netdev, "%s OOM\n", __func__);
+ return -ENOMEM;
+ }
+ work->type = add ? DW_TYPE_RX_ADDR_ADD : DW_TYPE_RX_ADDR_DEL;
+ memcpy(work->addr, addr, ETH_ALEN);
+ netdev_dbg(lif->netdev, "deferred: rx_filter %s %pM\n",
+ add ? "add" : "del", addr);
+ ionic_lif_deferred_enqueue(&lif->deferred, work);
+ } else {
+ netdev_dbg(lif->netdev, "rx_filter %s %pM\n",
+ add ? "add" : "del", addr);
+ if (add)
+ return ionic_lif_addr_add(lif, addr);
+ else
+ return ionic_lif_addr_del(lif, addr);
+ }
+
+ return 0;
+}
+
+static int ionic_addr_add(struct net_device *netdev, const u8 *addr)
+{
+ return ionic_lif_addr(netdev_priv(netdev), addr, true);
+}
+
+static int ionic_addr_del(struct net_device *netdev, const u8 *addr)
+{
+ return ionic_lif_addr(netdev_priv(netdev), addr, false);
+}
+
+static void ionic_lif_rx_mode(struct lif *lif, unsigned int rx_mode)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_mode_set = {
+ .opcode = CMD_OPCODE_RX_MODE_SET,
+ .lif_index = cpu_to_le16(lif->index),
+ .rx_mode = cpu_to_le16(rx_mode),
+ },
+ };
+ char buf[128];
+ int err;
+ int i;
+#define REMAIN(__x) (sizeof(buf) - (__x))
+
+ i = snprintf(buf, sizeof(buf), "rx_mode 0x%04x -> 0x%04x:",
+ lif->rx_mode, rx_mode);
+ if (rx_mode & RX_MODE_F_UNICAST)
+ i += snprintf(&buf[i], REMAIN(i), " RX_MODE_F_UNICAST");
+ if (rx_mode & RX_MODE_F_MULTICAST)
+ i += snprintf(&buf[i], REMAIN(i), " RX_MODE_F_MULTICAST");
+ if (rx_mode & RX_MODE_F_BROADCAST)
+ i += snprintf(&buf[i], REMAIN(i), " RX_MODE_F_BROADCAST");
+ if (rx_mode & RX_MODE_F_PROMISC)
+ i += snprintf(&buf[i], REMAIN(i), " RX_MODE_F_PROMISC");
+ if (rx_mode & RX_MODE_F_ALLMULTI)
+ i += snprintf(&buf[i], REMAIN(i), " RX_MODE_F_ALLMULTI");
+ netdev_dbg(lif->netdev, "lif%d %s\n", lif->index, buf);
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ netdev_warn(lif->netdev, "set rx_mode 0x%04x failed: %d\n",
+ rx_mode, err);
+ else
+ lif->rx_mode = rx_mode;
+}
+
+static void _ionic_lif_rx_mode(struct lif *lif, unsigned int rx_mode)
+{
+ struct ionic_deferred_work *work;
+
+ if (in_interrupt()) {
+ work = kzalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work) {
+ netdev_err(lif->netdev, "%s OOM\n", __func__);
+ return;
+ }
+ work->type = DW_TYPE_RX_MODE;
+ work->rx_mode = rx_mode;
+ netdev_dbg(lif->netdev, "deferred: rx_mode\n");
+ ionic_lif_deferred_enqueue(&lif->deferred, work);
+ } else {
+ ionic_lif_rx_mode(lif, rx_mode);
+ }
+}
+
+static void ionic_set_rx_mode(struct net_device *netdev)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct identity *ident = &lif->ionic->ident;
+ unsigned int nfilters;
+ unsigned int rx_mode;
+
+ rx_mode = RX_MODE_F_UNICAST;
+ rx_mode |= (netdev->flags & IFF_MULTICAST) ? RX_MODE_F_MULTICAST : 0;
+ rx_mode |= (netdev->flags & IFF_BROADCAST) ? RX_MODE_F_BROADCAST : 0;
+ rx_mode |= (netdev->flags & IFF_PROMISC) ? RX_MODE_F_PROMISC : 0;
+ rx_mode |= (netdev->flags & IFF_ALLMULTI) ? RX_MODE_F_ALLMULTI : 0;
+
+ /* sync unicast addresses
+ * next check to see if we're in an overflow state
+ * if so, we track that we overflowed and enable NIC PROMISC
+ * else if the overflow is set and not needed
+ * we remove our overflow flag and check the netdev flags
+ * to see if we can disable NIC PROMISC
+ */
+ __dev_uc_sync(netdev, ionic_addr_add, ionic_addr_del);
+ nfilters = le32_to_cpu(ident->lif.eth.max_ucast_filters);
+ if (netdev_uc_count(netdev) + 1 > nfilters) {
+ rx_mode |= RX_MODE_F_PROMISC;
+ lif->uc_overflow = true;
+ } else if (lif->uc_overflow) {
+ lif->uc_overflow = false;
+ if (!(netdev->flags & IFF_PROMISC))
+ rx_mode &= ~RX_MODE_F_PROMISC;
+ }
+
+ /* same for multicast */
+ __dev_mc_sync(netdev, ionic_addr_add, ionic_addr_del);
+ nfilters = le32_to_cpu(ident->lif.eth.max_mcast_filters);
+ if (netdev_mc_count(netdev) > nfilters) {
+ rx_mode |= RX_MODE_F_ALLMULTI;
+ lif->mc_overflow = true;
+ } else if (lif->mc_overflow) {
+ lif->mc_overflow = false;
+ if (!(netdev->flags & IFF_ALLMULTI))
+ rx_mode &= ~RX_MODE_F_ALLMULTI;
+ }
+
+ if (lif->rx_mode != rx_mode)
+ _ionic_lif_rx_mode(lif, rx_mode);
+}
+
static int ionic_set_features(struct net_device *netdev,
netdev_features_t features)
{
@@ -196,8 +471,26 @@ static int ionic_set_features(struct net_device *netdev,
static int ionic_set_mac_address(struct net_device *netdev, void *sa)
{
- netdev_info(netdev, "%s: stubbed\n", __func__);
- return 0;
+ struct sockaddr *addr = sa;
+ u8 *mac;
+
+ mac = (u8 *)addr->sa_data;
+ if (ether_addr_equal(netdev->dev_addr, mac))
+ return 0;
+
+ if (!is_valid_ether_addr(mac))
+ return -EADDRNOTAVAIL;
+
+ if (!is_zero_ether_addr(netdev->dev_addr)) {
+ netdev_info(netdev, "deleting mac addr %pM\n",
+ netdev->dev_addr);
+ ionic_addr_del(netdev, netdev->dev_addr);
+ }
+
+ memcpy(netdev->dev_addr, mac, netdev->addr_len);
+ netdev_info(netdev, "updating mac addr %pM\n", mac);
+
+ return ionic_addr_add(netdev, mac);
}
static int ionic_change_mtu(struct net_device *netdev, int new_mtu)
@@ -232,20 +525,63 @@ static void ionic_tx_timeout(struct net_device *netdev)
static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
u16 vid)
{
- netdev_info(netdev, "%s: stubbed\n", __func__);
- return 0;
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_filter_add = {
+ .opcode = CMD_OPCODE_RX_FILTER_ADD,
+ .lif_index = cpu_to_le16(lif->index),
+ .match = cpu_to_le16(RX_FILTER_MATCH_VLAN),
+ .vlan.vlan = cpu_to_le16(vid),
+ },
+ };
+ int err;
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ netdev_dbg(netdev, "rx_filter add VLAN %d (id %d)\n", vid,
+ ctx.comp.rx_filter_add.filter_id);
+
+ return ionic_rx_filter_save(lif, 0, RXQ_INDEX_ANY, 0, &ctx);
}
static int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto,
u16 vid)
{
- netdev_info(netdev, "%s: stubbed\n", __func__);
- return 0;
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_filter_del = {
+ .opcode = CMD_OPCODE_RX_FILTER_DEL,
+ .lif_index = cpu_to_le16(lif->index),
+ },
+ };
+ struct rx_filter *f;
+
+ spin_lock_bh(&lif->rx_filters.lock);
+
+ f = ionic_rx_filter_by_vlan(lif, vid);
+ if (!f) {
+ spin_unlock_bh(&lif->rx_filters.lock);
+ return -ENOENT;
+ }
+
+ netdev_dbg(netdev, "rx_filter del VLAN %d (id %d)\n", vid,
+ le32_to_cpu(ctx.cmd.rx_filter_del.filter_id));
+
+ ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
+ ionic_rx_filter_free(lif, f);
+ spin_unlock_bh(&lif->rx_filters.lock);
+
+ return ionic_adminq_post_wait(lif, &ctx);
}
static const struct net_device_ops ionic_netdev_ops = {
.ndo_open = ionic_open,
.ndo_stop = ionic_stop,
+ .ndo_set_rx_mode = ionic_set_rx_mode,
.ndo_set_features = ionic_set_features,
.ndo_set_mac_address = ionic_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
@@ -546,6 +882,10 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
spin_lock_init(&lif->adminq_lock);
+ spin_lock_init(&lif->deferred.lock);
+ INIT_LIST_HEAD(&lif->deferred.list);
+ INIT_WORK(&lif->deferred.work, ionic_lif_deferred_work);
+
/* allocate lif info */
lif->info_sz = ALIGN(sizeof(*lif->info), PAGE_SIZE);
lif->info = dma_alloc_coherent(dev, lif->info_sz,
@@ -607,6 +947,8 @@ static void ionic_lif_free(struct lif *lif)
ionic_qcqs_free(lif);
ionic_lif_reset(lif);
+ cancel_work_sync(&lif->deferred.work);
+
/* free lif info */
dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa);
lif->info = NULL;
@@ -975,6 +1317,37 @@ static int ionic_init_nic_features(struct lif *lif)
return 0;
}
+static int ionic_station_set(struct lif *lif)
+{
+ struct net_device *netdev = lif->netdev;
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.lif_getattr = {
+ .opcode = CMD_OPCODE_LIF_GETATTR,
+ .index = cpu_to_le16(lif->index),
+ .attr = IONIC_LIF_ATTR_MAC,
+ },
+ };
+ int err;
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ if (!is_zero_ether_addr(netdev->dev_addr)) {
+ netdev_dbg(lif->netdev, "deleting station MAC addr %pM\n",
+ netdev->dev_addr);
+ ionic_lif_addr(lif, netdev->dev_addr, false);
+ }
+ memcpy(netdev->dev_addr, ctx.comp.lif_getattr.mac,
+ netdev->addr_len);
+ netdev_dbg(lif->netdev, "adding station MAC addr %pM\n",
+ netdev->dev_addr);
+ ionic_lif_addr(lif, netdev->dev_addr, true);
+
+ return 0;
+}
+
static int ionic_lif_init(struct lif *lif)
{
struct ionic_dev *idev = &lif->ionic->idev;
@@ -1039,6 +1412,10 @@ static int ionic_lif_init(struct lif *lif)
if (err)
goto err_out_notifyq_deinit;
+ err = ionic_station_set(lif);
+ if (err)
+ goto err_out_notifyq_deinit;
+
set_bit(LIF_INITED, lif->state);
return 0;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 9f112aa69033..20b4fa573f77 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -60,6 +60,29 @@ struct qcq {
#define napi_to_qcq(napi) container_of(napi, struct qcq, napi)
#define napi_to_cq(napi) (&napi_to_qcq(napi)->cq)
+enum ionic_deferred_work_type {
+ DW_TYPE_RX_MODE,
+ DW_TYPE_RX_ADDR_ADD,
+ DW_TYPE_RX_ADDR_DEL,
+ DW_TYPE_LINK_STATUS,
+ DW_TYPE_LIF_RESET,
+};
+
+struct ionic_deferred_work {
+ struct list_head list;
+ enum ionic_deferred_work_type type;
+ union {
+ unsigned int rx_mode;
+ u8 addr[ETH_ALEN];
+ };
+};
+
+struct ionic_deferred {
+ spinlock_t lock; /* lock for deferred work list */
+ struct list_head list;
+ struct work_struct work;
+};
+
enum lif_state_flags {
LIF_INITED,
LIF_UP,
@@ -87,13 +110,19 @@ struct lif {
u64 last_eid;
unsigned int neqs;
unsigned int nxqs;
+ unsigned int rx_mode;
u64 hw_features;
+ bool mc_overflow;
+ unsigned int nmcast;
+ bool uc_overflow;
+ unsigned int nucast;
struct lif_info *info;
dma_addr_t info_pa;
u32 info_sz;
struct rx_filters rx_filters;
+ struct ionic_deferred deferred;
unsigned long *dbid_inuse;
unsigned int dbid_count;
struct dentry *dentry;
--
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