* Re: [Bug 63871] New: BUG skbuff_head_cache (Tainted: G W ): Object padding overwritten
From: Hannes Frederic Sowa @ 2013-11-01 23:08 UTC (permalink / raw)
To: Andrew Morton; +Cc: netdev, bugzilla-daemon, mikhail.v.gavrilov
In-Reply-To: <20131101155620.b4388760cfb8562430e3039c@linux-foundation.org>
On Fri, Nov 01, 2013 at 03:56:20PM -0700, Andrew Morton wrote:
>
> (switched to email. Please respond via emailed reply-to-all, not via the
> bugzilla web interface).
>
> Possible networking memory scribble?
>
> On Sun, 27 Oct 2013 16:01:39 +0000 bugzilla-daemon@bugzilla.kernel.org wrote:
>
> > [12745.265370] [<ffffffff815df9fe>] __alloc_skb+0x4e/0x2b0
> > [12745.265372] [<ffffffff815d8de4>] sock_wmalloc+0x34/0x90
> > [12745.265375] [<ffffffff8163a803>] __ip_append_data.isra.44+0x7c3/0x9c0
> > [12745.265377] [<ffffffff816385a0>] ? ip_reply_glue_bits+0x60/0x60
> > [12745.265378] [<ffffffff816384aa>] ? ip_setup_cork+0x7a/0x110
> > [12745.265380] [<ffffffff8163c4f3>] ip_make_skb+0x113/0x160
> > [12745.265382] [<ffffffff816385a0>] ? ip_reply_glue_bits+0x60/0x60
> > [12745.265384] [<ffffffff8162e400>] ? __ip_route_output_key+0x360/0xb20
> > [12745.265386] [<ffffffff81669faa>] udp_sendmsg+0x2ba/0xb70
Maybe this is the fix for this:
http://patchwork.ozlabs.org/patch/285292/
Greetings,
Hannes
^ permalink raw reply
* Re: [Bug 63871] New: BUG skbuff_head_cache (Tainted: G W ): Object padding overwritten
From: Andrew Morton @ 2013-11-01 22:56 UTC (permalink / raw)
To: netdev; +Cc: bugzilla-daemon, mikhail.v.gavrilov
In-Reply-To: <bug-63871-27@https.bugzilla.kernel.org/>
(switched to email. Please respond via emailed reply-to-all, not via the
bugzilla web interface).
Possible networking memory scribble?
On Sun, 27 Oct 2013 16:01:39 +0000 bugzilla-daemon@bugzilla.kernel.org wrote:
> https://bugzilla.kernel.org/show_bug.cgi?id=63871
>
> Bug ID: 63871
> Summary: BUG skbuff_head_cache (Tainted: G W ): Object
> padding overwritten
> Product: Memory Management
> Version: 2.5
> Kernel Version: 3.11.6
> Hardware: All
> OS: Linux
> Tree: Mainline
> Status: NEW
> Severity: normal
> Priority: P1
> Component: Slab Allocator
> Assignee: akpm@linux-foundation.org
> Reporter: mikhail.v.gavrilov@gmail.com
> Regression: No
>
> Created attachment 112441
> --> https://bugzilla.kernel.org/attachment.cgi?id=112441&action=edit
> dmesg output
>
> [12745.265250]
> =============================================================================
> [12745.265254] BUG skbuff_head_cache (Tainted: G W ): Object padding
> overwritten
> [12745.265254]
> -----------------------------------------------------------------------------
>
> [12745.265256] INFO: 0xffff88080c1ef5fe-0xffff88080c1ef5fe. First byte 0x7a
> instead of 0x5a
> [12745.265261] INFO: Allocated in __alloc_skb+0x4e/0x2b0 age=87 cpu=0 pid=10621
> [12745.265265] __slab_alloc+0x45f/0x526
> [12745.265267] kmem_cache_alloc_node+0xd8/0x3d0
> [12745.265268] __alloc_skb+0x4e/0x2b0
> [12745.265270] sock_alloc_send_pskb+0x27e/0x400
> [12745.265271] sock_alloc_send_skb+0x15/0x20
> [12745.265274] __ip_append_data.isra.44+0x5a2/0x9c0
> [12745.265275] ip_make_skb+0x113/0x160
> [12745.265278] udp_sendmsg+0x2ba/0xb70
> [12745.265279] inet_sendmsg+0x117/0x230
> [12745.265280] sock_sendmsg+0x99/0xd0
> [12745.265281] SYSC_sendto+0x124/0x1d0
> [12745.265282] SyS_sendto+0xe/0x10
> [12745.265286] system_call_fastpath+0x16/0x1b
> [12745.265287] INFO: Freed in kfree_skbmem+0x37/0x90 age=87 cpu=0 pid=10621
> [12745.265289] __slab_free+0x3a/0x382
> [12745.265290] kmem_cache_free+0x37a/0x390
> [12745.265291] kfree_skbmem+0x37/0x90
> [12745.265293] consume_skb+0x38/0x150
> [12745.265297] rtl8169_poll+0x508/0x708 [r8169]
> [12745.265298] net_rx_action+0x172/0x380
> [12745.265300] __do_softirq+0x107/0x410
> [12745.265302] call_softirq+0x1c/0x30
> [12745.265304] do_softirq+0x85/0xc0
> [12745.265305] local_bh_enable+0xdb/0xf0
> [12745.265307] ip_finish_output2+0x22d/0x540
> [12745.265308] ip_fragment+0x7a3/0x9a0
> [12745.265310] ip_finish_output+0x54f/0x800
> [12745.265311] ip_output+0x68/0x110
> [12745.265312] ip_local_out+0x29/0x90
> [12745.265313] ip_send_skb+0x15/0x50
> [12745.265314] INFO: Slab 0xffffea0020307b00 objects=28 used=28 fp=0x
> (null) flags=0x5ff00000004080
> [12745.265315] INFO: Object 0xffff88080c1ef3c0 @offset=13248
> fp=0xffff88080c1ec240
>
> [12745.265317] Bytes b4 ffff88080c1ef3b0: 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a
> 5a 5a 5a 5a ZZZZZZZZZZZZZZZZ
> [12745.265318] Object ffff88080c1ef3c0: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265318] Object ffff88080c1ef3d0: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265319] Object ffff88080c1ef3e0: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265320] Object ffff88080c1ef3f0: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265321] Object ffff88080c1ef400: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265322] Object ffff88080c1ef410: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265322] Object ffff88080c1ef420: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265323] Object ffff88080c1ef430: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265324] Object ffff88080c1ef440: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265325] Object ffff88080c1ef450: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265326] Object ffff88080c1ef460: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265326] Object ffff88080c1ef470: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265327] Object ffff88080c1ef480: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265328] Object ffff88080c1ef490: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b 6b kkkkkkkkkkkkkkkk
> [12745.265329] Object ffff88080c1ef4a0: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
> 6b 6b a5 kkkkkkkkkkkkkkk.
> [12745.265330] Redzone ffff88080c1ef4b0: bb bb bb bb bb bb bb bb
> ........
> [12745.265331] Padding ffff88080c1ef5f0: 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a
> 5a 7a 5a ZZZZZZZZZZZZZZzZ
> [12745.265332] CPU: 0 PID: 10621 Comm: transmission-gt Tainted: G B W
> 3.11.6-301.fc20.x86_64+debug #1
> [12745.265333] Hardware name: Gigabyte Technology Co., Ltd. Z87M-D3H/Z87M-D3H,
> BIOS F8 08/03/2013
> [12745.265334] ffff88080c1ef3c0 ffff880298869760 ffffffff817289cc
> ffff880813901200
> [12745.265337] ffff8802988697a0 ffffffff811cd4ed 0000000000000010
> ffff880800000001
> [12745.265339] ffff88080c1ef5ff ffff880813901200 000000000000005a
> ffff88080c1ef3c0
> [12745.265342] Call Trace:
> [12745.265344] [<ffffffff817289cc>] dump_stack+0x54/0x74
> [12745.265348] [<ffffffff811cd4ed>] print_trailer+0x14d/0x200
> [12745.265350] [<ffffffff811cd6df>] check_bytes_and_report+0xcf/0x110
> [12745.265353] [<ffffffff811ce628>] check_object+0xa8/0x250
> [12745.265355] [<ffffffff815df9fe>] ? __alloc_skb+0x4e/0x2b0
> [12745.265358] [<ffffffff81726165>] alloc_debug_processing+0x76/0x118
> [12745.265360] [<ffffffff81726e3a>] __slab_alloc+0x45f/0x526
> [12745.265361] [<ffffffff811d462d>] ? __kmalloc_node_track_caller+0x1dd/0x420
> [12745.265363] [<ffffffff815df9fe>] ? __alloc_skb+0x4e/0x2b0
> [12745.265365] [<ffffffff815df241>] ? __kmalloc_reserve.isra.25+0x31/0x90
> [12745.265367] [<ffffffff815df9fe>] ? __alloc_skb+0x4e/0x2b0
> [12745.265368] [<ffffffff811d0d98>] kmem_cache_alloc_node+0xd8/0x3d0
> [12745.265370] [<ffffffff815df9fe>] __alloc_skb+0x4e/0x2b0
> [12745.265372] [<ffffffff815d8de4>] sock_wmalloc+0x34/0x90
> [12745.265375] [<ffffffff8163a803>] __ip_append_data.isra.44+0x7c3/0x9c0
> [12745.265377] [<ffffffff816385a0>] ? ip_reply_glue_bits+0x60/0x60
> [12745.265378] [<ffffffff816384aa>] ? ip_setup_cork+0x7a/0x110
> [12745.265380] [<ffffffff8163c4f3>] ip_make_skb+0x113/0x160
> [12745.265382] [<ffffffff816385a0>] ? ip_reply_glue_bits+0x60/0x60
> [12745.265384] [<ffffffff8162e400>] ? __ip_route_output_key+0x360/0xb20
> [12745.265386] [<ffffffff81669faa>] udp_sendmsg+0x2ba/0xb70
> [12745.265388] [<ffffffff816385a0>] ? ip_reply_glue_bits+0x60/0x60
> [12745.265390] [<ffffffff81676c75>] ? inet_sendmsg+0x5/0x230
> [12745.265391] [<ffffffff81676d87>] inet_sendmsg+0x117/0x230
> [12745.265392] [<ffffffff81676c75>] ? inet_sendmsg+0x5/0x230
> [12745.265393] [<ffffffff815d52d9>] sock_sendmsg+0x99/0xd0
> [12745.265395] [<ffffffff81668e09>] ? udp_poll+0xe9/0x230
> [12745.265397] [<ffffffff81668d25>] ? udp_poll+0x5/0x230
> [12745.265398] [<ffffffff815d5834>] SYSC_sendto+0x124/0x1d0
> [12745.265402] [<ffffffff812111e9>] ? fget_light+0xf9/0x510
> [12745.265405] [<ffffffff8137a8ce>] ? trace_hardirqs_on_thunk+0x3a/0x3f
> [12745.265406] [<ffffffff815d699e>] SyS_sendto+0xe/0x10
> [12745.265409] [<ffffffff8173bcd9>] system_call_fastpath+0x16/0x1b
> [12745.265410] FIX skbuff_head_cache: Restoring
> 0xffff88080c1ef5fe-0xffff88080c1ef5fe=0x5a
>
> --
> You are receiving this mail because:
> You are the assignee for the bug.
^ permalink raw reply
* [PATCH v3] can: add Renesas R-Car CAN driver
From: Sergei Shtylyov @ 2013-11-01 23:40 UTC (permalink / raw)
To: netdev, wg, mkl, linux-can; +Cc: linux-sh, vksavl
Add support for the CAN controller found in Renesas R-Car SoCs.
Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
---
The patch is against the 'linux-can-next.git' repo.
Changes in version 3:
- replaced the register #define's with 'struct rcar_can_regs' fields, replaced
rcar_can_{read|write}[bwl]() with mere {read|write}[bwl]();
- removed hardware bus-off recovery support which allowed to also remove
rcar_can_start() prototype;
- added RX/TX error count to error data frame for error warning/passive;
- moved TX completion interrupt handling into separate function;
- removed unneeded type cast in the probe() method.
Changes in version 2:
- added function to clean up TX mailboxes after bus error and bus-off;
- added module parameter to enable hardware recovery from bus-off, added handler
for the bus-off recovery interrupt, and set the control register according to
the parameter value and the restart timer setting in rcar_can_start();
- changed the way CAN_ERR_CRTL_[RT]X_{PASSIVE|WARNING} flags are set to a more
realicstic one;
- replaced MBX_* macros and rcar_can_mbx_{read|write}[bl]() functions with
'struct rcar_can_mbox_regs', 'struct rcar_can_regs', and {read|write}[bl](),
replaced 'reg_base' field of 'struct rcar_can_priv' with 'struct rcar_can_regs
__iomem *regs';
- added 'ier' field to 'struct rcar_can_priv' to cache the current value of the
interrupt enable register;
- added a check for enabled interrupts on entry to rcar_can_interrupt();
- limited transmit mailbox search loop in rcar_can_interrupt();
- decoupled TX byte count increment from can_get_echo_skb() call;
- removed netif_queue_stopped() call from rcar_can_interrupt();
- added clk_prepare_enable()/clk_disable_unprepare() to ndo_{open|close}(),
do_set_bittiming(), and do_get_berr_counter() methods, removed clk_enable()
call from the probe() method and clk_disable() call from the remove() method;
- allowed rcar_can_set_bittiming() to be called when the device is closed and
remove explicit call to it from rcar_can_start();
- switched to using mailbox number priority transmit mode, and switched to the
sequential mailbox use in ndo_start_xmit() method;
- stopped reading the message control registers in ndo_start_xmit() method;
- avoided returning NETDEV_TX_BUSY from ndo_start_xmit() method;
- stopped reading data when RTR bit is set in the CAN frame;
- made 'num_pkts' variable *int* and moved its check to the *while* condition in
rcar_can_rx_poll();
- used dev_get_platdata() in the probe() method;
- enabled bus error interrupt only if CAN_CTRLMODE_BERR_REPORTING flag is set;
- started reporting CAN_CTRLMODE_BERR_REPORTING support and stopped reporting
CAN_CTRLMODE_3_SAMPLES support;
- set CAN_ERR_ACK flag on ACK error;
- switched to incrementing bus error counter only once per bus error interrupt;
- started switching to CAN sleep mode in rcar_can_stop() and stopped switching
to it in the remove() method;
- removed netdev_err() calls on allocation failure in rcar_can_error() and
rcar_can_rx_pkt();
- removed "CANi" from the register offset macro comments.
drivers/net/can/Kconfig | 9
drivers/net/can/Makefile | 1
drivers/net/can/rcar_can.c | 893 ++++++++++++++++++++++++++++++++++
include/linux/can/platform/rcar_can.h | 15
4 files changed, 918 insertions(+)
Index: linux-can-next/drivers/net/can/Kconfig
===================================================================
--- linux-can-next.orig/drivers/net/can/Kconfig
+++ linux-can-next/drivers/net/can/Kconfig
@@ -125,6 +125,15 @@ config CAN_GRCAN
endian syntheses of the cores would need some modifications on
the hardware level to work.
+config CAN_RCAR
+ tristate "Renesas R-Car CAN controller"
+ ---help---
+ Say Y here if you want to use CAN controller found on Renesas R-Car
+ SoCs.
+
+ To compile this driver as a module, choose M here: the module will
+ be called rcar_can.
+
source "drivers/net/can/mscan/Kconfig"
source "drivers/net/can/sja1000/Kconfig"
Index: linux-can-next/drivers/net/can/Makefile
===================================================================
--- linux-can-next.orig/drivers/net/can/Makefile
+++ linux-can-next/drivers/net/can/Makefile
@@ -25,5 +25,6 @@ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ica
obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
obj-$(CONFIG_PCH_CAN) += pch_can.o
obj-$(CONFIG_CAN_GRCAN) += grcan.o
+obj-$(CONFIG_CAN_RCAR) += rcar_can.o
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
Index: linux-can-next/drivers/net/can/rcar_can.c
===================================================================
--- /dev/null
+++ linux-can-next/drivers/net/can/rcar_can.c
@@ -0,0 +1,893 @@
+/*
+ * Renesas R-Car CAN device driver
+ *
+ * Copyright (C) 2013 Cogent Embedded, Inc. <source@cogentembedded.com>
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/can/led.h>
+#include <linux/can/dev.h>
+#include <linux/clk.h>
+#include <linux/can/platform/rcar_can.h>
+
+#define DRV_NAME "rcar_can"
+
+/* Mailbox configuration:
+ * mailbox 0 - not used
+ * mailbox 1-31 - Rx
+ * mailbox 32-63 - Tx
+ * no FIFO mailboxes
+ */
+#define N_MBX 64
+#define FIRST_TX_MB 32
+#define N_TX_MB (N_MBX - FIRST_TX_MB)
+#define RX_MBX_MASK 0xFFFFFFFE
+
+/* Mailbox registers structure */
+struct rcar_can_mbox_regs {
+ u32 id; /* IDE and RTR bits, SID and EID */
+ u8 stub; /* Not used */
+ u8 dlc; /* Data Length Code - bits [0..3] */
+ u8 data[8]; /* Data Bytes */
+ u8 tsh; /* Time Stamp Higher Byte */
+ u8 tsl; /* Time Stamp Lower Byte */
+} __packed;
+
+struct rcar_can_regs {
+ struct rcar_can_mbox_regs mb[N_MBX]; /* Mailbox registers */
+ u32 mkr_2_9[8]; /* Mask Registers 2-9 */
+ u32 fidcr[2]; /* FIFO Received ID Compare Register */
+ u32 mkivlr1; /* Mask Invalid Register 1 */
+ u32 mier1; /* Mailbox Interrupt Enable Register 1 */
+ u32 mkr_0_1[2]; /* Mask Registers 0-1 */
+ u32 mkivlr0; /* Mask Invalid Register 0*/
+ u32 mier0; /* Mailbox Interrupt Enable Register 0 */
+ u8 pad_440[0x3c0];
+ u8 mctl[64]; /* Message Control Registers */
+ u16 ctlr; /* Control Register */
+ u16 str; /* Status register */
+ u8 bcr[3]; /* Bit Configuration Register */
+ u8 clkr; /* Clock Select Register */
+ u8 rfcr; /* Receive FIFO Control Register */
+ u8 rfpcr; /* Receive FIFO Pointer Control Register */
+ u8 tfcr; /* Transmit FIFO Control Register */
+ u8 tfpcr; /* Transmit FIFO Pointer Control Register */
+ u8 eier; /* Error Interrupt Enable Register */
+ u8 eifr; /* Error Interrupt Factor Judge Register */
+ u8 recr; /* Receive Error Count Register */
+ u8 tecr; /* Transmit Error Count Register */
+ u8 ecsr; /* Error Code Store Register */
+ u8 cssr; /* Channel Search Support Register */
+ u8 mssr; /* Mailbox Search Status Register */
+ u8 msmr; /* Mailbox Search Mode Register */
+ u16 tsr; /* Time Stamp Register */
+ u8 afsr; /* Acceptance Filter Support Register */
+ u8 pad_857;
+ u8 tcr; /* Test Control Register */
+ u8 pad_859[7];
+ u8 ier; /* Interrupt Enable Register */
+ u8 isr; /* Interrupt Status Register */
+ u8 pad_862;
+ u8 mbsmr; /* Mailbox Search Mask Register */
+} __packed;
+
+struct rcar_can_priv {
+ struct can_priv can; /* Must be the first member! */
+ struct net_device *ndev;
+ struct napi_struct napi;
+ struct rcar_can_regs __iomem *regs;
+ struct clk *clk;
+ spinlock_t mier_lock;
+ u8 clock_select;
+ u8 ier;
+};
+
+static const struct can_bittiming_const rcar_can_bittiming_const = {
+ .name = DRV_NAME,
+ .tseg1_min = 4,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 1024,
+ .brp_inc = 1,
+};
+
+/* Control Register bits */
+#define CTLR_BOM (3 << 11) /* Bus-Off Recovery Mode Bits */
+#define CTLR_BOM_ENT BIT(11) /* Entry to halt mode at bus-off entry */
+#define CTLR_SLPM BIT(10)
+#define CTLR_HALT BIT(9)
+#define CTLR_RESET BIT(8)
+#define CTLR_FORCE_RESET (3 << 8)
+#define CTLR_TPM BIT(4) /* Transmission Priority Mode Select Bit */
+#define CTLR_IDFM_MIXED BIT(2) /* Mixed ID mode */
+
+/* Message Control Register bits */
+#define MCTL_TRMREQ BIT(7)
+#define MCTL_RECREQ BIT(6)
+#define MCTL_ONESHOT BIT(4)
+#define MCTL_SENTDATA BIT(0)
+#define MCTL_NEWDATA BIT(0)
+
+#define N_RX_MKREGS 2 /* Number of mask registers */
+ /* for Rx mailboxes 0-31 */
+
+/* Bit Configuration Register settings */
+#define BCR_TSEG1(x) (((x) & 0x0f) << 28)
+#define BCR_BPR(x) (((x) & 0x3ff) << 16)
+#define BCR_SJW(x) (((x) & 0x3) << 12)
+#define BCR_TSEG2(x) (((x) & 0x07) << 8)
+
+/* Mailbox and Mask Registers bits */
+#define RCAR_CAN_IDE BIT(31)
+#define RCAR_CAN_RTR BIT(30)
+#define RCAR_CAN_SID_SHIFT 18
+
+/* Interrupt Enable Register bits */
+#define IER_ERSIE BIT(5) /* Error (ERS) Interrupt Enable Bit */
+#define IER_RXM0IE BIT(2) /* Mailbox 0 Successful Reception (RXM0) */
+ /* Interrupt Enable Bit */
+#define IER_RXM1IE BIT(1) /* Mailbox 1 Successful Reception (RXM0) */
+ /* Interrupt Enable Bit */
+#define IER_TXMIE BIT(0) /* Mailbox 32 to 63 Successful Tx */
+ /* Interrupt Enable Bit */
+
+/* Interrupt Status Register bits */
+#define ISR_ERSF BIT(5) /* Error (ERS) Interrupt Status Bit */
+#define ISR_RXM0F BIT(2) /* Mailbox 0 Successful Reception (RXM0) */
+ /* Interrupt Status Bit */
+#define ISR_RXM1F BIT(1) /* Mailbox 1 to 63 Successful Reception */
+ /* (RXM1) Interrupt Status Bit */
+#define ISR_TXMF BIT(0) /* Mailbox 32 to 63 Successful Transmission */
+ /* (TXM) Interrupt Status Bit */
+
+/* Error Interrupt Enable Register bits */
+#define EIER_BLIE BIT(7) /* Bus Lock Interrupt Enable */
+#define EIER_OLIE BIT(6) /* Overload Frame Transmit Interrupt Enable */
+#define EIER_ORIE BIT(5) /* Receive Overrun Interrupt Enable */
+#define EIER_BORIE BIT(4) /* Bus-Off Recovery Interrupt Enable */
+
+#define EIER_BOEIE BIT(3) /* Bus-Off Entry Interrupt Enable */
+#define EIER_EPIE BIT(2) /* Error Passive Interrupt Enable */
+#define EIER_EWIE BIT(1) /* Error Warning Interrupt Enable */
+#define EIER_BEIE BIT(0) /* Bus Error Interrupt Enable */
+
+/* Error Interrupt Factor Judge Register bits */
+#define EIFR_BLIF BIT(7) /* Bus Lock Detect Flag */
+#define EIFR_OLIF BIT(6) /* Overload Frame Transmission Detect Flag */
+#define EIFR_ORIF BIT(5) /* Receive Overrun Detect Flag */
+#define EIFR_BORIF BIT(4) /* Bus-Off Recovery Detect Flag */
+#define EIFR_BOEIF BIT(3) /* Bus-Off Entry Detect Flag */
+#define EIFR_EPIF BIT(2) /* Error Passive Detect Flag */
+#define EIFR_EWIF BIT(1) /* Error Warning Detect Flag */
+#define EIFR_BEIF BIT(0) /* Bus Error Detect Flag */
+
+/* Error Code Store Register bits */
+#define ECSR_EDPM BIT(7) /* Error Display Mode Select Bit */
+#define ECSR_ADEF BIT(6) /* ACK Delimiter Error Flag */
+#define ECSR_BE0F BIT(5) /* Bit Error (dominant) Flag */
+#define ECSR_BE1F BIT(4) /* Bit Error (recessive) Flag */
+#define ECSR_CEF BIT(3) /* CRC Error Flag */
+#define ECSR_AEF BIT(2) /* ACK Error Flag */
+#define ECSR_FEF BIT(1) /* Form Error Flag */
+#define ECSR_SEF BIT(0) /* Stuff Error Flag */
+
+/* Mailbox Search Status Register bits */
+#define MSSR_SEST BIT(7) /* Search Result Status Bit */
+#define MSSR_MBNST 0x3f /* Search Result Mailbox Number Status mask */
+
+/* Mailbox Search Mode Register values */
+#define MSMR_TXMB 1 /* Transmit mailbox search mode */
+#define MSMR_RXMB 0 /* Receive mailbox search mode */
+
+#define RCAR_CAN_NAPI_WEIGHT (FIRST_TX_MB - 1)
+
+static void tx_failure_cleanup(struct net_device *ndev)
+{
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+ u32 mier1;
+ u8 mbx;
+
+ spin_lock(&priv->mier_lock);
+ mier1 = readl(&priv->regs->mier1);
+ for (mbx = FIRST_TX_MB; mbx < N_MBX; mbx++) {
+ if (mier1 & BIT(mbx - FIRST_TX_MB)) {
+ writeb(0, &priv->regs->mctl[mbx]);
+ can_free_echo_skb(ndev, mbx - FIRST_TX_MB);
+ }
+ }
+ writel(0, &priv->regs->mier1);
+ spin_unlock(&priv->mier_lock);
+}
+
+static void rcar_can_error(struct net_device *ndev)
+{
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ u8 eifr, txerr = 0, rxerr = 0;
+
+ /* Propagate the error condition to the CAN stack */
+ skb = alloc_can_err_skb(ndev, &cf);
+ if (!skb)
+ return;
+
+ eifr = readb(&priv->regs->eifr);
+ if (eifr & (EIFR_EWIF | EIFR_EPIF)) {
+ cf->can_id |= CAN_ERR_CRTL;
+ txerr = readb(&priv->regs->tecr);
+ rxerr = readb(&priv->regs->recr);
+ cf->data[6] = txerr;
+ cf->data[7] = rxerr;
+ }
+ if (eifr & EIFR_BEIF) {
+ int rx_errors = 0, tx_errors = 0;
+ u8 ecsr;
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ tx_failure_cleanup(ndev);
+ netdev_dbg(priv->ndev, "Bus error interrupt:\n");
+ cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_UNSPEC;
+
+ ecsr = readb(&priv->regs->ecsr);
+ if (ecsr & ECSR_ADEF) {
+ netdev_dbg(priv->ndev, "ACK Delimiter Error\n");
+ cf->data[3] |= CAN_ERR_PROT_LOC_ACK_DEL;
+ tx_errors++;
+ writeb((u8)~ECSR_ADEF, &priv->regs->ecsr);
+ }
+ if (ecsr & ECSR_BE0F) {
+ netdev_dbg(priv->ndev, "Bit Error (dominant)\n");
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ tx_errors++;
+ writeb((u8)~ECSR_BE0F, &priv->regs->ecsr);
+ }
+ if (ecsr & ECSR_BE1F) {
+ netdev_dbg(priv->ndev, "Bit Error (recessive)\n");
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ tx_errors++;
+ writeb((u8)~ECSR_BE1F, &priv->regs->ecsr);
+ }
+ if (ecsr & ECSR_CEF) {
+ netdev_dbg(priv->ndev, "CRC Error\n");
+ cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+ rx_errors++;
+ writeb((u8)~ECSR_CEF, &priv->regs->ecsr);
+ }
+ if (ecsr & ECSR_AEF) {
+ netdev_dbg(priv->ndev, "ACK Error\n");
+ cf->can_id |= CAN_ERR_ACK;
+ cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
+ tx_errors++;
+ writeb((u8)~ECSR_AEF, &priv->regs->ecsr);
+ }
+ if (ecsr & ECSR_FEF) {
+ netdev_dbg(priv->ndev, "Form Error\n");
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ rx_errors++;
+ writeb((u8)~ECSR_FEF, &priv->regs->ecsr);
+ }
+ if (ecsr & ECSR_SEF) {
+ netdev_dbg(priv->ndev, "Stuff Error\n");
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ rx_errors++;
+ writeb((u8)~ECSR_SEF, &priv->regs->ecsr);
+ }
+
+ priv->can.can_stats.bus_error++;
+ ndev->stats.rx_errors += rx_errors;
+ ndev->stats.tx_errors += tx_errors;
+ writeb((u8)~EIFR_BEIF, &priv->regs->eifr);
+ }
+ if (eifr & EIFR_EWIF) {
+ netdev_dbg(priv->ndev, "Error warning interrupt\n");
+ priv->can.state = CAN_STATE_ERROR_WARNING;
+ priv->can.can_stats.error_warning++;
+ cf->data[1] |= txerr > rxerr ? CAN_ERR_CRTL_TX_WARNING :
+ CAN_ERR_CRTL_RX_WARNING;
+ /* Clear interrupt condition */
+ writeb((u8)~EIFR_EWIF, &priv->regs->eifr);
+ }
+ if (eifr & EIFR_EPIF) {
+ netdev_dbg(priv->ndev, "Error passive interrupt\n");
+ priv->can.state = CAN_STATE_ERROR_PASSIVE;
+ priv->can.can_stats.error_passive++;
+ cf->data[1] |= txerr > rxerr ? CAN_ERR_CRTL_TX_PASSIVE :
+ CAN_ERR_CRTL_RX_PASSIVE;
+ /* Clear interrupt condition */
+ writeb((u8)~EIFR_EPIF, &priv->regs->eifr);
+ }
+ if (eifr & EIFR_BOEIF) {
+ netdev_dbg(priv->ndev, "Bus-off entry interrupt\n");
+ tx_failure_cleanup(ndev);
+ priv->ier = IER_ERSIE;
+ writeb(priv->ier, &priv->regs->ier);
+ priv->can.state = CAN_STATE_BUS_OFF;
+ cf->can_id |= CAN_ERR_BUSOFF;
+ /* Clear interrupt condition */
+ writeb((u8)~EIFR_BOEIF, &priv->regs->eifr);
+ can_bus_off(ndev);
+ }
+ if (eifr & EIFR_ORIF) {
+ netdev_dbg(priv->ndev, "Receive overrun error interrupt\n");
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+ ndev->stats.rx_over_errors++;
+ ndev->stats.rx_errors++;
+ writeb((u8)~EIFR_ORIF, &priv->regs->eifr);
+ }
+ if (eifr & EIFR_OLIF) {
+ netdev_dbg(priv->ndev,
+ "Overload Frame Transmission error interrupt\n");
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] |= CAN_ERR_PROT_OVERLOAD;
+ ndev->stats.rx_over_errors++;
+ ndev->stats.rx_errors++;
+ writeb((u8)~EIFR_OLIF, &priv->regs->eifr);
+ }
+
+ netif_rx(skb);
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void rcar_can_tx_done(struct net_device *ndev)
+{
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ u32 ie_mask = 0;
+ int i;
+
+ /* Set Transmit Mailbox Search Mode */
+ writeb(MSMR_TXMB, &priv->regs->msmr);
+ for (i = 0; i < N_TX_MB; i++) {
+ u8 mctl, mbx;
+
+ mbx = readb(&priv->regs->mssr);
+ if (mbx & MSSR_SEST)
+ break;
+ mbx &= MSSR_MBNST;
+ stats->tx_bytes += readb(&priv->regs->mb[mbx].dlc);
+ stats->tx_packets++;
+ mctl = readb(&priv->regs->mctl[mbx]);
+ /* Bits SENTDATA and TRMREQ cannot be
+ * set to 0 simultaneously
+ */
+ mctl &= ~MCTL_TRMREQ;
+ writeb(mctl, &priv->regs->mctl[mbx]);
+ mctl &= ~MCTL_SENTDATA;
+ /* Clear interrupt */
+ writeb(mctl, &priv->regs->mctl[mbx]);
+ ie_mask |= BIT(mbx - FIRST_TX_MB);
+ can_get_echo_skb(ndev, mbx - FIRST_TX_MB);
+ can_led_event(ndev, CAN_LED_EVENT_TX);
+ }
+ /* Set receive mailbox search mode */
+ writeb(MSMR_RXMB, &priv->regs->msmr);
+ /* Disable mailbox interrupt, mark mailbox as free */
+ if (ie_mask) {
+ u32 mier1;
+
+ spin_lock(&priv->mier_lock);
+ mier1 = readl(&priv->regs->mier1);
+ writel(mier1 & ~ie_mask, &priv->regs->mier1);
+ spin_unlock(&priv->mier_lock);
+ netif_wake_queue(ndev);
+ }
+}
+
+static irqreturn_t rcar_can_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+ u8 isr;
+
+ isr = readb(&priv->regs->isr);
+ if (!(isr & priv->ier))
+ return IRQ_NONE;
+
+ if (isr & ISR_ERSF)
+ rcar_can_error(ndev);
+
+ if (isr & ISR_TXMF)
+ rcar_can_tx_done(ndev);
+
+ if (isr & ISR_RXM1F) {
+ if (napi_schedule_prep(&priv->napi)) {
+ /* Disable Rx interrupts */
+ priv->ier &= ~IER_RXM1IE;
+ writeb(priv->ier, &priv->regs->ier);
+ __napi_schedule(&priv->napi);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rcar_can_set_bittiming(struct net_device *dev)
+{
+ struct rcar_can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ u32 bcr;
+ u16 ctlr;
+ u8 clkr;
+
+ clk_prepare_enable(priv->clk);
+ /* rcar_can_set_bittiming() is called in CAN sleep mode.
+ * Can write to BCR in CAN reset mode or CAN halt mode.
+ * Cannot write to CLKR in halt mode, so go to reset mode.
+ */
+ ctlr = readw(&priv->regs->ctlr);
+ ctlr &= ~CTLR_SLPM;
+ ctlr |= CTLR_FORCE_RESET;
+ writew(ctlr, &priv->regs->ctlr);
+ /* Don't overwrite CLKR with 32-bit BCR access */
+ /* CLKR has 8-bit access */
+ clkr = readb(&priv->regs->clkr);
+ bcr = BCR_TSEG1(bt->phase_seg1 + bt->prop_seg - 1) |
+ BCR_BPR(bt->brp - 1) | BCR_SJW(bt->sjw - 1) |
+ BCR_TSEG2(bt->phase_seg2 - 1);
+ writel(bcr, &priv->regs->bcr);
+ writeb(clkr, &priv->regs->clkr);
+ ctlr |= CTLR_SLPM;
+ writew(ctlr, &priv->regs->ctlr);
+ clk_disable_unprepare(priv->clk);
+ return 0;
+}
+
+static void rcar_can_start(struct net_device *ndev)
+{
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+ u16 ctlr, n;
+
+ /* Set controller to known mode:
+ * - normal mailbox mode (no FIFO);
+ * - accept all messages (no filter).
+ * CAN is in sleep mode after MCU hardware or software reset.
+ */
+ ctlr = readw(&priv->regs->ctlr);
+ ctlr &= ~CTLR_SLPM;
+ writew(ctlr, &priv->regs->ctlr);
+ /* Go to reset mode */
+ ctlr |= CTLR_FORCE_RESET;
+ writew(ctlr, &priv->regs->ctlr);
+ ctlr |= CTLR_IDFM_MIXED; /* Select mixed ID mode */
+ ctlr |= CTLR_TPM; /* Set mailbox number priority transmit mode */
+ ctlr |= CTLR_BOM_ENT; /* Entry to halt mode automatically */
+ /* at bus-off */
+ writew(ctlr, &priv->regs->ctlr);
+
+ writeb(priv->clock_select, &priv->regs->clkr);
+
+ /* Accept all SID and EID */
+ for (n = 0; n < N_RX_MKREGS; n++)
+ writel(0, &priv->regs->mkr_0_1[n]);
+ writel(0, &priv->regs->mkivlr0);
+
+ /* Initial value of MIER1 undefined. Mark all Tx mailboxes as free. */
+ writel(0, &priv->regs->mier1);
+
+ priv->ier = IER_TXMIE | IER_ERSIE | IER_RXM1IE;
+ writeb(priv->ier, &priv->regs->ier);
+
+ /* Accumulate error codes */
+ writeb(ECSR_EDPM, &priv->regs->ecsr);
+ /* Enable error interrupts */
+ writeb(EIER_EWIE | EIER_EPIE | EIER_BOEIE |
+ (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING ?
+ EIER_BEIE : 0) | EIER_ORIE | EIER_OLIE,
+ &priv->regs->eier);
+ /* Enable interrupts for RX mailboxes */
+ writel(RX_MBX_MASK, &priv->regs->mier0);
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ /* Write to the CiMCTLj register in CAN
+ * operation mode or CAN halt mode.
+ * Configure mailboxes 0-31 as Rx mailboxes.
+ * Configure mailboxes 32-63 as Tx mailboxes.
+ */
+ /* Go to halt mode */
+ ctlr |= CTLR_HALT;
+ ctlr &= ~CTLR_RESET;
+ writew(ctlr, &priv->regs->ctlr);
+ for (n = 0; n < FIRST_TX_MB; n++) {
+ /* According to documentation we should clear MCTL
+ * register before configuring mailbox.
+ */
+ writeb(0, &priv->regs->mctl[n]);
+ writeb(MCTL_RECREQ, &priv->regs->mctl[n]);
+ writeb(0, &priv->regs->mctl[FIRST_TX_MB + n]);
+ }
+ /* Go to operation mode */
+ writew(ctlr & ~CTLR_FORCE_RESET, &priv->regs->ctlr);
+}
+
+static int rcar_can_open(struct net_device *ndev)
+{
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+ int err;
+
+ clk_prepare_enable(priv->clk);
+ err = open_candev(ndev);
+ if (err) {
+ netdev_err(ndev, "open_candev() failed %d\n", err);
+ goto out;
+ }
+ napi_enable(&priv->napi);
+ err = request_irq(ndev->irq, rcar_can_interrupt, 0, ndev->name, ndev);
+ if (err) {
+ netdev_err(ndev, "error requesting interrupt %x\n", ndev->irq);
+ goto out_close;
+ }
+ can_led_event(ndev, CAN_LED_EVENT_OPEN);
+ rcar_can_start(ndev);
+ netif_start_queue(ndev);
+ return 0;
+out_close:
+ napi_disable(&priv->napi);
+ close_candev(ndev);
+ clk_disable_unprepare(priv->clk);
+out:
+ return err;
+}
+
+static void rcar_can_stop(struct net_device *ndev)
+{
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+ u16 ctlr;
+
+ /* Go to (force) reset mode */
+ ctlr = readw(&priv->regs->ctlr);
+ ctlr |= CTLR_FORCE_RESET;
+ writew(ctlr, &priv->regs->ctlr);
+ writel(0, &priv->regs->mier0);
+ writel(0, &priv->regs->mier1);
+ writeb(0, &priv->regs->ier);
+ writeb(0, &priv->regs->eier);
+ /* Go to sleep mode */
+ ctlr |= CTLR_SLPM;
+ writew(ctlr, &priv->regs->ctlr);
+ priv->can.state = CAN_STATE_STOPPED;
+}
+
+static int rcar_can_close(struct net_device *ndev)
+{
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ rcar_can_stop(ndev);
+ free_irq(ndev->irq, ndev);
+ napi_disable(&priv->napi);
+ clk_disable_unprepare(priv->clk);
+ close_candev(ndev);
+ can_led_event(ndev, CAN_LED_EVENT_STOP);
+ return 0;
+}
+
+static netdev_tx_t rcar_can_start_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ u32 data, mier1, mbxno, i;
+ unsigned long flags;
+ u8 mctl = 0;
+
+ if (can_dropped_invalid_skb(ndev, skb))
+ return NETDEV_TX_OK;
+
+ spin_lock_irqsave(&priv->mier_lock, flags);
+ mier1 = readl(&priv->regs->mier1);
+ if (mier1) {
+ i = __builtin_clz(mier1);
+ mbxno = i ? N_MBX - i : FIRST_TX_MB;
+ } else {
+ mbxno = FIRST_TX_MB;
+ }
+ mier1 |= BIT(mbxno - FIRST_TX_MB);
+ writel(mier1, &priv->regs->mier1);
+ spin_unlock_irqrestore(&priv->mier_lock, flags);
+ if (unlikely(mier1 == 0xffffffff))
+ netif_stop_queue(ndev);
+
+ if (cf->can_id & CAN_EFF_FLAG) {
+ /* Extended frame format */
+ data = (cf->can_id & CAN_EFF_MASK) | RCAR_CAN_IDE;
+ } else {
+ /* Standard frame format */
+ data = (cf->can_id & CAN_SFF_MASK) << RCAR_CAN_SID_SHIFT;
+ }
+ if (cf->can_id & CAN_RTR_FLAG) {
+ /* Remote transmission request */
+ data |= RCAR_CAN_RTR;
+ }
+ writel(data, &priv->regs->mb[mbxno].id);
+
+ writeb(cf->can_dlc, &priv->regs->mb[mbxno].dlc);
+
+ for (i = 0; i < cf->can_dlc; i++)
+ writeb(cf->data[i], &priv->regs->mb[mbxno].data[i]);
+
+ can_put_echo_skb(skb, ndev, mbxno - FIRST_TX_MB);
+
+ priv->ier |= IER_TXMIE;
+ writeb(priv->ier, &priv->regs->ier);
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ mctl |= MCTL_ONESHOT;
+
+ /* Start TX */
+ mctl |= MCTL_TRMREQ;
+ writeb(mctl, &priv->regs->mctl[mbxno]);
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops rcar_can_netdev_ops = {
+ .ndo_open = rcar_can_open,
+ .ndo_stop = rcar_can_close,
+ .ndo_start_xmit = rcar_can_start_xmit,
+};
+
+static void rcar_can_rx_pkt(struct rcar_can_priv *priv, int mbx)
+{
+ struct net_device_stats *stats = &priv->ndev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ u32 data;
+ u8 dlc;
+
+ skb = alloc_can_skb(priv->ndev, &cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ data = readl(&priv->regs->mb[mbx].id);
+ if (data & RCAR_CAN_IDE)
+ cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ else
+ cf->can_id = (data >> RCAR_CAN_SID_SHIFT) & CAN_SFF_MASK;
+
+ dlc = readb(&priv->regs->mb[mbx].dlc);
+ cf->can_dlc = get_can_dlc(dlc);
+ if (data & RCAR_CAN_RTR) {
+ cf->can_id |= CAN_RTR_FLAG;
+ } else {
+ for (dlc = 0; dlc < cf->can_dlc; dlc++)
+ cf->data[dlc] = readb(&priv->regs->mb[mbx].data[dlc]);
+ }
+
+ can_led_event(priv->ndev, CAN_LED_EVENT_RX);
+
+ netif_receive_skb(skb);
+ stats->rx_bytes += cf->can_dlc;
+ stats->rx_packets++;
+}
+
+static int rcar_can_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct rcar_can_priv *priv = container_of(napi,
+ struct rcar_can_priv, napi);
+ int num_pkts = 0;
+
+ /* Find mailbox */
+ while (num_pkts < quota) {
+ u8 mctl, mbx;
+
+ mbx = readb(&priv->regs->mssr);
+ if (mbx & MSSR_SEST)
+ break;
+ mbx &= MSSR_MBNST;
+ mctl = readb(&priv->regs->mctl[mbx]);
+ /* Clear interrupt */
+ writeb(mctl & ~MCTL_NEWDATA, &priv->regs->mctl[mbx]);
+ rcar_can_rx_pkt(priv, mbx);
+ ++num_pkts;
+ }
+ /* All packets processed */
+ if (num_pkts < quota) {
+ napi_complete(napi);
+ priv->ier |= IER_RXM1IE;
+ writeb(priv->ier, &priv->regs->ier);
+ }
+ return num_pkts;
+}
+
+static int rcar_can_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+ switch (mode) {
+ case CAN_MODE_START:
+ rcar_can_start(ndev);
+ netif_wake_queue(ndev);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int rcar_can_get_berr_counter(const struct net_device *dev,
+ struct can_berr_counter *bec)
+{
+ struct rcar_can_priv *priv = netdev_priv(dev);
+
+ clk_prepare_enable(priv->clk);
+ bec->txerr = readb(&priv->regs->tecr);
+ bec->rxerr = readb(&priv->regs->recr);
+ clk_disable_unprepare(priv->clk);
+ return 0;
+}
+
+static int rcar_can_probe(struct platform_device *pdev)
+{
+ struct rcar_can_platform_data *pdata;
+ struct rcar_can_priv *priv;
+ struct net_device *ndev;
+ struct resource *mem;
+ void __iomem *addr;
+ int err = -ENODEV;
+ int irq;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data provided!\n");
+ goto fail;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "No IRQ resource\n");
+ goto fail;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ addr = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(addr)) {
+ err = PTR_ERR(addr);
+ goto fail;
+ }
+
+ ndev = alloc_candev(sizeof(struct rcar_can_priv), N_MBX - FIRST_TX_MB);
+ if (!ndev) {
+ dev_err(&pdev->dev, "alloc_candev failed\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ priv = netdev_priv(ndev);
+
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ err = PTR_ERR(priv->clk);
+ dev_err(&pdev->dev, "cannot get clock: %d\n", err);
+ goto fail_clk;
+ }
+
+ ndev->netdev_ops = &rcar_can_netdev_ops;
+ ndev->irq = irq;
+ ndev->flags |= IFF_ECHO;
+ priv->ndev = ndev;
+ priv->regs = addr;
+ priv->clock_select = pdata->clock_select;
+ priv->can.clock.freq = clk_get_rate(priv->clk);
+ priv->can.bittiming_const = &rcar_can_bittiming_const;
+ priv->can.do_set_bittiming = rcar_can_set_bittiming;
+ priv->can.do_set_mode = rcar_can_do_set_mode;
+ priv->can.do_get_berr_counter = rcar_can_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING |
+ CAN_CTRLMODE_ONE_SHOT;
+ platform_set_drvdata(pdev, ndev);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ spin_lock_init(&priv->mier_lock);
+
+ netif_napi_add(ndev, &priv->napi, rcar_can_rx_poll,
+ RCAR_CAN_NAPI_WEIGHT);
+ err = register_candev(ndev);
+ if (err) {
+ dev_err(&pdev->dev, "register_candev() failed\n");
+ goto fail_candev;
+ }
+
+ devm_can_led_init(ndev);
+
+ dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n",
+ priv->regs, ndev->irq);
+
+ return 0;
+fail_candev:
+ netif_napi_del(&priv->napi);
+fail_clk:
+ free_candev(ndev);
+fail:
+ return err;
+}
+
+static int rcar_can_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+
+ unregister_candev(ndev);
+ netif_napi_del(&priv->napi);
+ free_candev(ndev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rcar_can_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+ u16 ctlr;
+
+ if (netif_running(ndev)) {
+ netif_stop_queue(ndev);
+ netif_device_detach(ndev);
+ }
+ ctlr = readw(&priv->regs->ctlr);
+ ctlr |= CTLR_HALT;
+ writew(ctlr, &priv->regs->ctlr);
+ ctlr |= CTLR_SLPM;
+ writew(ctlr, &priv->regs->ctlr);
+ priv->can.state = CAN_STATE_SLEEPING;
+
+ clk_disable(priv->clk);
+ return 0;
+}
+
+static int rcar_can_resume(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct rcar_can_priv *priv = netdev_priv(ndev);
+ u16 ctlr;
+
+ clk_enable(priv->clk);
+
+ ctlr = readw(&priv->regs->ctlr);
+ ctlr &= ~CTLR_SLPM;
+ writew(ctlr, &priv->regs->ctlr);
+ ctlr &= ~CTLR_FORCE_RESET;
+ writew(ctlr, &priv->regs->ctlr);
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ if (netif_running(ndev)) {
+ netif_device_attach(ndev);
+ netif_start_queue(ndev);
+ }
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rcar_can_pm_ops, rcar_can_suspend, rcar_can_resume);
+
+static struct platform_driver rcar_can_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &rcar_can_pm_ops,
+ },
+ .probe = rcar_can_probe,
+ .remove = rcar_can_remove,
+};
+
+module_platform_driver(rcar_can_driver);
+
+MODULE_AUTHOR("Cogent Embedded, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CAN driver for Renesas R-Car SoC");
+MODULE_ALIAS("platform:" DRV_NAME);
Index: linux-can-next/include/linux/can/platform/rcar_can.h
===================================================================
--- /dev/null
+++ linux-can-next/include/linux/can/platform/rcar_can.h
@@ -0,0 +1,15 @@
+#ifndef _CAN_PLATFORM_RCAR_CAN_H_
+#define _CAN_PLATFORM_RCAR_CAN_H_
+
+#include <linux/types.h>
+
+/* Clock Select Register settings */
+#define CLKR_CLKEXT 3 /* Externally input clock */
+#define CLKR_CLKP2 1 /* Peripheral clock (clkp2) */
+#define CLKR_CLKP1 0 /* Peripheral clock (clkp1) */
+
+struct rcar_can_platform_data {
+ u8 clock_select; /* Clock source select */
+};
+
+#endif /* !_CAN_PLATFORM_RCAR_CAN_H_ */
^ permalink raw reply
* [PATCH 1/8] net ipset: use rbtree postorder iteration instead of opencoding
From: Cody P Schafer @ 2013-11-01 22:38 UTC (permalink / raw)
To: David S. Miller, Florian Westphal, Jozsef Kadlecsik,
Pablo Neira Ayuso, YOSHIFUJI Hideaki
Cc: Cody P Schafer, coreteam, linux-kernel, netdev, netfilter-devel,
netfilter, Patrick McHardy
Use rbtree_postorder_for_each_entry_safe() to destroy the rbtree instead
of opencoding an alternate postorder iteration that modifies the tree
Signed-off-by: Cody P Schafer <cody@linux.vnet.ibm.com>
---
net/netfilter/ipset/ip_set_hash_netiface.c | 27 ++++-----------------------
1 file changed, 4 insertions(+), 23 deletions(-)
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c
index 7d798d5..99dba4c 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -45,31 +45,12 @@ struct iface_node {
static void
rbtree_destroy(struct rb_root *root)
{
- struct rb_node *p, *n = root->rb_node;
- struct iface_node *node;
-
- /* Non-recursive destroy, like in ext3 */
- while (n) {
- if (n->rb_left) {
- n = n->rb_left;
- continue;
- }
- if (n->rb_right) {
- n = n->rb_right;
- continue;
- }
- p = rb_parent(n);
- node = rb_entry(n, struct iface_node, node);
- if (!p)
- *root = RB_ROOT;
- else if (p->rb_left == n)
- p->rb_left = NULL;
- else if (p->rb_right == n)
- p->rb_right = NULL;
+ struct iface_node *node, *next;
+ rbtree_postorder_for_each_entry_safe(node, next, root, node)
kfree(node);
- n = p;
- }
+
+ *root = RB_ROOT;
}
static int
--
1.8.4.2
^ permalink raw reply related
* Re: [PATCH v2] can: add Renesas R-Car CAN driver
From: Sergei Shtylyov @ 2013-11-01 22:24 UTC (permalink / raw)
To: Wolfgang Grandegger, netdev, mkl, linux-can; +Cc: linux-sh, vksavl
In-Reply-To: <526AC65B.5020203@grandegger.com>
On 10/25/2013 11:28 PM, Wolfgang Grandegger wrote:
>> Add support for the CAN controller found in Renesas R-Car SoCs.
>> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
[...]
>> Index: linux-can-next/drivers/net/can/rcar_can.c
>> ===================================================================
>> --- /dev/null
>> +++ linux-can-next/drivers/net/can/rcar_can.c
>> @@ -0,0 +1,920 @@
[...]
>> +static bool autorecovery;
>> +module_param(autorecovery, bool, 0644);
>> +MODULE_PARM_DESC(autorecovery, "Automatic hardware recovery from bus-off");
> Software recovery is the preferred solution. No need to support
> automatic recovery by the hardware.
OK, removed it.
>> +/* Mailbox registers structure */
>> +struct rcar_can_mbox_regs {
>> + u32 id; /* IDE and RTR bits, SID and EID */
>> + u8 stub; /* Not used */
>> + u8 dlc; /* Data Length Code - bits [0..3] */
>> + u8 data[8]; /* Data Bytes */
>> + u8 tsh; /* Time Stamp Higher Byte */
>> + u8 tsl; /* Time Stamp Lower Byte */
> I would add padding bytes here to ensure alignment.
What padding? This is how the hardware registers are laid out. I think I
should rather add __packed after }.
>> +};
>> +
>> +struct rcar_can_regs {
>> + struct rcar_can_mbox_regs mb[N_MBX];
>> +};
> Where are the other registers? Using a mix of struct and #define offsets
> is really ugly.
OK, fixed. I just thought using {read|write}[bwl]() doesn't buy us any
type checking anyway, so was being lazy here.
>> +struct rcar_can_priv {
>> + struct can_priv can; /* Must be the first member! */
>> + struct net_device *ndev;
>> + struct napi_struct napi;
>> + struct rcar_can_regs __iomem *regs;
>> + struct clk *clk;
>> + spinlock_t mier_lock;
>> + u8 clock_select;
>> + u8 ier;
> s/ier/ier_shadow/ ?
I'd rather leave it as is... hoping you won't insist.
>> +};
>> +static void rcar_can_start(struct net_device *ndev);
> Forward declarations should be avoided.
Removed it since it's not needed anymore.
>> +
>> +static void rcar_can_error(struct net_device *ndev)
>> +{
>> + struct rcar_can_priv *priv = netdev_priv(ndev);
>> + struct net_device_stats *stats = &ndev->stats;
>> + struct can_frame *cf;
>> + struct sk_buff *skb;
>> + u8 eifr, txerr = 0, rxerr = 0;
>> +
>> + /* Propagate the error condition to the CAN stack */
>> + skb = alloc_can_err_skb(ndev, &cf);
>> + if (!skb)
>> + return;
>> + eifr = rcar_can_readb(priv, RCAR_CAN_EIFR);
>> + if (eifr & (EIFR_EWIF | EIFR_EPIF)) {
>> + cf->can_id |= CAN_ERR_CRTL;
>> + txerr = rcar_can_readb(priv, RCAR_CAN_TECR);
>> + rxerr = rcar_can_readb(priv, RCAR_CAN_RECR);
> Could you please add these values to data[6..7] as shown here:
> http://lxr.free-electrons.com/source/drivers/net/can/sja1000/sja1000.c#L475
OK.
>> + }
>> + if (eifr & EIFR_BEIF) {
>> + int rx_errors = 0, tx_errors = 0;
>> + u8 ecsr;
>> +
>> + if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
>> + tx_failure_cleanup(ndev);
>> + netdev_dbg(priv->ndev, "Bus error interrupt:\n");
>> + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
>> + cf->data[2] = CAN_ERR_PROT_UNSPEC;
>> +
>> + ecsr = rcar_can_readb(priv, RCAR_CAN_ECSR);
>> + if (ecsr & ECSR_ADEF) {
>> + netdev_dbg(priv->ndev, "ACK Delimiter Error\n");
>> + cf->data[3] |= CAN_ERR_PROT_LOC_ACK_DEL;
>> + tx_errors++;
>> + rcar_can_writeb(priv, RCAR_CAN_ECSR, (u8)~ECSR_ADEF);
> Please avoid casts here and below.
These casts help avoid compiler warnings.
[...]
>> + if (eifr & EIFR_BORIF) {
>> + netdev_dbg(priv->ndev, "Bus-off recovery interrupt\n");
>> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
>> + cf->can_id |= CAN_ERR_RESTARTED;
> Does this interrupt come when Bus-off recovery has compeleted (back to
> error-active?) CAN_ERR_RESTARTED should be reported when the bus-off
> recovery has been triggered. It takes some time before it is really
> completed.
Removed the handler.
>> + if (eifr & EIFR_OLIF) {
>> + netdev_dbg(priv->ndev,
>> + "Overload Frame Transmission error interrupt\n");
>> + cf->can_id |= CAN_ERR_PROT;
>> + cf->data[2] |= CAN_ERR_PROT_OVERLOAD;
>> + ndev->stats.rx_over_errors++;
>> + ndev->stats.rx_errors++;
>> + rcar_can_writeb(priv, RCAR_CAN_EIFR, (u8)~EIFR_OLIF);
> No unnecessary casts please.
Unfortunately, they are necessary.
[...]
>> +static irqreturn_t rcar_can_interrupt(int irq, void *dev_id)
>> +{
>> + struct net_device *ndev = (struct net_device *)dev_id;
>> + struct rcar_can_priv *priv = netdev_priv(ndev);
>> + struct net_device_stats *stats = &ndev->stats;
>> + u8 isr;
>> +
>> + isr = rcar_can_readb(priv, RCAR_CAN_ISR);
>> + if (!(isr & priv->ier))
>> + return IRQ_NONE;
>> +
>> + if (isr & ISR_ERSF)
>> + rcar_can_error(ndev);
>> +
>> + if (isr & ISR_TXMF) {
>> + u32 ie_mask = 0;
>> + int i;
>> +
>> + /* Set Transmit Mailbox Search Mode */
>> + rcar_can_writeb(priv, RCAR_CAN_MSMR, MSMR_TXMB);
>> + for (i = 0; i < N_TX_MB; i++) {
>> + u8 mctl, mbx;
>> +
>> + mbx = rcar_can_readb(priv, RCAR_CAN_MSSR);
>> + if (mbx & MSSR_SEST)
>> + break;
>> + mbx &= MSSR_MBNST;
>> + stats->tx_bytes += readb(&priv->regs->mb[mbx].dlc);
>> + stats->tx_packets++;
>> + mctl = rcar_can_readb(priv, RCAR_CAN_MCTL(mbx));
>> + /* Bits SENTDATA and TRMREQ cannot be
>> + * set to 0 simultaneously
>> + */
>> + mctl &= ~MCTL_TRMREQ;
>> + rcar_can_writeb(priv, RCAR_CAN_MCTL(mbx), mctl);
>> + mctl &= ~MCTL_SENTDATA;
>> + /* Clear interrupt */
>> + rcar_can_writeb(priv, RCAR_CAN_MCTL(mbx), mctl);
>> + ie_mask |= BIT(mbx - FIRST_TX_MB);
>> + can_get_echo_skb(ndev, mbx - FIRST_TX_MB);
>> + can_led_event(ndev, CAN_LED_EVENT_TX);
>> + }
>> + /* Set receive mailbox search mode */
>> + rcar_can_writeb(priv, RCAR_CAN_MSMR, MSMR_RXMB);
>> + /* Disable mailbox interrupt, mark mailbox as free */
>> + if (ie_mask) {
>> + u32 mier1;
>> +
>> + spin_lock(&priv->mier_lock);
>> + mier1 = rcar_can_readl(priv, RCAR_CAN_MIER1);
>> + rcar_can_writel(priv, RCAR_CAN_MIER1, mier1 & ~ie_mask);
>> + spin_unlock(&priv->mier_lock);
>> + netif_wake_queue(ndev);
>> + }
>> + }
> Would be nice to have this in an (inline) function, like for the error
> handling above.
OK, but I guess *inline* keyword is not necessary as gcc should figure it
out itself, given only a single call.
[...]
>> +static int rcar_can_probe(struct platform_device *pdev)
>> +{
[...]
>> + ndev->netdev_ops = &rcar_can_netdev_ops;
>> + ndev->irq = irq;
>> + ndev->flags |= IFF_ECHO;
>> + priv->ndev = ndev;
>> + priv->regs = (struct rcar_can_regs *)addr;
> Is this cast needed?
Not really, removed it.
[...]
> Wolfgang.
WBR, Sergei
^ permalink raw reply
* Re: [PATCH] ipv6: remove the unnecessary statement in find_match()
From: David Miller @ 2013-11-01 22:08 UTC (permalink / raw)
To: duanj.fnst; +Cc: netdev, hannes
In-Reply-To: <52723A84.9080308@cn.fujitsu.com>
From: Duan Jiong <duanj.fnst@cn.fujitsu.com>
Date: Thu, 31 Oct 2013 19:09:56 +0800
> Removing CONFIG_IPV6_ROUTER_PREF means that the Router Preference is
> always on, is this understanding right?
>
> If that's is correct, i think compatibility issues will arise. For
> example, when the CONFIG_IPV6_ROUTER_PREF option is on, the kernel
> should not do round-robin during default router selection, but when
> the CONFIG_IPV6_ROUTER_PREF option is off, the kernel should do it.
Everyone is essentially getting it enabled right now anyways, since
%99.999999 of users run distribution kernels. Nobody has asked
for a run-time knob for this, which therefore means that %99.999999
of users simply do not care.
Yes there are people who build their own kernels, but to tell you
the truth I doubt anyone really turns this off explicitly to turn
off router preference handling.
But I'm willing to cater to them _iff_ the RFC tells us we should
provide should a capability.
^ permalink raw reply
* Re: [PATCH] bnx2: Use dev_kfree_skb_any() in bnx2_tx_int()
From: David Miller @ 2013-11-01 22:01 UTC (permalink / raw)
To: xiyou.wangcong; +Cc: mchan, tdmackey, netdev, linux-kernel
In-Reply-To: <CAM_iQpXPrEj619=PRaKN4PxgOoEB96U0mLFS65AzXOezSWvhxQ@mail.gmail.com>
From: Cong Wang <xiyou.wangcong@gmail.com>
Date: Thu, 31 Oct 2013 21:19:16 -0700
> 2013年10月30日 下午9:26于 "David Miller" <davem@davemloft.net>写道:
>>
>> We have to provide a softint compatible environment for this callback
>> to run in else everything is completely broken.
>>
>> All these drivers can safely assume softirq safe locking is
>> sufficient, you're suggesting we need to take this hardirq safety and
>> I'm really not willing to allow things to go that far. A lot of
>> effort has been expended precisely to avoid that kind of overhead and
>> cost.
>
> Alright, I am thinking to move netpoll_poll_dev() to a delayed work.
What if the printk is outputting a message that will help us discover
that work queues are deadlocked?
You can't delay the message, because every layer of indirection you
add increases the possibility that the message it never seen. You
have to do it synchronously.
^ permalink raw reply
* Re: [PATCH net-next 1/1] ipv6: remove the destination condition on flow label sharing
From: Hannes Frederic Sowa @ 2013-11-01 21:40 UTC (permalink / raw)
To: Florent Fourcot; +Cc: netdev
In-Reply-To: <1383322286-21464-1-git-send-email-florent.fourcot@enst-bretagne.fr>
On Fri, Nov 01, 2013 at 05:11:26PM +0100, Florent Fourcot wrote:
> In case of label sharing, it should be possible to use
> one label to more than one destination. This
> old restriction is not mandatory, so we can remove it.
Why?
Maybe I am not the only one up to date with recent advances in flow label
management but I guess others want to know why, too?
Please reflect this in your changelog.
Greetings,
Hannes
^ permalink raw reply
* Re: [patch net-next] ipv6: allow userspace to create address with IFLA_F_TEMPORARY flag
From: Hannes Frederic Sowa @ 2013-11-01 21:28 UTC (permalink / raw)
To: David Miller
Cc: David.Laight, jiri, vyasevich, netdev, kuznet, jmorris, yoshfuji,
kaber, thaller, stephen
In-Reply-To: <20131029.155809.311976718174740421.davem@davemloft.net>
On Tue, Oct 29, 2013 at 03:58:09PM -0400, David Miller wrote:
> From: Hannes Frederic Sowa <hannes@stressinduktion.org>
> Date: Tue, 29 Oct 2013 13:40:10 +0100
>
> > It seems not that invasive to switch from af_packet to an udp socket
> > with SO_BROADCAST set.
>
> Precisely!
dhclient compiled with --enable-use-sockets worked out of the box, I merely
had to fix a small compile error (most non-intrusive version):
https://github.com/hannes/isc-dhcp/commit/55c3b7d80541b38389244f67f7f5bdb16ad02474
# lsof -p $(pidof dhclient)
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
dhclient 1247 root cwd DIR 252,2 4096 2 /
dhclient 1247 root rtd DIR 252,2 4096 2 /
dhclient 1247 root txt REG 252,2 6683635 145354 /home/hannes/isc-dhcp/client/dhclient
dhclient 1247 root mem REG 252,2 162472 30914 /usr/lib64/ld-2.17.so
dhclient 1247 root mem REG 252,2 2108632 30915 /usr/lib64/libc-2.17.so
dhclient 1247 root mem REG 252,2 62368 2205 /usr/lib64/libnss_files-2.17.so
dhclient 1247 root 0u CHR 1,3 0t0 1028 /dev/null
dhclient 1247 root 1u CHR 1,3 0t0 1028 /dev/null
dhclient 1247 root 2u CHR 1,3 0t0 1028 /dev/null
dhclient 1247 root 3u unix 0xffff880114b3c200 0t0 20634 socket
dhclient 1247 root 4w REG 252,2 802 539097 /var/db/dhclient.leases
dhclient 1247 root 5u IPv4 20658 0t0 UDP *:bootpc
dhclient 1247 root 20u IPv4 20635 0t0 UDP *:43148
dhclient 1247 root 21u IPv6 20636 0t0 UDP *:37190
Broadcasts get dropped when rp_filter is activated and no ip address
is bound for that interface. Do we want to relax the restriction for
broadcast so dhcp with sockets can be shipped by default?
Greetings,
Hannes
^ permalink raw reply
* [PATCH net-next 4/6] qlcnic: refactor Tx/SDS ring calculation and validation in driver.
From: Himanshu Madhani @ 2013-11-01 20:51 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept_NX_Linux_NIC_Driver, Himanshu Madhani
In-Reply-To: <cover.1383358893.git.himanshu.madhani@qlogic.com>
From: Himanshu Madhani <himanshu.madhani@qlogic.com>
o Current driver has duplicate code for validating user input
for changing Tx/SDS rings using set_channel ethtool interface.
This patch removes duplicate code and refactored Tx/SDS ring
validation for 82xx/83xx/84xx series adapter.
o Refactored code now calculates maximum Tx/Rx ring driver can
support based on Default, NPAR and SRIOV PF/VF mode of driver.
Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 66 +++--
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 69 +++--
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h | 13 +-
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c | 38 ++-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 26 +-
.../net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 116 +++++---
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | 8 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c | 2 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c | 46 +--
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 327 ++++++++++-----------
.../ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 6 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c | 4 +-
12 files changed, 407 insertions(+), 314 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 2d818c1..3109730 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -98,8 +98,22 @@
#define TX_STOP_THRESH ((MAX_SKB_FRAGS >> 2) + MAX_TSO_HEADER_DESC \
+ MGMT_CMD_DESC_RESV)
#define QLCNIC_MAX_TX_TIMEOUTS 2
-#define QLCNIC_MAX_TX_RINGS 8
-#define QLCNIC_MAX_SDS_RINGS 8
+
+/* Driver will use 1 Tx ring in INT-x/MSI/SRIOV mode. */
+#define QLCNIC_SINGLE_RING 1
+#define QLCNIC_DEF_SDS_RINGS 4
+#define QLCNIC_DEF_TX_RINGS 4
+#define QLCNIC_MAX_VNIC_TX_RINGS 4
+#define QLCNIC_MAX_VNIC_SDS_RINGS 4
+
+enum qlcnic_queue_type {
+ QLCNIC_TX_QUEUE = 1,
+ QLCNIC_RX_QUEUE,
+};
+
+/* Operational mode for driver */
+#define QLCNIC_VNIC_MODE 0xFF
+#define QLCNIC_DEFAULT_MODE 0x0
/*
* Following are the states of the Phantom. Phantom will set them and
@@ -945,8 +959,6 @@ struct qlcnic_ipaddr {
#define QLCNIC_BEACON_EANBLE 0xC
#define QLCNIC_BEACON_DISABLE 0xD
-#define QLCNIC_DEF_NUM_STS_DESC_RINGS 4
-#define QLCNIC_DEF_NUM_TX_RINGS 4
#define QLCNIC_MSIX_TBL_SPACE 8192
#define QLCNIC_PCI_REG_MSIX_TBL 0x44
#define QLCNIC_MSIX_TBL_PGSIZE 4096
@@ -1017,7 +1029,6 @@ struct qlcnic_adapter {
unsigned long state;
u32 flags;
- int max_drv_tx_rings;
u16 num_txd;
u16 num_rxd;
u16 num_jumbo_rxd;
@@ -1025,7 +1036,13 @@ struct qlcnic_adapter {
u16 max_jumbo_rxd;
u8 max_rds_rings;
- u8 max_sds_rings;
+
+ u8 max_sds_rings; /* max sds rings supported by adapter */
+ u8 max_tx_rings; /* max tx rings supported by adapter */
+
+ u8 drv_tx_rings; /* max tx rings supported by driver */
+ u8 drv_sds_rings; /* max sds rings supported by driver */
+
u8 rx_csum;
u8 portnum;
@@ -1548,12 +1565,13 @@ int qlcnic_loopback_test(struct net_device *, u8);
/* Functions from qlcnic_main.c */
int qlcnic_reset_context(struct qlcnic_adapter *);
-void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings);
-int qlcnic_diag_alloc_res(struct net_device *netdev, int test);
-netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
-int qlcnic_set_max_rss(struct qlcnic_adapter *, u8, int);
-int qlcnic_validate_max_rss(struct qlcnic_adapter *, __u32);
-int qlcnic_validate_max_tx_rings(struct qlcnic_adapter *, u32 txq);
+void qlcnic_diag_free_res(struct net_device *netdev, int);
+int qlcnic_diag_alloc_res(struct net_device *netdev, int);
+netdev_tx_t qlcnic_xmit_frame(struct sk_buff *, struct net_device *);
+void qlcnic_set_tx_ring_count(struct qlcnic_adapter *, u8);
+void qlcnic_set_sds_ring_count(struct qlcnic_adapter *, u8);
+int qlcnic_setup_rings(struct qlcnic_adapter *, u8, u8);
+int qlcnic_validate_rings(struct qlcnic_adapter *, __u32, int);
void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter);
void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *);
int qlcnic_enable_msix(struct qlcnic_adapter *, u32);
@@ -1646,19 +1664,18 @@ static inline u32 qlcnic_tx_avail(struct qlcnic_host_tx_ring *tx_ring)
static inline int qlcnic_set_real_num_queues(struct qlcnic_adapter *adapter,
struct net_device *netdev)
{
- int err, tx_q;
-
- tx_q = adapter->max_drv_tx_rings;
+ int err;
- netdev->num_tx_queues = tx_q;
- netdev->real_num_tx_queues = tx_q;
+ netdev->num_tx_queues = adapter->drv_tx_rings;
+ netdev->real_num_tx_queues = adapter->drv_tx_rings;
- err = netif_set_real_num_tx_queues(netdev, tx_q);
+ err = netif_set_real_num_tx_queues(netdev, adapter->drv_tx_rings);
if (err)
dev_err(&adapter->pdev->dev, "failed to set %d Tx queues\n",
- tx_q);
+ adapter->drv_tx_rings);
else
- dev_info(&adapter->pdev->dev, "set %d Tx queues\n", tx_q);
+ dev_info(&adapter->pdev->dev, "Set %d Tx queues\n",
+ adapter->drv_tx_rings);
return err;
}
@@ -1700,7 +1717,7 @@ struct qlcnic_hardware_ops {
int (*write_reg) (struct qlcnic_adapter *, ulong, u32);
void (*get_ocm_win) (struct qlcnic_hardware_context *);
int (*get_mac_address) (struct qlcnic_adapter *, u8 *, u8);
- int (*setup_intr) (struct qlcnic_adapter *, u8, int);
+ int (*setup_intr) (struct qlcnic_adapter *);
int (*alloc_mbx_args)(struct qlcnic_cmd_args *,
struct qlcnic_adapter *, u32);
int (*mbx_cmd) (struct qlcnic_adapter *, struct qlcnic_cmd_args *);
@@ -1771,10 +1788,9 @@ static inline int qlcnic_get_mac_address(struct qlcnic_adapter *adapter,
return adapter->ahw->hw_ops->get_mac_address(adapter, mac, function);
}
-static inline int qlcnic_setup_intr(struct qlcnic_adapter *adapter,
- u8 num_intr, int txq)
+static inline int qlcnic_setup_intr(struct qlcnic_adapter *adapter)
{
- return adapter->ahw->hw_ops->setup_intr(adapter, num_intr, txq);
+ return adapter->ahw->hw_ops->setup_intr(adapter);
}
static inline int qlcnic_alloc_mbx_args(struct qlcnic_cmd_args *mbx,
@@ -2010,7 +2026,7 @@ static inline bool qlcnic_check_multi_tx(struct qlcnic_adapter *adapter)
static inline void qlcnic_disable_multi_tx(struct qlcnic_adapter *adapter)
{
test_and_clear_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state);
- adapter->max_drv_tx_rings = 1;
+ adapter->drv_tx_rings = QLCNIC_SINGLE_RING;
}
/* When operating in a muti tx mode, driver needs to write 0x1
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index 2270a3e..75c09ff 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -13,7 +13,6 @@
#include <linux/interrupt.h>
#include <linux/aer.h>
-#define QLCNIC_MAX_TX_QUEUES 1
#define RSS_HASHTYPE_IP_TCP 0x3
#define QLC_83XX_FW_MBX_CMD 0
@@ -268,20 +267,18 @@ int qlcnic_83xx_wrt_reg_indirect(struct qlcnic_adapter *adapter, ulong addr,
}
}
-int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr, int txq)
+int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter)
{
int err, i, num_msix;
struct qlcnic_hardware_context *ahw = adapter->ahw;
- if (!num_intr)
- num_intr = QLCNIC_DEF_NUM_STS_DESC_RINGS;
- num_msix = rounddown_pow_of_two(min_t(int, num_online_cpus(),
- num_intr));
+ num_msix = adapter->drv_sds_rings;
+
/* account for AEN interrupt MSI-X based interrupts */
num_msix += 1;
if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
- num_msix += adapter->max_drv_tx_rings;
+ num_msix += adapter->drv_tx_rings;
err = qlcnic_enable_msix(adapter, num_msix);
if (err == -ENOMEM)
@@ -986,14 +983,14 @@ static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter)
sds_mbx_size = sizeof(struct qlcnic_sds_mbx);
context_id = recv_ctx->context_id;
- num_sds = (adapter->max_sds_rings - QLCNIC_MAX_RING_SETS);
+ num_sds = adapter->drv_sds_rings - QLCNIC_MAX_SDS_RINGS;
ahw->hw_ops->alloc_mbx_args(&cmd, adapter,
QLCNIC_CMD_ADD_RCV_RINGS);
cmd.req.arg[1] = 0 | (num_sds << 8) | (context_id << 16);
/* set up status rings, mbx 2-81 */
index = 2;
- for (i = 8; i < adapter->max_sds_rings; i++) {
+ for (i = 8; i < adapter->drv_sds_rings; i++) {
memset(&sds_mbx, 0, sds_mbx_size);
sds = &recv_ctx->sds_rings[i];
sds->consumer = 0;
@@ -1028,7 +1025,7 @@ static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter)
mbx_out = (struct qlcnic_add_rings_mbx_out *)&cmd.rsp.arg[1];
index = 0;
/* status descriptor ring */
- for (i = 8; i < adapter->max_sds_rings; i++) {
+ for (i = 8; i < adapter->drv_sds_rings; i++) {
sds = &recv_ctx->sds_rings[i];
sds->crb_sts_consumer = ahw->pci_base0 +
mbx_out->host_csmr[index];
@@ -1086,10 +1083,10 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)
struct qlcnic_hardware_context *ahw = adapter->ahw;
num_rds = adapter->max_rds_rings;
- if (adapter->max_sds_rings <= QLCNIC_MAX_RING_SETS)
- num_sds = adapter->max_sds_rings;
+ if (adapter->drv_sds_rings <= QLCNIC_MAX_SDS_RINGS)
+ num_sds = adapter->drv_sds_rings;
else
- num_sds = QLCNIC_MAX_RING_SETS;
+ num_sds = QLCNIC_MAX_SDS_RINGS;
sds_mbx_size = sizeof(struct qlcnic_sds_mbx);
rds_mbx_size = sizeof(struct qlcnic_rds_mbx);
@@ -1190,7 +1187,7 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)
sds->crb_intr_mask = ahw->pci_base0 + intr_mask;
}
- if (adapter->max_sds_rings > QLCNIC_MAX_RING_SETS)
+ if (adapter->drv_sds_rings > QLCNIC_MAX_SDS_RINGS)
err = qlcnic_83xx_add_rings(adapter);
out:
qlcnic_free_mbx_args(&cmd);
@@ -1246,9 +1243,9 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
mbx.size = tx->num_desc;
if (adapter->flags & QLCNIC_MSIX_ENABLED) {
if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
- msix_vector = adapter->max_sds_rings + ring;
+ msix_vector = adapter->drv_sds_rings + ring;
else
- msix_vector = adapter->max_sds_rings - 1;
+ msix_vector = adapter->drv_sds_rings - 1;
msix_id = ahw->intr_tbl[msix_vector].id;
} else {
msix_id = QLCRDX(ahw, QLCNIC_DEF_INT_ID);
@@ -1271,7 +1268,8 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
qlcnic_pf_set_interface_id_create_tx_ctx(adapter, &temp);
cmd.req.arg[1] = QLCNIC_CAP0_LEGACY_CONTEXT;
- cmd.req.arg[5] = QLCNIC_MAX_TX_QUEUES | temp;
+ cmd.req.arg[5] = QLCNIC_SINGLE_RING | temp;
+
buf = &cmd.req.arg[6];
memcpy(buf, &mbx, sizeof(struct qlcnic_tx_mbx));
/* send the mailbox command*/
@@ -1286,7 +1284,7 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
tx->ctx_id = mbx_out->ctx_id;
if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
!(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
- intr_mask = ahw->intr_tbl[adapter->max_sds_rings + ring].src;
+ intr_mask = ahw->intr_tbl[adapter->drv_sds_rings + ring].src;
tx->crb_intr_mask = ahw->pci_base0 + intr_mask;
}
dev_info(&adapter->pdev->dev, "Tx Context[0x%x] Created, state:0x%x\n",
@@ -1297,7 +1295,7 @@ out:
}
static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test,
- int num_sds_ring)
+ u8 num_sds_ring)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_host_sds_ring *sds_ring;
@@ -1313,7 +1311,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test,
qlcnic_detach(adapter);
- adapter->max_sds_rings = 1;
+ adapter->drv_sds_rings = QLCNIC_SINGLE_RING;
adapter->ahw->diag_test = test;
adapter->ahw->linkup = 0;
@@ -1327,7 +1325,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test,
if (ret) {
qlcnic_detach(adapter);
if (adapter_state == QLCNIC_ADAPTER_UP_MAGIC) {
- adapter->max_sds_rings = num_sds_ring;
+ adapter->drv_sds_rings = num_sds_ring;
qlcnic_attach(adapter);
}
netif_device_attach(netdev);
@@ -1340,7 +1338,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test,
}
if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &adapter->recv_ctx->sds_rings[ring];
qlcnic_83xx_enable_intr(adapter, sds_ring);
}
@@ -1361,7 +1359,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test,
}
static void qlcnic_83xx_diag_free_res(struct net_device *netdev,
- int max_sds_rings)
+ u8 drv_sds_rings)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_host_sds_ring *sds_ring;
@@ -1369,7 +1367,7 @@ static void qlcnic_83xx_diag_free_res(struct net_device *netdev,
clear_bit(__QLCNIC_DEV_UP, &adapter->state);
if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &adapter->recv_ctx->sds_rings[ring];
qlcnic_83xx_disable_intr(adapter, sds_ring);
if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
@@ -1393,7 +1391,7 @@ static void qlcnic_83xx_diag_free_res(struct net_device *netdev,
}
}
adapter->ahw->diag_test = 0;
- adapter->max_sds_rings = max_sds_rings;
+ adapter->drv_sds_rings = drv_sds_rings;
if (qlcnic_attach(adapter))
goto out;
@@ -1655,7 +1653,8 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_hardware_context *ahw = adapter->ahw;
- int ret = 0, loop = 0, max_sds_rings = adapter->max_sds_rings;
+ u8 drv_sds_rings = adapter->drv_sds_rings;
+ int ret = 0, loop = 0;
if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) {
netdev_warn(netdev,
@@ -1677,7 +1676,7 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
mode == QLCNIC_ILB_MODE ? "internal" : "external");
ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST,
- max_sds_rings);
+ drv_sds_rings);
if (ret)
goto fail_diag_alloc;
@@ -1715,10 +1714,10 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
qlcnic_83xx_clear_lb_mode(adapter, mode);
free_diag_res:
- qlcnic_83xx_diag_free_res(netdev, max_sds_rings);
+ qlcnic_83xx_diag_free_res(netdev, drv_sds_rings);
fail_diag_alloc:
- adapter->max_sds_rings = max_sds_rings;
+ adapter->drv_sds_rings = drv_sds_rings;
qlcnic_release_diag_lock(adapter);
return ret;
}
@@ -3303,10 +3302,10 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_hardware_context *ahw = adapter->ahw;
struct qlcnic_cmd_args cmd;
+ u8 val, drv_sds_rings = adapter->drv_sds_rings;
u32 data;
u16 intrpt_id, id;
- u8 val;
- int ret, max_sds_rings = adapter->max_sds_rings;
+ int ret;
if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
netdev_info(netdev, "Device is resetting\n");
@@ -3319,7 +3318,7 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
}
ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_INTERRUPT_TEST,
- max_sds_rings);
+ drv_sds_rings);
if (ret)
goto fail_diag_irq;
@@ -3356,10 +3355,10 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
done:
qlcnic_free_mbx_args(&cmd);
- qlcnic_83xx_diag_free_res(netdev, max_sds_rings);
+ qlcnic_83xx_diag_free_res(netdev, drv_sds_rings);
fail_diag_irq:
- adapter->max_sds_rings = max_sds_rings;
+ adapter->drv_sds_rings = drv_sds_rings;
qlcnic_release_diag_lock(adapter);
return ret;
}
@@ -3513,7 +3512,7 @@ int qlcnic_83xx_resume(struct qlcnic_adapter *adapter)
if (err)
return err;
- if (ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE) {
+ if (ahw->nic_mode == QLCNIC_VNIC_MODE) {
if (ahw->op_mode == QLCNIC_MGMT_FUNC) {
qlcnic_83xx_set_vnic_opmode(adapter);
} else {
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
index 9f4e4c4..4cae6ca 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
@@ -61,7 +61,6 @@
#define QLC_83XX_HOST_SDS_MBX_IDX 8
#define QLCNIC_HOST_RDS_MBX_IDX 88
-#define QLCNIC_MAX_RING_SETS 8
/* Pause control registers */
#define QLC_83XX_SRE_SHIM_REG 0x0D200284
@@ -183,8 +182,8 @@ struct qlcnic_rcv_mbx_out {
u8 num_pci_func;
u8 state;
#endif
- u32 host_csmr[QLCNIC_MAX_RING_SETS];
- struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS];
+ u32 host_csmr[QLCNIC_MAX_SDS_RINGS];
+ struct __host_producer_mbx host_prod[QLCNIC_MAX_SDS_RINGS];
} __packed;
struct qlcnic_add_rings_mbx_out {
@@ -197,8 +196,8 @@ struct qlcnic_add_rings_mbx_out {
u8 sts_num;
u8 rcv_num;
#endif
- u32 host_csmr[QLCNIC_MAX_RING_SETS];
- struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS];
+ u32 host_csmr[QLCNIC_MAX_SDS_RINGS];
+ struct __host_producer_mbx host_prod[QLCNIC_MAX_SDS_RINGS];
} __packed;
/* Transmit context mailbox inbox registers
@@ -415,8 +414,6 @@ enum qlcnic_83xx_states {
#define QLC_83XX_GET_VLAN_ALIGN_CAPABILITY(val) (val & 0x4000)
#define QLC_83XX_GET_FW_LRO_MSS_CAPABILITY(val) (val & 0x20000)
#define QLC_83XX_ESWITCH_CAPABILITY BIT_23
-#define QLC_83XX_VIRTUAL_NIC_MODE 0xFF
-#define QLC_83XX_DEFAULT_MODE 0x0
#define QLC_83XX_SRIOV_MODE 0x1
#define QLCNIC_BRDTYPE_83XX_10G 0x0083
@@ -524,7 +521,7 @@ enum qlc_83xx_ext_regs {
/* 83xx funcitons */
int qlcnic_83xx_get_fw_version(struct qlcnic_adapter *);
int qlcnic_83xx_issue_cmd(struct qlcnic_adapter *, struct qlcnic_cmd_args *);
-int qlcnic_83xx_setup_intr(struct qlcnic_adapter *, u8, int);
+int qlcnic_83xx_setup_intr(struct qlcnic_adapter *);
void qlcnic_83xx_get_func_no(struct qlcnic_adapter *);
int qlcnic_83xx_cam_lock(struct qlcnic_adapter *);
void qlcnic_83xx_cam_unlock(struct qlcnic_adapter *);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
index 475e919..1d97c0f 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
@@ -902,7 +902,7 @@ static int qlcnic_83xx_idc_need_reset_state(struct qlcnic_adapter *adapter)
qlcnic_83xx_idc_update_audit_reg(adapter, 0, 1);
set_bit(__QLCNIC_RESETTING, &adapter->state);
clear_bit(QLC_83XX_MBX_READY, &mbx->status);
- if (adapter->ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE)
+ if (adapter->ahw->nic_mode == QLCNIC_VNIC_MODE)
qlcnic_83xx_disable_vnic_mode(adapter, 1);
if (qlcnic_check_diag_status(adapter)) {
@@ -2033,6 +2033,8 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)
ahw->max_mac_filters = nic_info.max_mac_filters;
ahw->max_mtu = nic_info.max_mtu;
+ adapter->max_tx_rings = ahw->max_tx_ques;
+ adapter->max_sds_rings = ahw->max_rx_ques;
/* eSwitch capability indicates vNIC mode.
* vNIC and SRIOV are mutually exclusive operational modes.
* If SR-IOV capability is detected, SR-IOV physical function
@@ -2045,7 +2047,7 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)
return QLC_83XX_DEFAULT_OPMODE;
if (ahw->capabilities & QLC_83XX_ESWITCH_CAPABILITY)
- return QLC_83XX_VIRTUAL_NIC_MODE;
+ return QLCNIC_VNIC_MODE;
return QLC_83XX_DEFAULT_OPMODE;
}
@@ -2059,15 +2061,20 @@ int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter)
if (ret == -EIO)
return -EIO;
- if (ret == QLC_83XX_VIRTUAL_NIC_MODE) {
- ahw->nic_mode = QLC_83XX_VIRTUAL_NIC_MODE;
+ if (ret == QLCNIC_VNIC_MODE) {
+ ahw->nic_mode = QLCNIC_VNIC_MODE;
+
if (qlcnic_83xx_config_vnic_opmode(adapter))
return -EIO;
+ adapter->max_sds_rings = QLCNIC_MAX_VNIC_SDS_RINGS;
+ adapter->max_tx_rings = QLCNIC_SINGLE_RING;
} else if (ret == QLC_83XX_DEFAULT_OPMODE) {
- ahw->nic_mode = QLC_83XX_DEFAULT_MODE;
+ ahw->nic_mode = QLCNIC_DEFAULT_MODE;
adapter->nic_ops->init_driver = qlcnic_83xx_init_default_driver;
ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry;
+ adapter->max_sds_rings = ahw->max_rx_ques;
+ adapter->max_tx_rings = QLCNIC_SINGLE_RING;
} else {
return -EIO;
}
@@ -2170,6 +2177,19 @@ static int qlcnic_83xx_get_fw_info(struct qlcnic_adapter *adapter)
return err;
}
+static void qlcnic_83xx_init_rings(struct qlcnic_adapter *adapter)
+{
+ adapter->max_tx_rings = QLCNIC_MAX_TX_RINGS;
+ adapter->max_sds_rings = QLCNIC_MAX_SDS_RINGS;
+
+ qlcnic_set_tx_ring_count(adapter, QLCNIC_SINGLE_RING);
+
+ /* compute and set drv sds rings */
+ if (adapter->ahw->msix_supported)
+ qlcnic_set_sds_ring_count(adapter, QLCNIC_DEF_SDS_RINGS);
+ else
+ qlcnic_set_sds_ring_count(adapter, QLCNIC_SINGLE_RING);
+}
int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
{
@@ -2178,6 +2198,9 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
int err = 0;
ahw->msix_supported = !!qlcnic_use_msi_x;
+
+ qlcnic_83xx_init_rings(adapter);
+
err = qlcnic_83xx_init_mailbox_work(adapter);
if (err)
goto exit;
@@ -2209,7 +2232,7 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
if (err)
goto detach_mbx;
- err = qlcnic_setup_intr(adapter, 0, 0);
+ err = qlcnic_setup_intr(adapter);
if (err) {
dev_err(&adapter->pdev->dev, "Failed to setup interrupt\n");
goto disable_intr;
@@ -2231,6 +2254,7 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
if (err)
goto disable_mbx_intr;
+
/* Perform operating mode specific initialization */
err = adapter->nic_ops->init_driver(adapter);
if (err)
@@ -2267,7 +2291,7 @@ void qlcnic_83xx_aer_stop_poll_work(struct qlcnic_adapter *adapter)
clear_bit(QLC_83XX_MBX_READY, &idc->status);
cancel_delayed_work_sync(&adapter->fw_work);
- if (ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE)
+ if (ahw->nic_mode == QLCNIC_VNIC_MODE)
qlcnic_83xx_disable_vnic_mode(adapter, 1);
qlcnic_83xx_idc_detach_driver(adapter);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
index 86850dd..c7cbb90 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
@@ -270,7 +270,7 @@ int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
int err;
nrds_rings = adapter->max_rds_rings;
- nsds_rings = adapter->max_sds_rings;
+ nsds_rings = adapter->drv_sds_rings;
rq_size = SIZEOF_HOSTRQ_RX(struct qlcnic_hostrq_rx_ctx, nrds_rings,
nsds_rings);
@@ -475,7 +475,7 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,
if (qlcnic_check_multi_tx(adapter) &&
!adapter->ahw->diag_test) {
- temp_nsds_rings = adapter->max_sds_rings;
+ temp_nsds_rings = adapter->drv_sds_rings;
index = temp_nsds_rings + ring;
msix_id = ahw->intr_tbl[index].id;
prq->msi_index = cpu_to_le16(msix_id);
@@ -512,7 +512,7 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,
if (qlcnic_check_multi_tx(adapter) &&
!adapter->ahw->diag_test &&
(adapter->flags & QLCNIC_MSIX_ENABLED)) {
- index = adapter->max_sds_rings + ring;
+ index = adapter->drv_sds_rings + ring;
intr_mask = ahw->intr_tbl[index].src;
tx_ring->crb_intr_mask = ahw->pci_base0 + intr_mask;
}
@@ -582,7 +582,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
recv_ctx = adapter->recv_ctx;
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
ptr = (__le32 *)dma_alloc_coherent(&pdev->dev, sizeof(u32),
&tx_ring->hw_cons_phys_addr,
@@ -616,7 +616,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)
}
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
addr = dma_alloc_coherent(&adapter->pdev->dev,
@@ -664,7 +664,7 @@ int qlcnic_fw_create_ctx(struct qlcnic_adapter *dev)
if (err)
goto err_out;
- for (ring = 0; ring < dev->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < dev->drv_tx_rings; ring++) {
err = qlcnic_fw_cmd_create_tx_ctx(dev,
&dev->tx_ring[ring],
ring);
@@ -703,7 +703,7 @@ void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)
if (test_and_clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) {
qlcnic_fw_cmd_del_rx_ctx(adapter);
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++)
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++)
qlcnic_fw_cmd_del_tx_ctx(adapter,
&adapter->tx_ring[ring]);
@@ -733,7 +733,7 @@ void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter)
recv_ctx = adapter->recv_ctx;
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
if (tx_ring->hw_consumer != NULL) {
dma_free_coherent(&adapter->pdev->dev, sizeof(u32),
@@ -764,7 +764,7 @@ void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter)
}
}
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
if (sds_ring->desc_head != NULL) {
@@ -795,9 +795,9 @@ int qlcnic_82xx_config_intrpt(struct qlcnic_adapter *adapter, u8 op_type)
cmd.req.arg[1] = val;
err = qlcnic_issue_cmd(adapter, &cmd);
if (err) {
- netdev_err(netdev, "Failed to %s interrupts %d\n",
- op_type == QLCNIC_INTRPT_ADD ? "Add" :
- "Delete", err);
+ netdev_err(netdev, "failed to %s interrupts, id=%d, err=%d\n",
+ op_type == QLCNIC_INTRPT_ADD ? "add" :
+ "delete", ahw->intr_tbl[i].id, err);
qlcnic_free_mbx_args(&cmd);
return err;
}
@@ -895,6 +895,8 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter,
npar_info->max_rx_ques = le16_to_cpu(nic_info->max_rx_ques);
npar_info->capabilities = le32_to_cpu(nic_info->capabilities);
npar_info->max_mtu = le16_to_cpu(nic_info->max_mtu);
+ adapter->max_tx_rings = npar_info->max_tx_ques;
+ adapter->max_sds_rings = npar_info->max_rx_ques;
}
qlcnic_free_mbx_args(&cmd);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index 9b6dd98..225743c 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -226,9 +226,9 @@ static const u32 ext_diag_registers[] = {
static inline int qlcnic_get_ring_regs_len(struct qlcnic_adapter *adapter)
{
- int ring_regs_cnt = (adapter->max_drv_tx_rings * 5) +
+ int ring_regs_cnt = (adapter->drv_tx_rings * 5) +
(adapter->max_rds_rings * 2) +
- (adapter->max_sds_rings * 3) + 5;
+ (adapter->drv_sds_rings * 3) + 5;
return ring_regs_cnt * sizeof(u32);
}
@@ -530,8 +530,8 @@ qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
/* Marker btw regs and TX ring count */
regs_buff[i++] = 0xFFEFCDAB;
- regs_buff[i++] = adapter->max_drv_tx_rings; /* No. of TX ring */
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ regs_buff[i++] = adapter->drv_tx_rings; /* No. of TX ring */
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
regs_buff[i++] = le32_to_cpu(*(tx_ring->hw_consumer));
regs_buff[i++] = tx_ring->sw_consumer;
@@ -550,8 +550,8 @@ qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
regs_buff[i++] = rds_rings->producer;
}
- regs_buff[i++] = adapter->max_sds_rings; /* No. of SDS ring */
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ regs_buff[i++] = adapter->drv_sds_rings; /* No. of SDS ring */
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &(recv_ctx->sds_rings[ring]);
regs_buff[i++] = readl(sds_ring->crb_sts_consumer);
regs_buff[i++] = sds_ring->consumer;
@@ -664,46 +664,88 @@ qlcnic_set_ringparam(struct net_device *dev,
return qlcnic_reset_context(adapter);
}
+static int qlcnic_validate_ring_count(struct qlcnic_adapter *adapter,
+ u8 rx_ring, u8 tx_ring)
+{
+ if (rx_ring != 0) {
+ if (rx_ring > adapter->max_sds_rings) {
+ netdev_err(adapter->netdev, "Invalid ring count, SDS ring count %d should not be greater than max %d driver sds rings.\n",
+ rx_ring, adapter->max_sds_rings);
+ return -EINVAL;
+ }
+ }
+
+ if (tx_ring != 0) {
+ if (qlcnic_82xx_check(adapter) &&
+ (tx_ring > adapter->max_tx_rings)) {
+ netdev_err(adapter->netdev,
+ "Invalid ring count, Tx ring count %d should not be greater than max %d driver Tx rings.\n",
+ tx_ring, adapter->max_tx_rings);
+ return -EINVAL;
+ }
+
+ if (qlcnic_83xx_check(adapter) &&
+ (tx_ring > QLCNIC_SINGLE_RING)) {
+ netdev_err(adapter->netdev,
+ "Invalid ring count, Tx ring count %d should not be greater than %d driver Tx rings.\n",
+ tx_ring, QLCNIC_SINGLE_RING);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static void qlcnic_get_channels(struct net_device *dev,
struct ethtool_channels *channel)
{
struct qlcnic_adapter *adapter = netdev_priv(dev);
- int min;
-
- min = min_t(int, adapter->ahw->max_rx_ques, num_online_cpus());
- channel->max_rx = rounddown_pow_of_two(min);
- channel->max_tx = min_t(int, QLCNIC_MAX_TX_RINGS, num_online_cpus());
- channel->rx_count = adapter->max_sds_rings;
- channel->tx_count = adapter->max_drv_tx_rings;
+ channel->max_rx = adapter->max_sds_rings;
+ channel->max_tx = adapter->max_tx_rings;
+ channel->rx_count = adapter->drv_sds_rings;
+ channel->tx_count = adapter->drv_tx_rings;
}
static int qlcnic_set_channels(struct net_device *dev,
- struct ethtool_channels *channel)
+ struct ethtool_channels *channel)
{
struct qlcnic_adapter *adapter = netdev_priv(dev);
int err;
- int txq = 0;
if (channel->other_count || channel->combined_count)
return -EINVAL;
+ err = qlcnic_validate_ring_count(adapter, channel->rx_count,
+ channel->tx_count);
+ if (err)
+ return err;
+
if (channel->rx_count) {
- err = qlcnic_validate_max_rss(adapter, channel->rx_count);
- if (err)
+ err = qlcnic_validate_rings(adapter, channel->rx_count,
+ QLCNIC_RX_QUEUE);
+ if (err) {
+ netdev_err(dev, "Unable to configure %u SDS rings\n",
+ channel->rx_count);
return err;
+ }
}
if (qlcnic_82xx_check(adapter) && channel->tx_count) {
- err = qlcnic_validate_max_tx_rings(adapter, channel->tx_count);
- if (err)
+ err = qlcnic_validate_rings(adapter, channel->tx_count,
+ QLCNIC_TX_QUEUE);
+ if (err) {
+ netdev_err(dev, "Unable to configure %u Tx rings\n",
+ channel->tx_count);
return err;
- txq = channel->tx_count;
+ }
}
- err = qlcnic_set_max_rss(adapter, channel->rx_count, txq);
- netdev_info(dev, "allocated 0x%x sds rings and 0x%x tx rings\n",
- adapter->max_sds_rings, adapter->max_drv_tx_rings);
+ err = qlcnic_setup_rings(adapter, channel->rx_count,
+ channel->tx_count);
+ netdev_info(dev, "Allocated %d SDS rings and %d Tx rings\n",
+ adapter->drv_sds_rings, adapter->drv_tx_rings);
+
return err;
}
@@ -905,7 +947,7 @@ static int qlcnic_irq_test(struct net_device *netdev)
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_hardware_context *ahw = adapter->ahw;
struct qlcnic_cmd_args cmd;
- int ret, max_sds_rings = adapter->max_sds_rings;
+ int ret, drv_sds_rings = adapter->drv_sds_rings;
if (qlcnic_83xx_check(adapter))
return qlcnic_83xx_interrupt_test(netdev);
@@ -934,10 +976,10 @@ done:
qlcnic_free_mbx_args(&cmd);
free_diag_res:
- qlcnic_diag_free_res(netdev, max_sds_rings);
+ qlcnic_diag_free_res(netdev, drv_sds_rings);
clear_diag_irq:
- adapter->max_sds_rings = max_sds_rings;
+ adapter->drv_sds_rings = drv_sds_rings;
clear_bit(__QLCNIC_RESETTING, &adapter->state);
return ret;
@@ -1013,8 +1055,8 @@ int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode)
int qlcnic_loopback_test(struct net_device *netdev, u8 mode)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
- int max_drv_tx_rings = adapter->max_drv_tx_rings;
- int max_sds_rings = adapter->max_sds_rings;
+ int drv_tx_rings = adapter->drv_tx_rings;
+ int drv_sds_rings = adapter->drv_sds_rings;
struct qlcnic_host_sds_ring *sds_ring;
struct qlcnic_hardware_context *ahw = adapter->ahw;
int loop = 0;
@@ -1069,11 +1111,11 @@ int qlcnic_loopback_test(struct net_device *netdev, u8 mode)
qlcnic_clear_lb_mode(adapter, mode);
free_res:
- qlcnic_diag_free_res(netdev, max_sds_rings);
+ qlcnic_diag_free_res(netdev, drv_sds_rings);
clear_it:
- adapter->max_sds_rings = max_sds_rings;
- adapter->max_drv_tx_rings = max_drv_tx_rings;
+ adapter->drv_sds_rings = drv_sds_rings;
+ adapter->drv_tx_rings = drv_tx_rings;
clear_bit(__QLCNIC_RESETTING, &adapter->state);
return ret;
}
@@ -1127,7 +1169,7 @@ qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 *data)
break;
case ETH_SS_STATS:
num_stats = ARRAY_SIZE(qlcnic_tx_queue_stats_strings);
- for (i = 0; i < adapter->max_drv_tx_rings; i++) {
+ for (i = 0; i < adapter->drv_tx_rings; i++) {
for (index = 0; index < num_stats; index++) {
sprintf(data, "tx_queue_%d %s", i,
qlcnic_tx_queue_stats_strings[index]);
@@ -1233,7 +1275,7 @@ static void qlcnic_update_stats(struct qlcnic_adapter *adapter)
struct qlcnic_host_tx_ring *tx_ring;
int ring;
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
adapter->stats.xmit_on += tx_ring->tx_stats.xmit_on;
adapter->stats.xmit_off += tx_ring->tx_stats.xmit_off;
@@ -1268,10 +1310,10 @@ static void qlcnic_get_ethtool_stats(struct net_device *dev,
int index, ret, length, size, tx_size, ring;
char *p;
- tx_size = adapter->max_drv_tx_rings * QLCNIC_TX_STATS_LEN;
+ tx_size = adapter->drv_tx_rings * QLCNIC_TX_STATS_LEN;
memset(data, 0, tx_size * sizeof(u64));
- for (ring = 0, index = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0, index = 0; ring < adapter->drv_tx_rings; ring++) {
if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
tx_ring = &adapter->tx_ring[ring];
data = qlcnic_fill_tx_queue_stats(data, tx_ring);
@@ -1320,7 +1362,7 @@ static int qlcnic_set_led(struct net_device *dev,
enum ethtool_phys_id_state state)
{
struct qlcnic_adapter *adapter = netdev_priv(dev);
- int max_sds_rings = adapter->max_sds_rings;
+ int drv_sds_rings = adapter->drv_sds_rings;
int err = -EIO, active = 1;
if (qlcnic_83xx_check(adapter))
@@ -1378,7 +1420,7 @@ static int qlcnic_set_led(struct net_device *dev,
}
if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state))
- qlcnic_diag_free_res(dev, max_sds_rings);
+ qlcnic_diag_free_res(dev, drv_sds_rings);
if (!active || err)
clear_bit(__QLCNIC_LED_ENABLE, &adapter->state);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
index 272c356..13303e7 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
@@ -146,6 +146,12 @@ struct qlcnic_mailbox_metadata {
#define QLCNIC_MBX_PORT_RSP_OK 0x1a
#define QLCNIC_MBX_ASYNC_EVENT BIT_15
+/* Set HW Tx ring limit for 82xx adapter. */
+#define QLCNIC_MAX_HW_TX_RINGS 8
+#define QLCNIC_MAX_HW_VNIC_TX_RINGS 4
+#define QLCNIC_MAX_TX_RINGS 8
+#define QLCNIC_MAX_SDS_RINGS 8
+
struct qlcnic_pci_info;
struct qlcnic_info;
struct qlcnic_cmd_args;
@@ -176,7 +182,7 @@ int qlcnic_82xx_set_lb_mode(struct qlcnic_adapter *, u8);
void qlcnic_82xx_write_crb(struct qlcnic_adapter *, char *, loff_t, size_t);
void qlcnic_82xx_read_crb(struct qlcnic_adapter *, char *, loff_t, size_t);
void qlcnic_82xx_dev_request_reset(struct qlcnic_adapter *, u32);
-int qlcnic_82xx_setup_intr(struct qlcnic_adapter *, u8, int);
+int qlcnic_82xx_setup_intr(struct qlcnic_adapter *);
irqreturn_t qlcnic_82xx_clear_legacy_intr(struct qlcnic_adapter *);
int qlcnic_82xx_issue_cmd(struct qlcnic_adapter *adapter,
struct qlcnic_cmd_args *);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
index 66c26cf..e9c21e5 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c
@@ -236,7 +236,7 @@ int qlcnic_alloc_sw_resources(struct qlcnic_adapter *adapter)
spin_lock_init(&rds_ring->lock);
}
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
sds_ring->irq = adapter->msix_entries[ring].vector;
sds_ring->adapter = adapter;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
index df31be7..1c07ec2 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
@@ -787,6 +787,9 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter,
struct net_device *netdev = adapter->netdev;
struct qlcnic_skb_frag *frag;
+ if (!spin_trylock(&adapter->tx_clean_lock))
+ return 1;
+
sw_consumer = tx_ring->sw_consumer;
hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
@@ -840,6 +843,7 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter,
*/
hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
done = (sw_consumer == hw_consumer);
+ spin_unlock(&adapter->tx_clean_lock);
return done;
}
@@ -1459,18 +1463,18 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
struct qlcnic_host_tx_ring *tx_ring;
- if (qlcnic_alloc_sds_rings(recv_ctx, adapter->max_sds_rings))
+ if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings))
return -ENOMEM;
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
if (qlcnic_check_multi_tx(adapter) &&
!adapter->ahw->diag_test &&
- (adapter->max_drv_tx_rings > 1)) {
+ (adapter->drv_tx_rings > QLCNIC_SINGLE_RING)) {
netif_napi_add(netdev, &sds_ring->napi, qlcnic_rx_poll,
NAPI_POLL_WEIGHT);
} else {
- if (ring == (adapter->max_sds_rings - 1))
+ if (ring == (adapter->drv_sds_rings - 1))
netif_napi_add(netdev, &sds_ring->napi,
qlcnic_poll,
NAPI_POLL_WEIGHT);
@@ -1487,7 +1491,7 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
}
if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) {
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
netif_napi_add(netdev, &tx_ring->napi, qlcnic_tx_poll,
NAPI_POLL_WEIGHT);
@@ -1504,7 +1508,7 @@ void qlcnic_82xx_napi_del(struct qlcnic_adapter *adapter)
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
struct qlcnic_host_tx_ring *tx_ring;
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
netif_napi_del(&sds_ring->napi);
}
@@ -1512,7 +1516,7 @@ void qlcnic_82xx_napi_del(struct qlcnic_adapter *adapter)
qlcnic_free_sds_rings(adapter->recv_ctx);
if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) {
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
netif_napi_del(&tx_ring->napi);
}
@@ -1531,7 +1535,7 @@ void qlcnic_82xx_napi_enable(struct qlcnic_adapter *adapter)
if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
return;
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
napi_enable(&sds_ring->napi);
qlcnic_enable_int(sds_ring);
@@ -1540,8 +1544,8 @@ void qlcnic_82xx_napi_enable(struct qlcnic_adapter *adapter)
if (qlcnic_check_multi_tx(adapter) &&
(adapter->flags & QLCNIC_MSIX_ENABLED) &&
!adapter->ahw->diag_test &&
- (adapter->max_drv_tx_rings > 1)) {
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ (adapter->drv_tx_rings > QLCNIC_SINGLE_RING)) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
napi_enable(&tx_ring->napi);
qlcnic_enable_tx_intr(adapter, tx_ring);
@@ -1559,7 +1563,7 @@ void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter)
if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
return;
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
qlcnic_disable_int(sds_ring);
napi_synchronize(&sds_ring->napi);
@@ -1569,7 +1573,7 @@ void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter)
if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
!adapter->ahw->diag_test &&
qlcnic_check_multi_tx(adapter)) {
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
qlcnic_disable_tx_int(adapter, tx_ring);
napi_synchronize(&tx_ring->napi);
@@ -1907,7 +1911,7 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter)
if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
return;
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
napi_enable(&sds_ring->napi);
if (adapter->flags & QLCNIC_MSIX_ENABLED)
@@ -1916,7 +1920,7 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter)
if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
!(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
napi_enable(&tx_ring->napi);
qlcnic_83xx_enable_tx_intr(adapter, tx_ring);
@@ -1934,7 +1938,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)
if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
return;
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
if (adapter->flags & QLCNIC_MSIX_ENABLED)
qlcnic_83xx_disable_intr(adapter, sds_ring);
@@ -1944,7 +1948,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)
if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
!(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
qlcnic_83xx_disable_tx_intr(adapter, tx_ring);
napi_synchronize(&tx_ring->napi);
@@ -1961,10 +1965,10 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring;
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
- if (qlcnic_alloc_sds_rings(recv_ctx, adapter->max_sds_rings))
+ if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings))
return -ENOMEM;
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
if (adapter->flags & QLCNIC_MSIX_ENABLED) {
if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
@@ -1990,7 +1994,7 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,
if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
!(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
netif_napi_add(netdev, &tx_ring->napi,
qlcnic_83xx_msix_tx_poll,
@@ -2008,7 +2012,7 @@ void qlcnic_83xx_napi_del(struct qlcnic_adapter *adapter)
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
struct qlcnic_host_tx_ring *tx_ring;
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
netif_napi_del(&sds_ring->napi);
}
@@ -2017,7 +2021,7 @@ void qlcnic_83xx_napi_del(struct qlcnic_adapter *adapter)
if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
!(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
netif_napi_del(&tx_ring->napi);
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 1215d2a..1b59f1d 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -548,36 +548,75 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = {
.io_resume = qlcnic_82xx_io_resume,
};
-static void qlcnic_get_multiq_capability(struct qlcnic_adapter *adapter)
+static int qlcnic_check_multi_tx_capability(struct qlcnic_adapter *adapter)
{
struct qlcnic_hardware_context *ahw = adapter->ahw;
- int num_tx_q;
- if (ahw->msix_supported &&
+ if (qlcnic_82xx_check(adapter) &&
(ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_2_MULTI_TX)) {
- num_tx_q = min_t(int, QLCNIC_DEF_NUM_TX_RINGS,
- num_online_cpus());
- if (num_tx_q > 1) {
- test_and_set_bit(__QLCNIC_MULTI_TX_UNIQUE,
- &adapter->state);
- adapter->max_drv_tx_rings = num_tx_q;
- }
+ test_and_set_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state);
+ return 0;
} else {
- adapter->max_drv_tx_rings = 1;
+ return 1;
}
}
+static int qlcnic_max_rings(struct qlcnic_adapter *adapter, u8 ring_cnt,
+ int queue_type)
+{
+ int num_rings, max_rings = QLCNIC_MAX_SDS_RINGS;
+
+ if (queue_type == QLCNIC_RX_QUEUE)
+ max_rings = adapter->max_sds_rings;
+ else if (queue_type == QLCNIC_TX_QUEUE)
+ max_rings = adapter->max_tx_rings;
+
+ num_rings = rounddown_pow_of_two(min_t(int, num_online_cpus(),
+ max_rings));
+
+ if (ring_cnt > num_rings)
+ return num_rings;
+ else
+ return ring_cnt;
+}
+
+void qlcnic_set_tx_ring_count(struct qlcnic_adapter *adapter, u8 tx_cnt)
+{
+ /* 83xx adapter does not have max_tx_rings intialized in probe */
+ if (adapter->max_tx_rings)
+ adapter->drv_tx_rings = qlcnic_max_rings(adapter, tx_cnt,
+ QLCNIC_TX_QUEUE);
+ else
+ adapter->drv_tx_rings = tx_cnt;
+
+ dev_info(&adapter->pdev->dev, "Set %d Tx rings\n",
+ adapter->drv_tx_rings);
+}
+
+void qlcnic_set_sds_ring_count(struct qlcnic_adapter *adapter, u8 rx_cnt)
+{
+ /* 83xx adapter does not have max_sds_rings intialized in probe */
+ if (adapter->max_sds_rings)
+ adapter->drv_sds_rings = qlcnic_max_rings(adapter, rx_cnt,
+ QLCNIC_RX_QUEUE);
+ else
+ adapter->drv_sds_rings = rx_cnt;
+
+ dev_info(&adapter->pdev->dev, "Set %d SDS rings\n",
+ adapter->drv_sds_rings);
+}
+
int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
{
struct pci_dev *pdev = adapter->pdev;
- int max_tx_rings, max_sds_rings, tx_vector;
+ int drv_tx_rings, drv_sds_rings, tx_vector;
int err = -1, i;
if (adapter->flags & QLCNIC_TX_INTR_SHARED) {
- max_tx_rings = 0;
+ drv_tx_rings = 0;
tx_vector = 0;
} else {
- max_tx_rings = adapter->max_drv_tx_rings;
+ drv_tx_rings = adapter->drv_tx_rings;
tx_vector = 1;
}
@@ -589,7 +628,7 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
return -ENOMEM;
}
- adapter->max_sds_rings = 1;
+ adapter->drv_sds_rings = QLCNIC_SINGLE_RING;
adapter->flags &= ~(QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED);
if (adapter->ahw->msix_supported) {
@@ -602,18 +641,18 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
if (qlcnic_83xx_check(adapter)) {
adapter->ahw->num_msix = num_msix;
/* subtract mail box and tx ring vectors */
- adapter->max_sds_rings = num_msix -
- max_tx_rings - 1;
+ adapter->drv_sds_rings = num_msix -
+ drv_tx_rings - 1;
} else {
adapter->ahw->num_msix = num_msix;
if (qlcnic_check_multi_tx(adapter) &&
!adapter->ahw->diag_test &&
- (adapter->max_drv_tx_rings > 1))
- max_sds_rings = num_msix - max_tx_rings;
+ (adapter->drv_tx_rings > 1))
+ drv_sds_rings = num_msix - drv_tx_rings;
else
- max_sds_rings = num_msix;
+ drv_sds_rings = num_msix;
- adapter->max_sds_rings = max_sds_rings;
+ adapter->drv_sds_rings = drv_sds_rings;
}
dev_info(&pdev->dev, "using msi-x interrupts\n");
return err;
@@ -624,13 +663,13 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
if (qlcnic_83xx_check(adapter)) {
if (err < (QLC_83XX_MINIMUM_VECTOR - tx_vector))
return err;
- err -= (max_tx_rings + 1);
+ err -= drv_tx_rings + 1;
num_msix = rounddown_pow_of_two(err);
- num_msix += (max_tx_rings + 1);
+ num_msix += drv_tx_rings + 1;
} else {
num_msix = rounddown_pow_of_two(err);
if (qlcnic_check_multi_tx(adapter))
- num_msix += max_tx_rings;
+ num_msix += drv_tx_rings;
}
if (num_msix) {
@@ -683,25 +722,14 @@ static int qlcnic_enable_msi_legacy(struct qlcnic_adapter *adapter)
return err;
}
-int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr, int txq)
+int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter)
{
- struct qlcnic_hardware_context *ahw = adapter->ahw;
int num_msix, err = 0;
- if (!num_intr)
- num_intr = QLCNIC_DEF_NUM_STS_DESC_RINGS;
+ num_msix = adapter->drv_sds_rings;
- if (ahw->msix_supported) {
- num_msix = rounddown_pow_of_two(min_t(int, num_online_cpus(),
- num_intr));
- if (qlcnic_check_multi_tx(adapter)) {
- if (txq)
- adapter->max_drv_tx_rings = txq;
- num_msix += adapter->max_drv_tx_rings;
- }
- } else {
- num_msix = 1;
- }
+ if (qlcnic_check_multi_tx(adapter))
+ num_msix += adapter->drv_tx_rings;
err = qlcnic_enable_msix(adapter, num_msix);
if (err == -ENOMEM)
@@ -1136,14 +1164,18 @@ qlcnic_initialize_nic(struct qlcnic_adapter *adapter)
adapter->ahw->max_mac_filters = nic_info.max_mac_filters;
adapter->ahw->max_mtu = nic_info.max_mtu;
- /* Disable NPAR for 83XX */
- if (qlcnic_83xx_check(adapter))
- return err;
-
- if (adapter->ahw->capabilities & BIT_6)
+ if (adapter->ahw->capabilities & BIT_6) {
adapter->flags |= QLCNIC_ESWITCH_ENABLED;
- else
+ adapter->ahw->nic_mode = QLCNIC_VNIC_MODE;
+ adapter->max_tx_rings = QLCNIC_MAX_HW_VNIC_TX_RINGS;
+ adapter->max_sds_rings = QLCNIC_MAX_VNIC_SDS_RINGS;
+
+ dev_info(&adapter->pdev->dev, "vNIC mode enabled.\n");
+ } else {
+ adapter->ahw->nic_mode = QLCNIC_DEFAULT_MODE;
+ adapter->max_tx_rings = QLCNIC_MAX_HW_TX_RINGS;
adapter->flags &= ~QLCNIC_ESWITCH_ENABLED;
+ }
return err;
}
@@ -1291,6 +1323,8 @@ qlcnic_check_eswitch_mode(struct qlcnic_adapter *adapter)
"HAL Version: %d, Privileged function\n",
adapter->ahw->fw_hal_version);
}
+ } else {
+ adapter->ahw->nic_mode = QLCNIC_DEFAULT_MODE;
}
adapter->flags |= QLCNIC_ADAPTER_INITIALIZED;
@@ -1550,7 +1584,7 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter)
if (qlcnic_82xx_check(adapter) ||
(qlcnic_83xx_check(adapter) &&
(adapter->flags & QLCNIC_MSIX_ENABLED))) {
- num_sds_rings = adapter->max_sds_rings;
+ num_sds_rings = adapter->drv_sds_rings;
for (ring = 0; ring < num_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
if (qlcnic_82xx_check(adapter) &&
@@ -1584,7 +1618,7 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter)
(adapter->flags & QLCNIC_MSIX_ENABLED) &&
!(adapter->flags & QLCNIC_TX_INTR_SHARED))) {
handler = qlcnic_msix_tx_intr;
- for (ring = 0; ring < adapter->max_drv_tx_rings;
+ for (ring = 0; ring < adapter->drv_tx_rings;
ring++) {
tx_ring = &adapter->tx_ring[ring];
snprintf(tx_ring->name, sizeof(tx_ring->name),
@@ -1612,7 +1646,7 @@ qlcnic_free_irq(struct qlcnic_adapter *adapter)
if (qlcnic_82xx_check(adapter) ||
(qlcnic_83xx_check(adapter) &&
(adapter->flags & QLCNIC_MSIX_ENABLED))) {
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
free_irq(sds_ring->irq, sds_ring);
}
@@ -1621,7 +1655,7 @@ qlcnic_free_irq(struct qlcnic_adapter *adapter)
!(adapter->flags & QLCNIC_TX_INTR_SHARED)) ||
(qlcnic_82xx_check(adapter) &&
qlcnic_check_multi_tx(adapter))) {
- for (ring = 0; ring < adapter->max_drv_tx_rings;
+ for (ring = 0; ring < adapter->drv_tx_rings;
ring++) {
tx_ring = &adapter->tx_ring[ring];
if (tx_ring->irq)
@@ -1675,7 +1709,7 @@ int __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev)
adapter->ahw->linkup = 0;
- if (adapter->max_sds_rings > 1)
+ if (adapter->drv_sds_rings > 1)
qlcnic_config_rss(adapter, 1);
qlcnic_config_intr_coalesce(adapter);
@@ -1717,6 +1751,7 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
if (qlcnic_sriov_vf_check(adapter))
qlcnic_sriov_cleanup_async_list(&adapter->ahw->sriov->bc);
smp_mb();
+ spin_lock(&adapter->tx_clean_lock);
netif_carrier_off(netdev);
adapter->ahw->linkup = 0;
netif_tx_disable(netdev);
@@ -1735,8 +1770,9 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
qlcnic_reset_rx_buffers_list(adapter);
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++)
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++)
qlcnic_release_tx_buffers(adapter, &adapter->tx_ring[ring]);
+ spin_unlock(&adapter->tx_clean_lock);
}
/* Usage: During suspend and firmware recovery module */
@@ -1812,16 +1848,16 @@ void qlcnic_detach(struct qlcnic_adapter *adapter)
adapter->is_up = 0;
}
-void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings)
+void qlcnic_diag_free_res(struct net_device *netdev, int drv_sds_rings)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_host_sds_ring *sds_ring;
- int max_tx_rings = adapter->max_drv_tx_rings;
+ int drv_tx_rings = adapter->drv_tx_rings;
int ring;
clear_bit(__QLCNIC_DEV_UP, &adapter->state);
if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &adapter->recv_ctx->sds_rings[ring];
qlcnic_disable_int(sds_ring);
}
@@ -1832,8 +1868,8 @@ void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings)
qlcnic_detach(adapter);
adapter->ahw->diag_test = 0;
- adapter->max_sds_rings = max_sds_rings;
- adapter->max_drv_tx_rings = max_tx_rings;
+ adapter->drv_sds_rings = drv_sds_rings;
+ adapter->drv_tx_rings = drv_tx_rings;
if (qlcnic_attach(adapter))
goto out;
@@ -1899,10 +1935,10 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
qlcnic_detach(adapter);
- adapter->max_sds_rings = 1;
+ adapter->drv_sds_rings = QLCNIC_SINGLE_RING;
+ adapter->drv_tx_rings = QLCNIC_SINGLE_RING;
adapter->ahw->diag_test = test;
adapter->ahw->linkup = 0;
- adapter->max_drv_tx_rings = 1;
ret = qlcnic_attach(adapter);
if (ret) {
@@ -1923,7 +1959,7 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test)
}
if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) {
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &adapter->recv_ctx->sds_rings[ring];
qlcnic_enable_int(sds_ring);
}
@@ -2096,7 +2132,7 @@ void qlcnic_free_tx_rings(struct qlcnic_adapter *adapter)
int ring;
struct qlcnic_host_tx_ring *tx_ring;
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
if (tx_ring && tx_ring->cmd_buf_arr != NULL) {
vfree(tx_ring->cmd_buf_arr);
@@ -2114,14 +2150,14 @@ int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring;
struct qlcnic_cmd_buffer *cmd_buf_arr;
- tx_ring = kcalloc(adapter->max_drv_tx_rings,
+ tx_ring = kcalloc(adapter->drv_tx_rings,
sizeof(struct qlcnic_host_tx_ring), GFP_KERNEL);
if (tx_ring == NULL)
return -ENOMEM;
adapter->tx_ring = tx_ring;
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
tx_ring->num_desc = adapter->num_txd;
tx_ring->txq = netdev_get_tx_queue(netdev, ring);
@@ -2136,11 +2172,11 @@ int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter,
if (qlcnic_83xx_check(adapter) ||
(qlcnic_82xx_check(adapter) && qlcnic_check_multi_tx(adapter))) {
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
tx_ring->adapter = adapter;
if (adapter->flags & QLCNIC_MSIX_ENABLED) {
- index = adapter->max_sds_rings + ring;
+ index = adapter->drv_sds_rings + ring;
vector = adapter->msix_entries[index].vector;
tx_ring->irq = vector;
}
@@ -2260,6 +2296,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
rwlock_init(&adapter->ahw->crb_lock);
mutex_init(&adapter->ahw->mem_lock);
+ spin_lock_init(&adapter->tx_clean_lock);
INIT_LIST_HEAD(&adapter->mac_list);
qlcnic_register_dcb(adapter);
@@ -2274,20 +2311,21 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_maintenance_mode;
}
- qlcnic_get_multiq_capability(adapter);
-
- if ((adapter->ahw->act_pci_func > 2) &&
- qlcnic_check_multi_tx(adapter)) {
- adapter->max_drv_tx_rings = QLCNIC_DEF_NUM_TX_RINGS;
- dev_info(&adapter->pdev->dev,
- "vNIC mode enabled, Set max TX rings = %d\n",
- adapter->max_drv_tx_rings);
+ /* compute and set default and max tx/sds rings */
+ if (adapter->ahw->msix_supported) {
+ if (qlcnic_check_multi_tx_capability(adapter) == 1)
+ qlcnic_set_tx_ring_count(adapter,
+ QLCNIC_SINGLE_RING);
+ else
+ qlcnic_set_tx_ring_count(adapter,
+ QLCNIC_DEF_TX_RINGS);
+ qlcnic_set_sds_ring_count(adapter,
+ QLCNIC_DEF_SDS_RINGS);
+ } else {
+ qlcnic_set_tx_ring_count(adapter, QLCNIC_SINGLE_RING);
+ qlcnic_set_sds_ring_count(adapter, QLCNIC_SINGLE_RING);
}
- if (!qlcnic_check_multi_tx(adapter)) {
- clear_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state);
- adapter->max_drv_tx_rings = 1;
- }
err = qlcnic_setup_idc_param(adapter);
if (err)
goto err_out_free_hw;
@@ -2298,13 +2336,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (dcb && qlcnic_dcb_attach(dcb))
qlcnic_clear_dcb_ops(dcb);
-
} else if (qlcnic_83xx_check(adapter)) {
- adapter->max_drv_tx_rings = 1;
qlcnic_83xx_check_vf(adapter, ent);
adapter->portnum = adapter->ahw->pci_func;
err = qlcnic_83xx_init(adapter, pci_using_dac);
-
if (err) {
switch (err) {
case -ENOTRECOVERABLE:
@@ -2348,7 +2383,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
"Device does not support MSI interrupts\n");
if (qlcnic_82xx_check(adapter)) {
- err = qlcnic_setup_intr(adapter, 0, 0);
+ err = qlcnic_setup_intr(adapter);
if (err) {
dev_err(&pdev->dev, "Failed to setup interrupt\n");
goto err_out_disable_msi;
@@ -2719,7 +2754,7 @@ static void qlcnic_tx_timeout(struct net_device *netdev)
QLCNIC_FORCE_FW_DUMP_KEY);
} else {
netdev_info(netdev, "Tx timeout, reset adapter context.\n");
- for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
netdev_info(netdev, "Tx ring=%d\n", ring);
netdev_info(netdev,
@@ -2847,7 +2882,7 @@ static void qlcnic_poll_controller(struct net_device *netdev)
struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
disable_irq(adapter->irq);
- for (ring = 0; ring < adapter->max_sds_rings; ring++) {
+ for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
qlcnic_intr(adapter->irq, sds_ring);
}
@@ -3514,7 +3549,7 @@ static int qlcnic_attach_func(struct pci_dev *pdev)
qlcnic_clr_drv_state(adapter);
kfree(adapter->msix_entries);
adapter->msix_entries = NULL;
- err = qlcnic_setup_intr(adapter, 0, 0);
+ err = qlcnic_setup_intr(adapter);
if (err) {
kfree(adapter->msix_entries);
@@ -3659,130 +3694,94 @@ qlcnicvf_start_firmware(struct qlcnic_adapter *adapter)
return err;
}
-int qlcnic_validate_max_tx_rings(struct qlcnic_adapter *adapter, u32 txq)
+int qlcnic_validate_rings(struct qlcnic_adapter *adapter, __u32 ring_cnt,
+ int queue_type)
{
struct net_device *netdev = adapter->netdev;
- u8 max_hw = QLCNIC_MAX_TX_RINGS;
- u32 max_allowed;
+ u8 max_hw_rings = 0;
+ char buf[8];
+ int cur_rings;
+
+ if (queue_type == QLCNIC_RX_QUEUE) {
+ max_hw_rings = adapter->max_sds_rings;
+ cur_rings = adapter->drv_sds_rings;
+ strcpy(buf, "SDS");
+ } else if (queue_type == QLCNIC_TX_QUEUE) {
+ if (qlcnic_83xx_check(adapter))
+ max_hw_rings = QLCNIC_SINGLE_RING;
+ else
+ max_hw_rings = adapter->max_tx_rings;
- if (!qlcnic_use_msi_x && !qlcnic_use_msi) {
- netdev_err(netdev, "No Multi TX-Q support in INT-x mode\n");
- return -EINVAL;
+ cur_rings = adapter->drv_tx_rings;
+ strcpy(buf, "Tx");
}
- if (!qlcnic_check_multi_tx(adapter)) {
- netdev_err(netdev, "No Multi TX-Q support\n");
+ if (!qlcnic_use_msi_x && !qlcnic_use_msi) {
+ netdev_err(netdev, "No RSS/TSS support in INT-x mode\n");
return -EINVAL;
}
- if (txq > QLCNIC_MAX_TX_RINGS) {
- netdev_err(netdev, "Invalid ring count\n");
+ if (adapter->flags & QLCNIC_MSI_ENABLED) {
+ netdev_err(netdev, "No RSS/TSS support in MSI mode\n");
return -EINVAL;
}
- max_allowed = rounddown_pow_of_two(min_t(int, max_hw,
- num_online_cpus()));
- if ((txq > max_allowed) || !is_power_of_2(txq)) {
- if (!is_power_of_2(txq))
- netdev_err(netdev,
- "TX queue should be a power of 2\n");
- if (txq > num_online_cpus())
- netdev_err(netdev,
- "Tx queue should not be higher than [%u], number of online CPUs in the system\n",
- num_online_cpus());
- netdev_err(netdev, "Unable to configure %u Tx rings\n", txq);
+ if (ring_cnt < 2) {
+ netdev_err(netdev,
+ "%s rings value should not be lower than 2\n", buf);
return -EINVAL;
}
- return 0;
-}
-
-int qlcnic_validate_max_rss(struct qlcnic_adapter *adapter,
- __u32 val)
-{
- struct net_device *netdev = adapter->netdev;
- u8 max_hw = adapter->ahw->max_rx_ques;
- u32 max_allowed;
-
- if (!qlcnic_use_msi_x && !qlcnic_use_msi) {
- netdev_err(netdev, "No RSS support in INT-x mode\n");
+ if (!is_power_of_2(ring_cnt)) {
+ netdev_err(netdev, "%s rings value should be a power of 2\n",
+ buf);
return -EINVAL;
}
- if (val > QLCNIC_MAX_SDS_RINGS) {
- netdev_err(netdev, "RSS value should not be higher than %u\n",
- QLCNIC_MAX_SDS_RINGS);
- return -EINVAL;
+ if (qlcnic_82xx_check(adapter) && (queue_type == QLCNIC_TX_QUEUE) &&
+ !qlcnic_check_multi_tx(adapter)) {
+ netdev_err(netdev, "No Multi Tx queue support\n");
+ return -EINVAL;
}
- max_allowed = rounddown_pow_of_two(min_t(int, max_hw,
- num_online_cpus()));
- if ((val > max_allowed) || (val < 2) || !is_power_of_2(val)) {
- if (!is_power_of_2(val))
- netdev_err(netdev, "RSS value should be a power of 2\n");
-
- if (val < 2)
- netdev_err(netdev, "RSS value should not be lower than 2\n");
-
- if (val > max_hw)
- netdev_err(netdev,
- "RSS value should not be higher than[%u], the max RSS rings supported by the adapter\n",
- max_hw);
-
- if (val > num_online_cpus())
- netdev_err(netdev,
- "RSS value should not be higher than[%u], number of online CPUs in the system\n",
- num_online_cpus());
-
- netdev_err(netdev, "Unable to configure %u RSS rings\n", val);
-
+ if (ring_cnt > num_online_cpus()) {
+ netdev_err(netdev,
+ "%s value[%u] should not be higher than, number of online CPUs\n",
+ buf, num_online_cpus());
return -EINVAL;
}
+
return 0;
}
-int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, int txq)
+int qlcnic_setup_rings(struct qlcnic_adapter *adapter, u8 rx_cnt, u8 tx_cnt)
{
- int err;
struct net_device *netdev = adapter->netdev;
- int num_msix;
+ int err;
if (test_bit(__QLCNIC_RESETTING, &adapter->state))
return -EBUSY;
- if (qlcnic_82xx_check(adapter) && !qlcnic_use_msi_x &&
- !qlcnic_use_msi) {
- netdev_err(netdev, "No RSS support in INT-x mode\n");
- return -EINVAL;
- }
-
netif_device_detach(netdev);
if (netif_running(netdev))
__qlcnic_down(adapter, netdev);
qlcnic_detach(adapter);
- if (qlcnic_82xx_check(adapter)) {
- if (txq != 0)
- adapter->max_drv_tx_rings = txq;
-
- if (qlcnic_check_multi_tx(adapter) &&
- (txq > adapter->max_drv_tx_rings))
- num_msix = adapter->max_drv_tx_rings;
- else
- num_msix = data;
- }
-
if (qlcnic_83xx_check(adapter)) {
qlcnic_83xx_free_mbx_intr(adapter);
qlcnic_83xx_enable_mbx_poll(adapter);
}
- netif_set_real_num_tx_queues(netdev, adapter->max_drv_tx_rings);
-
qlcnic_teardown_intr(adapter);
- err = qlcnic_setup_intr(adapter, data, txq);
+ /* compute and set default and max tx/sds rings */
+ qlcnic_set_tx_ring_count(adapter, tx_cnt);
+ qlcnic_set_sds_ring_count(adapter, rx_cnt);
+
+ netif_set_real_num_tx_queues(netdev, adapter->drv_tx_rings);
+
+ err = qlcnic_setup_intr(adapter);
if (err) {
kfree(adapter->msix_entries);
netdev_err(netdev, "failed to setup interrupt\n");
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
index 8b96e29..21a4b27 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
@@ -508,7 +508,11 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
dev_warn(&adapter->pdev->dev,
"Device does not support MSI interrupts\n");
- err = qlcnic_setup_intr(adapter, 1, 0);
+ /* compute and set default and max tx/sds rings */
+ qlcnic_set_tx_ring_count(adapter, QLCNIC_SINGLE_RING);
+ qlcnic_set_sds_ring_count(adapter, QLCNIC_SINGLE_RING);
+
+ err = qlcnic_setup_intr(adapter);
if (err) {
dev_err(&adapter->pdev->dev, "Failed to setup interrupt\n");
goto err_out_disable_msi;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
index e8b1ce6..1a9f8a4 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
@@ -156,7 +156,7 @@ static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter,
const char *buf, size_t len)
{
struct qlcnic_hardware_context *ahw = adapter->ahw;
- int err, max_sds_rings = adapter->max_sds_rings;
+ int err, drv_sds_rings = adapter->drv_sds_rings;
u16 beacon;
u8 h_beacon_state, b_state, b_rate;
@@ -211,7 +211,7 @@ static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter,
}
if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state))
- qlcnic_diag_free_res(adapter->netdev, max_sds_rings);
+ qlcnic_diag_free_res(adapter->netdev, drv_sds_rings);
out:
if (!ahw->beacon_state)
--
1.8.1.4
^ permalink raw reply related
* [PATCH net-next 5/6] qlcnic: Enable multiple Tx queue support for 83xx/84xx Series adapters.
From: Himanshu Madhani @ 2013-11-01 20:51 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept_NX_Linux_NIC_Driver, Himanshu Madhani
In-Reply-To: <cover.1383358893.git.himanshu.madhani@qlogic.com>
From: Himanshu Madhani <himanshu.madhani@qlogic.com>
o 83xx and 84xx firmware is capable of multiple Tx queues.
This patch will enable multiple Tx queues for 83xx/84xx
series adapters. Max number of Tx queues supported will be 8.
Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 4 ++++
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c | 18 +++++++++++-------
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 2 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c | 5 +----
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 6 +-----
5 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index 75c09ff..18cb401 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -1654,6 +1654,7 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
struct qlcnic_adapter *adapter = netdev_priv(netdev);
struct qlcnic_hardware_context *ahw = adapter->ahw;
u8 drv_sds_rings = adapter->drv_sds_rings;
+ u8 drv_tx_rings = adapter->drv_tx_rings;
int ret = 0, loop = 0;
if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) {
@@ -1718,6 +1719,7 @@ free_diag_res:
fail_diag_alloc:
adapter->drv_sds_rings = drv_sds_rings;
+ adapter->drv_tx_rings = drv_tx_rings;
qlcnic_release_diag_lock(adapter);
return ret;
}
@@ -3303,6 +3305,7 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
struct qlcnic_hardware_context *ahw = adapter->ahw;
struct qlcnic_cmd_args cmd;
u8 val, drv_sds_rings = adapter->drv_sds_rings;
+ u8 drv_tx_rings = adapter->drv_tx_rings;
u32 data;
u16 intrpt_id, id;
int ret;
@@ -3359,6 +3362,7 @@ done:
fail_diag_irq:
adapter->drv_sds_rings = drv_sds_rings;
+ adapter->drv_tx_rings = drv_tx_rings;
qlcnic_release_diag_lock(adapter);
return ret;
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
index 1d97c0f..068c4b4 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
@@ -2068,13 +2068,13 @@ int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter)
return -EIO;
adapter->max_sds_rings = QLCNIC_MAX_VNIC_SDS_RINGS;
- adapter->max_tx_rings = QLCNIC_SINGLE_RING;
+ adapter->max_tx_rings = QLCNIC_MAX_VNIC_TX_RINGS;
} else if (ret == QLC_83XX_DEFAULT_OPMODE) {
ahw->nic_mode = QLCNIC_DEFAULT_MODE;
adapter->nic_ops->init_driver = qlcnic_83xx_init_default_driver;
ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry;
adapter->max_sds_rings = ahw->max_rx_ques;
- adapter->max_tx_rings = QLCNIC_SINGLE_RING;
+ adapter->max_tx_rings = ahw->max_tx_ques;
} else {
return -EIO;
}
@@ -2179,16 +2179,20 @@ static int qlcnic_83xx_get_fw_info(struct qlcnic_adapter *adapter)
static void qlcnic_83xx_init_rings(struct qlcnic_adapter *adapter)
{
+ u8 rx_cnt = QLCNIC_DEF_SDS_RINGS;
+ u8 tx_cnt = QLCNIC_DEF_TX_RINGS;
+
adapter->max_tx_rings = QLCNIC_MAX_TX_RINGS;
adapter->max_sds_rings = QLCNIC_MAX_SDS_RINGS;
- qlcnic_set_tx_ring_count(adapter, QLCNIC_SINGLE_RING);
+ if (!adapter->ahw->msix_supported) {
+ rx_cnt = QLCNIC_SINGLE_RING;
+ tx_cnt = QLCNIC_SINGLE_RING;
+ }
/* compute and set drv sds rings */
- if (adapter->ahw->msix_supported)
- qlcnic_set_sds_ring_count(adapter, QLCNIC_DEF_SDS_RINGS);
- else
- qlcnic_set_sds_ring_count(adapter, QLCNIC_SINGLE_RING);
+ qlcnic_set_tx_ring_count(adapter, tx_cnt);
+ qlcnic_set_sds_ring_count(adapter, rx_cnt);
}
int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index 225743c..b36c02f 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -731,7 +731,7 @@ static int qlcnic_set_channels(struct net_device *dev,
}
}
- if (qlcnic_82xx_check(adapter) && channel->tx_count) {
+ if (channel->tx_count) {
err = qlcnic_validate_rings(adapter, channel->tx_count,
QLCNIC_TX_QUEUE);
if (err) {
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
index 1c07ec2..0149c94 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
@@ -581,10 +581,7 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
goto drop_packet;
}
- if (qlcnic_check_multi_tx(adapter))
- tx_ring = &adapter->tx_ring[skb_get_queue_mapping(skb)];
- else
- tx_ring = &adapter->tx_ring[0];
+ tx_ring = &adapter->tx_ring[skb_get_queue_mapping(skb)];
num_txd = tx_ring->num_desc;
frag_count = skb_shinfo(skb)->nr_frags + 1;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 1b59f1d..8a2e4b2 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -3707,11 +3707,7 @@ int qlcnic_validate_rings(struct qlcnic_adapter *adapter, __u32 ring_cnt,
cur_rings = adapter->drv_sds_rings;
strcpy(buf, "SDS");
} else if (queue_type == QLCNIC_TX_QUEUE) {
- if (qlcnic_83xx_check(adapter))
- max_hw_rings = QLCNIC_SINGLE_RING;
- else
- max_hw_rings = adapter->max_tx_rings;
-
+ max_hw_rings = adapter->max_tx_rings;
cur_rings = adapter->drv_tx_rings;
strcpy(buf, "Tx");
}
--
1.8.1.4
^ permalink raw reply related
* [PATCH net-next 2/6] qlcnic: Replace kzalloc() with vzalloc().
From: Himanshu Madhani @ 2013-11-01 20:51 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept_NX_Linux_NIC_Driver, Manish Chopra, himanshu.madhani
In-Reply-To: <cover.1383358893.git.himanshu.madhani@qlogic.com>
From: Manish Chopra <manish.chopra@qlogic.com>
o Some of the places kzalloc() is not required. Replace it
with vzalloc() wherever it is not used appropriately
Signed-off-by: Manish Chopra <manish.chopra@qlogic.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 4 ++--
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c | 12 ++++++------
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | 12 ++++++------
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 21 ++++++++++-----------
4 files changed, 24 insertions(+), 25 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index f81e2dd..2270a3e 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -3544,7 +3544,7 @@ void qlcnic_83xx_free_mailbox(struct qlcnic_mailbox *mbx)
return;
destroy_workqueue(mbx->work_q);
- kfree(mbx);
+ vfree(mbx);
}
static inline void
@@ -3817,7 +3817,7 @@ int qlcnic_83xx_init_mailbox_work(struct qlcnic_adapter *adapter)
struct qlcnic_hardware_context *ahw = adapter->ahw;
struct qlcnic_mailbox *mbx;
- ahw->mailbox = kzalloc(sizeof(*mbx), GFP_KERNEL);
+ ahw->mailbox = vzalloc(sizeof(*mbx));
if (!ahw->mailbox)
return -ENOMEM;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
index 4a8a3f1..475e919 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
@@ -1254,24 +1254,24 @@ static int qlcnic_83xx_copy_bootloader(struct qlcnic_adapter *adapter)
if (size & 0xF)
size = (size + 16) & ~0xF;
- p_cache = kzalloc(size, GFP_KERNEL);
+ p_cache = vzalloc(size);
if (p_cache == NULL)
return -ENOMEM;
ret = qlcnic_83xx_lockless_flash_read32(adapter, src, p_cache,
size / sizeof(u32));
if (ret) {
- kfree(p_cache);
+ vfree(p_cache);
return ret;
}
/* 16 byte write to MS memory */
ret = qlcnic_83xx_ms_mem_write128(adapter, dest, (u32 *)p_cache,
size / 16);
if (ret) {
- kfree(p_cache);
+ vfree(p_cache);
return ret;
}
- kfree(p_cache);
+ vfree(p_cache);
return ret;
}
@@ -1605,13 +1605,13 @@ int qlcnic_83xx_get_reset_instruction_template(struct qlcnic_adapter *p_dev)
prev_ver = p_dev->fw_version;
curr_ver = qlcnic_83xx_get_fw_version(p_dev);
if (curr_ver > prev_ver)
- kfree(ahw->reset.buff);
+ vfree(ahw->reset.buff);
else
return 0;
}
ahw->reset.seq_error = 0;
- ahw->reset.buff = kzalloc(QLC_83XX_RESTART_TEMPLATE_SIZE, GFP_KERNEL);
+ ahw->reset.buff = vzalloc(QLC_83XX_RESTART_TEMPLATE_SIZE);
if (p_dev->ahw->reset.buff == NULL)
return -ENOMEM;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
index 86bca7c..d8949b5 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
@@ -260,7 +260,7 @@ int qlcnic_register_dcb(struct qlcnic_adapter *adapter)
{
struct qlcnic_dcb *dcb;
- dcb = kzalloc(sizeof(struct qlcnic_dcb), GFP_ATOMIC);
+ dcb = vzalloc(sizeof(*dcb));
if (!dcb)
return -ENOMEM;
@@ -292,11 +292,11 @@ static void __qlcnic_dcb_free(struct qlcnic_dcb *dcb)
dcb->wq = NULL;
}
- kfree(dcb->cfg);
+ vfree(dcb->cfg);
dcb->cfg = NULL;
- kfree(dcb->param);
+ vfree(dcb->param);
dcb->param = NULL;
- kfree(dcb);
+ vfree(dcb);
adapter->dcb = NULL;
}
@@ -320,13 +320,13 @@ static int __qlcnic_dcb_attach(struct qlcnic_dcb *dcb)
return -1;
}
- dcb->cfg = kzalloc(sizeof(struct qlcnic_dcb_cfg), GFP_ATOMIC);
+ dcb->cfg = vzalloc(sizeof(*dcb->cfg));
if (!dcb->cfg) {
err = -ENOMEM;
goto out_free_wq;
}
- dcb->param = kzalloc(sizeof(struct qlcnic_dcb_mbx_params), GFP_ATOMIC);
+ dcb->param = vzalloc(sizeof(*dcb->param));
if (!dcb->param) {
err = -ENOMEM;
goto out_free_cfg;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 46ddbd8..8d4536d 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -818,8 +818,8 @@ static bool qlcnic_port_eswitch_cfg_capability(struct qlcnic_adapter *adapter)
int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
{
+ int i, id = 0, ret = 0, j = 0, size;
struct qlcnic_pci_info *pci_info;
- int i, id = 0, ret = 0, j = 0;
u16 act_pci_func;
u8 pfn;
@@ -832,16 +832,15 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
goto err_pci_info;
act_pci_func = adapter->ahw->act_pci_func;
-
- adapter->npars = kzalloc(sizeof(struct qlcnic_npar_info) *
- act_pci_func, GFP_KERNEL);
+ size = sizeof(struct qlcnic_npar_info) * act_pci_func;
+ adapter->npars = vzalloc(size);
if (!adapter->npars) {
ret = -ENOMEM;
goto err_pci_info;
}
- adapter->eswitch = kzalloc(sizeof(struct qlcnic_eswitch) *
- QLCNIC_NIU_MAX_XG_PORTS, GFP_KERNEL);
+ size = sizeof(struct qlcnic_eswitch) * QLCNIC_NIU_MAX_XG_PORTS;
+ adapter->eswitch = vzalloc(size);
if (!adapter->eswitch) {
ret = -ENOMEM;
goto err_npars;
@@ -892,10 +891,10 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
return 0;
err_eswitch:
- kfree(adapter->eswitch);
+ vfree(adapter->eswitch);
adapter->eswitch = NULL;
err_npars:
- kfree(adapter->npars);
+ vfree(adapter->npars);
adapter->npars = NULL;
err_pci_info:
kfree(pci_info);
@@ -1881,7 +1880,7 @@ static void qlcnic_free_adapter_resources(struct qlcnic_adapter *adapter)
adapter->ahw->fw_dump.tmpl_hdr = NULL;
}
- kfree(adapter->ahw->reset.buff);
+ vfree(adapter->ahw->reset.buff);
adapter->ahw->fw_dump.tmpl_hdr = NULL;
}
@@ -2480,9 +2479,9 @@ static void qlcnic_remove(struct pci_dev *pdev)
qlcnic_detach(adapter);
if (adapter->npars != NULL)
- kfree(adapter->npars);
+ vfree(adapter->npars);
if (adapter->eswitch != NULL)
- kfree(adapter->eswitch);
+ vfree(adapter->eswitch);
if (qlcnic_82xx_check(adapter))
qlcnic_clr_all_drv_state(adapter, 0);
--
1.8.1.4
^ permalink raw reply related
* [PATCH net-next 3/6] qlcnic: Enhance ethtool Statistics for Multiple Tx queue.
From: Himanshu Madhani @ 2013-11-01 20:51 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept_NX_Linux_NIC_Driver, Himanshu Madhani
In-Reply-To: <cover.1383358893.git.himanshu.madhani@qlogic.com>
From: Himanshu Madhani <himanshu.madhani@qlogic.com>
o Enhance ethtool statistics to display multiple Tx queue stats for
all supported adapters.
Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 13 ++-
.../net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 96 +++++++++++++++-------
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c | 14 ++--
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 33 ++++----
4 files changed, 94 insertions(+), 62 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index db83edf..2d818c1 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -533,6 +533,14 @@ struct qlcnic_host_sds_ring {
char name[IFNAMSIZ + 12];
} ____cacheline_internodealigned_in_smp;
+struct qlcnic_tx_queue_stats {
+ u64 xmit_on;
+ u64 xmit_off;
+ u64 xmit_called;
+ u64 xmit_finished;
+ u64 tx_bytes;
+};
+
struct qlcnic_host_tx_ring {
int irq;
void __iomem *crb_intr_mask;
@@ -544,10 +552,7 @@ struct qlcnic_host_tx_ring {
u32 sw_consumer;
u32 num_desc;
- u64 xmit_on;
- u64 xmit_off;
- u64 xmit_called;
- u64 xmit_finished;
+ struct qlcnic_tx_queue_stats tx_stats;
void __iomem *crb_cmd_producer;
struct cmd_desc_type0 *desc_head;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index 0aaa52b..9b6dd98 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -27,43 +27,36 @@ static const u32 qlcnic_fw_dump_level[] = {
};
static const struct qlcnic_stats qlcnic_gstrings_stats[] = {
+ {"xmit_on", QLC_SIZEOF(stats.xmit_on), QLC_OFF(stats.xmit_on)},
+ {"xmit_off", QLC_SIZEOF(stats.xmit_off), QLC_OFF(stats.xmit_off)},
{"xmit_called", QLC_SIZEOF(stats.xmitcalled),
- QLC_OFF(stats.xmitcalled)},
+ QLC_OFF(stats.xmitcalled)},
{"xmit_finished", QLC_SIZEOF(stats.xmitfinished),
- QLC_OFF(stats.xmitfinished)},
- {"rx_dropped", QLC_SIZEOF(stats.rxdropped), QLC_OFF(stats.rxdropped)},
+ QLC_OFF(stats.xmitfinished)},
+ {"tx dma map error", QLC_SIZEOF(stats.tx_dma_map_error),
+ QLC_OFF(stats.tx_dma_map_error)},
+ {"tx_bytes", QLC_SIZEOF(stats.txbytes), QLC_OFF(stats.txbytes)},
{"tx_dropped", QLC_SIZEOF(stats.txdropped), QLC_OFF(stats.txdropped)},
- {"csummed", QLC_SIZEOF(stats.csummed), QLC_OFF(stats.csummed)},
+ {"rx dma map error", QLC_SIZEOF(stats.rx_dma_map_error),
+ QLC_OFF(stats.rx_dma_map_error)},
{"rx_pkts", QLC_SIZEOF(stats.rx_pkts), QLC_OFF(stats.rx_pkts)},
- {"lro_pkts", QLC_SIZEOF(stats.lro_pkts), QLC_OFF(stats.lro_pkts)},
{"rx_bytes", QLC_SIZEOF(stats.rxbytes), QLC_OFF(stats.rxbytes)},
- {"tx_bytes", QLC_SIZEOF(stats.txbytes), QLC_OFF(stats.txbytes)},
+ {"rx_dropped", QLC_SIZEOF(stats.rxdropped), QLC_OFF(stats.rxdropped)},
+ {"null rxbuf", QLC_SIZEOF(stats.null_rxbuf), QLC_OFF(stats.null_rxbuf)},
+ {"csummed", QLC_SIZEOF(stats.csummed), QLC_OFF(stats.csummed)},
+ {"lro_pkts", QLC_SIZEOF(stats.lro_pkts), QLC_OFF(stats.lro_pkts)},
{"lrobytes", QLC_SIZEOF(stats.lrobytes), QLC_OFF(stats.lrobytes)},
{"lso_frames", QLC_SIZEOF(stats.lso_frames), QLC_OFF(stats.lso_frames)},
- {"xmit_on", QLC_SIZEOF(stats.xmit_on), QLC_OFF(stats.xmit_on)},
- {"xmit_off", QLC_SIZEOF(stats.xmit_off), QLC_OFF(stats.xmit_off)},
{"skb_alloc_failure", QLC_SIZEOF(stats.skb_alloc_failure),
QLC_OFF(stats.skb_alloc_failure)},
- {"null rxbuf", QLC_SIZEOF(stats.null_rxbuf), QLC_OFF(stats.null_rxbuf)},
- {"rx dma map error", QLC_SIZEOF(stats.rx_dma_map_error),
- QLC_OFF(stats.rx_dma_map_error)},
- {"tx dma map error", QLC_SIZEOF(stats.tx_dma_map_error),
- QLC_OFF(stats.tx_dma_map_error)},
{"mac_filter_limit_overrun", QLC_SIZEOF(stats.mac_filter_limit_overrun),
- QLC_OFF(stats.mac_filter_limit_overrun)},
+ QLC_OFF(stats.mac_filter_limit_overrun)},
{"spurious intr", QLC_SIZEOF(stats.spurious_intr),
QLC_OFF(stats.spurious_intr)},
};
static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = {
- "rx unicast frames",
- "rx multicast frames",
- "rx broadcast frames",
- "rx dropped frames",
- "rx errors",
- "rx local frames",
- "rx numbytes",
"tx unicast frames",
"tx multicast frames",
"tx broadcast frames",
@@ -71,6 +64,13 @@ static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = {
"tx errors",
"tx local frames",
"tx numbytes",
+ "rx unicast frames",
+ "rx multicast frames",
+ "rx broadcast frames",
+ "rx dropped frames",
+ "rx errors",
+ "rx local frames",
+ "rx numbytes",
};
static const char qlcnic_83xx_tx_stats_strings[][ETH_GSTRING_LEN] = {
@@ -126,13 +126,16 @@ static const char qlcnic_83xx_mac_stats_strings[][ETH_GSTRING_LEN] = {
#define QLCNIC_STATS_LEN ARRAY_SIZE(qlcnic_gstrings_stats)
-static const char qlcnic_tx_ring_stats_strings[][ETH_GSTRING_LEN] = {
+static const char qlcnic_tx_queue_stats_strings[][ETH_GSTRING_LEN] = {
"xmit_on",
"xmit_off",
"xmit_called",
"xmit_finished",
+ "tx_bytes",
};
+#define QLCNIC_TX_STATS_LEN ARRAY_SIZE(qlcnic_tx_queue_stats_strings)
+
static const char qlcnic_83xx_rx_stats_strings[][ETH_GSTRING_LEN] = {
"ctx_rx_bytes",
"ctx_rx_pkts",
@@ -1123,11 +1126,11 @@ qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 *data)
QLCNIC_TEST_LEN * ETH_GSTRING_LEN);
break;
case ETH_SS_STATS:
- num_stats = ARRAY_SIZE(qlcnic_tx_ring_stats_strings);
+ num_stats = ARRAY_SIZE(qlcnic_tx_queue_stats_strings);
for (i = 0; i < adapter->max_drv_tx_rings; i++) {
for (index = 0; index < num_stats; index++) {
- sprintf(data, "tx_ring_%d %s", i,
- qlcnic_tx_ring_stats_strings[index]);
+ sprintf(data, "tx_queue_%d %s", i,
+ qlcnic_tx_queue_stats_strings[index]);
data += ETH_GSTRING_LEN;
}
}
@@ -1225,6 +1228,36 @@ static u64 *qlcnic_fill_stats(u64 *data, void *stats, int type)
return data;
}
+static void qlcnic_update_stats(struct qlcnic_adapter *adapter)
+{
+ struct qlcnic_host_tx_ring *tx_ring;
+ int ring;
+
+ for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ tx_ring = &adapter->tx_ring[ring];
+ adapter->stats.xmit_on += tx_ring->tx_stats.xmit_on;
+ adapter->stats.xmit_off += tx_ring->tx_stats.xmit_off;
+ adapter->stats.xmitcalled += tx_ring->tx_stats.xmit_called;
+ adapter->stats.xmitfinished += tx_ring->tx_stats.xmit_finished;
+ adapter->stats.txbytes += tx_ring->tx_stats.tx_bytes;
+ }
+}
+
+static u64 *qlcnic_fill_tx_queue_stats(u64 *data, void *stats)
+{
+ struct qlcnic_host_tx_ring *tx_ring;
+
+ tx_ring = (struct qlcnic_host_tx_ring *)stats;
+
+ *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_on);
+ *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_off);
+ *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_called);
+ *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_finished);
+ *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.tx_bytes);
+
+ return data;
+}
+
static void qlcnic_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
{
@@ -1232,19 +1265,20 @@ static void qlcnic_get_ethtool_stats(struct net_device *dev,
struct qlcnic_host_tx_ring *tx_ring;
struct qlcnic_esw_statistics port_stats;
struct qlcnic_mac_statistics mac_stats;
- int index, ret, length, size, ring;
+ int index, ret, length, size, tx_size, ring;
char *p;
- memset(data, 0, adapter->max_drv_tx_rings * 4 * sizeof(u64));
+ tx_size = adapter->max_drv_tx_rings * QLCNIC_TX_STATS_LEN;
+
+ memset(data, 0, tx_size * sizeof(u64));
for (ring = 0, index = 0; ring < adapter->max_drv_tx_rings; ring++) {
if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
tx_ring = &adapter->tx_ring[ring];
- *data++ = tx_ring->xmit_on;
- *data++ = tx_ring->xmit_off;
- *data++ = tx_ring->xmit_called;
- *data++ = tx_ring->xmit_finished;
+ data = qlcnic_fill_tx_queue_stats(data, tx_ring);
+ qlcnic_update_stats(adapter);
}
}
+
memset(data, 0, stats->n_stats * sizeof(u64));
length = QLCNIC_STATS_LEN;
for (index = 0; index < length; index++) {
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
index 897627d..df31be7 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
@@ -607,8 +607,7 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) {
netif_tx_start_queue(tx_ring->txq);
} else {
- adapter->stats.xmit_off++;
- tx_ring->xmit_off++;
+ tx_ring->tx_stats.xmit_off++;
return NETDEV_TX_BUSY;
}
}
@@ -669,9 +668,8 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
if (adapter->drv_mac_learn)
qlcnic_send_filter(adapter, first_desc, skb);
- adapter->stats.txbytes += skb->len;
- adapter->stats.xmitcalled++;
- tx_ring->xmit_called++;
+ tx_ring->tx_stats.tx_bytes += skb->len;
+ tx_ring->tx_stats.xmit_called++;
qlcnic_update_cmd_producer(tx_ring);
@@ -805,8 +803,7 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter,
PCI_DMA_TODEVICE);
frag->dma = 0ULL;
}
- adapter->stats.xmitfinished++;
- tx_ring->xmit_finished++;
+ tx_ring->tx_stats.xmit_finished++;
dev_kfree_skb_any(buffer->skb);
buffer->skb = NULL;
}
@@ -823,8 +820,7 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter,
netif_carrier_ok(netdev)) {
if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) {
netif_tx_wake_queue(tx_ring->txq);
- adapter->stats.xmit_on++;
- tx_ring->xmit_on++;
+ tx_ring->tx_stats.xmit_on++;
}
}
adapter->tx_timeo_cnt = 0;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 8d4536d..1215d2a 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -2719,24 +2719,21 @@ static void qlcnic_tx_timeout(struct net_device *netdev)
QLCNIC_FORCE_FW_DUMP_KEY);
} else {
netdev_info(netdev, "Tx timeout, reset adapter context.\n");
- if (qlcnic_82xx_check(adapter)) {
- for (ring = 0; ring < adapter->max_drv_tx_rings;
- ring++) {
- tx_ring = &adapter->tx_ring[ring];
- dev_info(&netdev->dev, "ring=%d\n", ring);
- dev_info(&netdev->dev, "crb_intr_mask=%d\n",
- readl(tx_ring->crb_intr_mask));
- dev_info(&netdev->dev, "producer=%d\n",
- readl(tx_ring->crb_cmd_producer));
- dev_info(&netdev->dev, "sw_consumer = %d\n",
- tx_ring->sw_consumer);
- dev_info(&netdev->dev, "hw_consumer = %d\n",
- le32_to_cpu(*(tx_ring->hw_consumer)));
- dev_info(&netdev->dev, "xmit-on=%llu\n",
- tx_ring->xmit_on);
- dev_info(&netdev->dev, "xmit-off=%llu\n",
- tx_ring->xmit_off);
- }
+ for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {
+ tx_ring = &adapter->tx_ring[ring];
+ netdev_info(netdev, "Tx ring=%d\n", ring);
+ netdev_info(netdev,
+ "crb_intr_mask=%d, producer=%d, sw_consumer=%d, hw_consumer=%d\n",
+ readl(tx_ring->crb_intr_mask),
+ readl(tx_ring->crb_cmd_producer),
+ tx_ring->sw_consumer,
+ le32_to_cpu(*(tx_ring->hw_consumer)));
+ netdev_info(netdev,
+ "xmit_finished=%llu, xmit_called=%llu, xmit_on=%llu, xmit_off=%llu\n",
+ tx_ring->tx_stats.xmit_finished,
+ tx_ring->tx_stats.xmit_called,
+ tx_ring->tx_stats.xmit_on,
+ tx_ring->tx_stats.xmit_off);
}
adapter->ahw->reset_context = 1;
}
--
1.8.1.4
^ permalink raw reply related
* [PATCH net-next 1/6] qlcnic: Register netdev in FAILED state for 83xx/84xx
From: Himanshu Madhani @ 2013-11-01 20:51 UTC (permalink / raw)
To: davem
Cc: netdev, Dept_NX_Linux_NIC_Driver, Sucheta Chakraborty,
himanshu.madhani
In-Reply-To: <cover.1383358893.git.himanshu.madhani@qlogic.com>
From: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
o Without failing probe, register netdev when device is in FAILED state.
o Device will come up with minimum functionality and allow diagnostics and
repair of the adapter.
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 1 +
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 32 ++++++++++++++-----
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c | 16 ++++++----
.../net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 6 ++--
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 37 +++++++++++++++++-----
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c | 8 ++---
6 files changed, 67 insertions(+), 33 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 0c2405d..db83edf 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -961,6 +961,7 @@ struct qlcnic_ipaddr {
#define __QLCNIC_SRIOV_CAPABLE 11
#define __QLCNIC_MBX_POLL_ENABLE 12
#define __QLCNIC_DIAG_MODE 13
+#define __QLCNIC_MAINTENANCE_MODE 16
#define QLCNIC_INTERRUPT_TEST 1
#define QLCNIC_LOOPBACK_TEST 2
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index a126bdf..f81e2dd 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -325,7 +325,8 @@ inline void qlcnic_83xx_clear_legacy_intr_mask(struct qlcnic_adapter *adapter)
inline void qlcnic_83xx_set_legacy_intr_mask(struct qlcnic_adapter *adapter)
{
- writel(1, adapter->tgt_mask_reg);
+ if (adapter->tgt_mask_reg)
+ writel(1, adapter->tgt_mask_reg);
}
/* Enable MSI-x and INT-x interrupts */
@@ -498,8 +499,11 @@ void qlcnic_83xx_free_mbx_intr(struct qlcnic_adapter *adapter)
num_msix = 0;
msleep(20);
- synchronize_irq(adapter->msix_entries[num_msix].vector);
- free_irq(adapter->msix_entries[num_msix].vector, adapter);
+
+ if (adapter->msix_entries) {
+ synchronize_irq(adapter->msix_entries[num_msix].vector);
+ free_irq(adapter->msix_entries[num_msix].vector, adapter);
+ }
}
int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter)
@@ -760,6 +764,9 @@ int qlcnic_83xx_issue_cmd(struct qlcnic_adapter *adapter,
int cmd_type, err, opcode;
unsigned long timeout;
+ if (!mbx)
+ return -EIO;
+
opcode = LSW(cmd->req.arg[0]);
cmd_type = cmd->type;
err = mbx->ops->enqueue_cmd(adapter, cmd, &timeout);
@@ -3049,11 +3056,14 @@ int qlcnic_83xx_get_settings(struct qlcnic_adapter *adapter,
int status = 0;
struct qlcnic_hardware_context *ahw = adapter->ahw;
- /* Get port configuration info */
- status = qlcnic_83xx_get_port_info(adapter);
- /* Get Link Status related info */
- config = qlcnic_83xx_test_link(adapter);
- ahw->module_type = QLC_83XX_SFP_MODULE_TYPE(config);
+ if (!test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) {
+ /* Get port configuration info */
+ status = qlcnic_83xx_get_port_info(adapter);
+ /* Get Link Status related info */
+ config = qlcnic_83xx_test_link(adapter);
+ ahw->module_type = QLC_83XX_SFP_MODULE_TYPE(config);
+ }
+
/* hard code until there is a way to get it from flash */
ahw->board_type = QLCNIC_BRDTYPE_83XX_10G;
@@ -3530,6 +3540,9 @@ void qlcnic_83xx_reinit_mbx_work(struct qlcnic_mailbox *mbx)
void qlcnic_83xx_free_mailbox(struct qlcnic_mailbox *mbx)
{
+ if (!mbx)
+ return;
+
destroy_workqueue(mbx->work_q);
kfree(mbx);
}
@@ -3650,6 +3663,9 @@ void qlcnic_83xx_detach_mailbox_work(struct qlcnic_adapter *adapter)
{
struct qlcnic_mailbox *mbx = adapter->ahw->mailbox;
+ if (!mbx)
+ return;
+
clear_bit(QLC_83XX_MBX_READY, &mbx->status);
complete(&mbx->completion);
cancel_work_sync(&mbx->work);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
index e2cd484..4a8a3f1 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
@@ -2190,20 +2190,24 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)
return err;
}
+ if (qlcnic_83xx_read_flash_descriptor_table(adapter) ||
+ qlcnic_83xx_read_flash_mfg_id(adapter)) {
+ dev_err(&adapter->pdev->dev, "Failed reading flash mfg id\n");
+ err = -ENOTRECOVERABLE;
+ goto detach_mbx;
+ }
+
err = qlcnic_83xx_check_hw_status(adapter);
if (err)
goto detach_mbx;
- if (!qlcnic_83xx_read_flash_descriptor_table(adapter))
- qlcnic_83xx_read_flash_mfg_id(adapter);
-
err = qlcnic_83xx_get_fw_info(adapter);
if (err)
goto detach_mbx;
err = qlcnic_83xx_idc_init(adapter);
if (err)
- goto clear_fw_info;
+ goto detach_mbx;
err = qlcnic_setup_intr(adapter, 0, 0);
if (err) {
@@ -2247,12 +2251,10 @@ disable_mbx_intr:
disable_intr:
qlcnic_teardown_intr(adapter);
-clear_fw_info:
- kfree(ahw->fw_info);
-
detach_mbx:
qlcnic_83xx_detach_mailbox_work(adapter);
qlcnic_83xx_free_mailbox(ahw->mailbox);
+ ahw->mailbox = NULL;
exit:
return err;
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index b2a8805..0aaa52b 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -1685,7 +1685,6 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val)
struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
bool valid_mask = false;
int i, ret = 0;
- u32 state;
switch (val->flag) {
case QLCNIC_FORCE_FW_DUMP_KEY:
@@ -1738,9 +1737,8 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val)
case QLCNIC_SET_QUIESCENT:
case QLCNIC_RESET_QUIESCENT:
- state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE);
- if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD))
- netdev_info(netdev, "Device in FAILED state\n");
+ if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state))
+ netdev_info(netdev, "Device is in non-operational state\n");
break;
default:
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index dcf4a4e..46ddbd8 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -2305,10 +2305,23 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
qlcnic_83xx_check_vf(adapter, ent);
adapter->portnum = adapter->ahw->pci_func;
err = qlcnic_83xx_init(adapter, pci_using_dac);
+
if (err) {
- dev_err(&pdev->dev, "%s: failed\n", __func__);
- goto err_out_free_hw;
+ switch (err) {
+ case -ENOTRECOVERABLE:
+ dev_err(&pdev->dev, "Adapter initialization failed due to a faulty hardware. Please reboot\n");
+ dev_err(&pdev->dev, "If reboot doesn't help, please replace the adapter with new one and return the faulty adapter for repair\n");
+ goto err_out_free_hw;
+ case -ENOMEM:
+ dev_err(&pdev->dev, "Adapter initialization failed. Please reboot\n");
+ goto err_out_free_hw;
+ default:
+ dev_err(&pdev->dev, "Adapter initialization failed. A reboot may be required to recover from this failure\n");
+ dev_err(&pdev->dev, "If reboot does not help to recover from this failure, try a flash update of the adapter\n");
+ goto err_out_maintenance_mode;
+ }
}
+
if (qlcnic_sriov_vf_check(adapter))
return 0;
} else {
@@ -2410,8 +2423,16 @@ err_out_disable_pdev:
return err;
err_out_maintenance_mode:
+ set_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state);
netdev->netdev_ops = &qlcnic_netdev_failed_ops;
SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_failed_ops);
+ ahw->port_type = QLCNIC_XGBE;
+
+ if (qlcnic_83xx_check(adapter))
+ adapter->tgt_status_reg = NULL;
+ else
+ ahw->board_type = QLCNIC_BRDTYPE_P3P_10G_SFP_PLUS;
+
err = register_netdev(netdev);
if (err) {
@@ -2533,12 +2554,11 @@ static int qlcnic_resume(struct pci_dev *pdev)
static int qlcnic_open(struct net_device *netdev)
{
struct qlcnic_adapter *adapter = netdev_priv(netdev);
- u32 state;
int err;
- state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE);
- if (state == QLCNIC_DEV_FAILED || state == QLCNIC_DEV_BADBAD) {
- netdev_err(netdev, "%s: Device is in FAILED state\n", __func__);
+ if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) {
+ netdev_err(netdev, "%s: Device is in non-operational state\n",
+ __func__);
return -EIO;
}
@@ -3251,8 +3271,9 @@ void qlcnic_82xx_dev_request_reset(struct qlcnic_adapter *adapter, u32 key)
return;
state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE);
- if (state == QLCNIC_DEV_FAILED || state == QLCNIC_DEV_BADBAD) {
- netdev_err(adapter->netdev, "%s: Device is in FAILED state\n",
+
+ if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) {
+ netdev_err(adapter->netdev, "%s: Device is in non-operational state\n",
__func__);
qlcnic_api_unlock(adapter);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
index 019f437..e8b1ce6 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
@@ -1272,7 +1272,6 @@ void qlcnic_remove_sysfs_entries(struct qlcnic_adapter *adapter)
void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter)
{
struct device *dev = &adapter->pdev->dev;
- u32 state;
if (device_create_bin_file(dev, &bin_attr_port_stats))
dev_info(dev, "failed to create port stats sysfs entry");
@@ -1286,8 +1285,7 @@ void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter)
if (device_create_bin_file(dev, &bin_attr_mem))
dev_info(dev, "failed to create mem sysfs entry\n");
- state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE);
- if (state == QLCNIC_DEV_FAILED || state == QLCNIC_DEV_BADBAD)
+ if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state))
return;
if (device_create_bin_file(dev, &bin_attr_pci_config))
@@ -1313,7 +1311,6 @@ void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter)
void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter)
{
struct device *dev = &adapter->pdev->dev;
- u32 state;
device_remove_bin_file(dev, &bin_attr_port_stats);
@@ -1323,8 +1320,7 @@ void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter)
device_remove_bin_file(dev, &bin_attr_crb);
device_remove_bin_file(dev, &bin_attr_mem);
- state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE);
- if (state == QLCNIC_DEV_FAILED || state == QLCNIC_DEV_BADBAD)
+ if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state))
return;
device_remove_bin_file(dev, &bin_attr_pci_config);
--
1.8.1.4
^ permalink raw reply related
* [PATCH net-next 0/6] qlcnic: Multiple Tx queue support and code refactoring
From: Himanshu Madhani @ 2013-11-01 20:51 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept_NX_Linux_NIC_Driver, Himanshu Madhani
From: Himanshu Madhani <himanshu.madhani@qlogic.com>
This Patch series contains following changes
o Refactored code for Tx/SDS ring calculation, validation for various modes.
o Enhanced ethtool statistics for multi Tx queue on all supported adapters.
o Enable multiple Tx queue for 83xx and 84xx Series adapters.
o Regiser netdev for failed device state.
o Replaced inapropriate usage of kzalloc() with vzalloc().
Please apply to net-next.
Thanks,
Himanshu
Himanshu Madhani (4):
qlcnic: Enhance ethtool Statistics for Multiple Tx queue.
qlcnic: refactor Tx/SDS ring calculation and validation in driver.
qlcnic: Enable multiple Tx queue support for 83xx/84xx Series
adapters.
qlcnic: update version to 5.3.52
Manish Chopra (1):
qlcnic: Replace kzalloc() with vzalloc().
Sucheta Chakraborty (1):
qlcnic: Register netdev in FAILED state for 83xx/84xx
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 84 +++--
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 109 +++---
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h | 13 +-
.../net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c | 70 +++-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 26 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | 12 +-
.../net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 216 +++++++----
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | 8 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c | 2 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c | 65 ++--
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 410 +++++++++++----------
.../ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 6 +-
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c | 12 +-
13 files changed, 596 insertions(+), 437 deletions(-)
--
1.8.1.4
^ permalink raw reply
* [PATCH net-next 6/6] qlcnic: update version to 5.3.52
From: Himanshu Madhani @ 2013-11-01 20:51 UTC (permalink / raw)
To: davem; +Cc: netdev, Dept_NX_Linux_NIC_Driver, Himanshu Madhani
In-Reply-To: <cover.1383358893.git.himanshu.madhani@qlogic.com>
From: Himanshu Madhani <himanshu.madhani@qlogic.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com>
---
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 3109730..631ea0a 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -38,8 +38,8 @@
#define _QLCNIC_LINUX_MAJOR 5
#define _QLCNIC_LINUX_MINOR 3
-#define _QLCNIC_LINUX_SUBVERSION 51
-#define QLCNIC_LINUX_VERSIONID "5.3.51"
+#define _QLCNIC_LINUX_SUBVERSION 52
+#define QLCNIC_LINUX_VERSIONID "5.3.52"
#define QLCNIC_DRV_IDC_VER 0x01
#define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\
(_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
--
1.8.1.4
^ permalink raw reply related
* [PATCH v3 net-next] net: pkt_sched: PIE AQM scheme
From: Vijay Subramanian @ 2013-11-01 20:45 UTC (permalink / raw)
To: netdev
Cc: davem, shemminger, eric.dumazet, Vijay Subramanian,
Mythili Prabhu, Dave Taht
From: Vijay Subramanian <vijaynsu@cisco.com>
Proportional Integral controller Enhanced (PIE) is a scheduler to address the
bufferbloat problem.
>From the IETF draft below:
"Bufferbloat is a phenomenon where excess buffers in the network cause high
latency and jitter. As more and more interactive applications (e.g. voice over
IP, real time video streaming and financial transactions) run in the Internet,
high latency and jitter degrade application performance. There is a pressing
need to design intelligent queue management schemes that can control latency
and jitter; and hence provide desirable quality of service to users.
We present here a lightweight design, PIE(Proportional Integral controller
Enhanced) that can effectively control the average queueing latency to a target
value. Simulation results, theoretical analysis and Linux testbed results have
shown that PIE can ensure low latency and achieve high link utilization under
various congestion situations. The design does not require per-packet
timestamp, so it incurs very small overhead and is simple enough to implement
in both hardware and software."
Many thanks to Dave Taht for extensive feedback, reviews, testing and
suggestions. Thanks also to Stephen Hemminger for initial review and suggestion
to use psched and friends. Naeem Khademi and Dave Taht independently
contributed to ECN support.
For more information, please see technical paper about PIE in the IEEE
Conference on High Performance Switching and Routing 2013. A copy of the paper
can be found at ftp://ftpeng.cisco.com/pie/.
Please also refer to the IETF draft submission at
http://tools.ietf.org/html/draft-pan-tsvwg-pie-00
All relevant code, documents and test scripts and results can be found at
ftp://ftpeng.cisco.com/pie/.
For problems with the iproute2/tc or Linux kernel code, please contact Vijay
Subramanian (vijaynsu@cisco.com or subramanian.vijay@gmail.com) or Mythili
Prabhu (mysuryan@cisco.com)
Signed-off-by: Vijay Subramanian <subramanian.vijay@gmail.com>
Signed-off-by: Mythili Prabhu <mysuryan@cisco.com>
CC: Dave Taht <dave.taht@bufferbloat.net>
---
Changes from V2: Prevent timer deadlock on qdisc destroy. Also maintain timer
period 'tupdate' in us throughout instead of in psched units.
Changes from V1: Addressed review comments regarding coding style and various
implementation issues. In particular 1) add locking for the timer and 2) use
psched timer directly instead of a custom timer based on psched.
include/uapi/linux/pkt_sched.h | 26 ++
net/sched/Kconfig | 13 +
net/sched/Makefile | 1 +
net/sched/sch_pie.c | 558 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 598 insertions(+)
create mode 100644 net/sched/sch_pie.c
diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
index f2624b5..2fb6e6d 100644
--- a/include/uapi/linux/pkt_sched.h
+++ b/include/uapi/linux/pkt_sched.h
@@ -787,4 +787,30 @@ struct tc_fq_qd_stats {
__u32 throttled_flows;
__u32 pad;
};
+
+/*PIE*/
+enum {
+ TCA_PIE_UNSPEC,
+ TCA_PIE_TARGET,
+ TCA_PIE_LIMIT,
+ TCA_PIE_TUPDATE,
+ TCA_PIE_ALPHA,
+ TCA_PIE_BETA,
+ TCA_PIE_ECN,
+ TCA_PIE_BYTEMODE,
+ __TCA_PIE_MAX
+};
+#define TCA_PIE_MAX (__TCA_PIE_MAX - 1)
+
+struct tc_pie_xstats {
+ __u32 prob; /* current probability */
+ __u32 delay; /* current delay in ms */
+ __u32 avg_dq_rate; /* current average dq_rate in bits/pie_time */
+ __u32 packets_in; /* total number of packets enqueued */
+ __u32 dropped; /* packets dropped due to pie_action */
+ __u32 overlimit; /* dropped due to lack of space in queue */
+ __u32 maxq; /* maximum queue size */
+ __u32 ecn_mark; /* packets marked with ecn*/
+};
+
#endif
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index c03a32a..a079e03 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -286,6 +286,19 @@ config NET_SCH_FQ
If unsure, say N.
+config NET_SCH_PIE
+ tristate "Proportional Integral controller Enhanced (PIE) scheduler"
+ help
+ Say Y here if you want to use the Proportional Integral controller
+ Enhanced) scheduler packet scheduling algorithm.
+ For more information, please see
+ http://tools.ietf.org/html/draft-pan-tsvwg-pie-00
+
+ To compile this driver as a module, choose M here: the module
+ will be called sch_pie.
+
+ If unsure, say N.
+
config NET_SCH_INGRESS
tristate "Ingress Qdisc"
depends on NET_CLS_ACT
diff --git a/net/sched/Makefile b/net/sched/Makefile
index e5f9abe..5b4ece9 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_NET_SCH_QFQ) += sch_qfq.o
obj-$(CONFIG_NET_SCH_CODEL) += sch_codel.o
obj-$(CONFIG_NET_SCH_FQ_CODEL) += sch_fq_codel.o
obj-$(CONFIG_NET_SCH_FQ) += sch_fq.o
+obj-$(CONFIG_NET_SCH_PIE) += sch_pie.o
obj-$(CONFIG_NET_CLS_U32) += cls_u32.o
obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o
diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c
new file mode 100644
index 0000000..fc8fc45
--- /dev/null
+++ b/net/sched/sch_pie.c
@@ -0,0 +1,558 @@
+/* Copyright (C) 2013 Cisco Systems, Inc, 2013.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * Author: Vijay Subramanian <vijaynsu@cisco.com>
+ * Author: Mythili Prabhu <mysuryan@cisco.com>
+ *
+ * ECN support is added by Naeem Khademi <naeemk@ifi.uio.no>
+ * University of Oslo, Norway.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <net/pkt_sched.h>
+#include <net/inet_ecn.h>
+
+#define QUEUE_THRESHOLD 5000
+#define DQCOUNT_INVALID -1
+#define MAX_PROB 0xffffffff
+#define PIE_SCALE 8
+
+/* parameters used */
+struct pie_params {
+ psched_time_t target; /* user specified target delay in pschedtime */
+ u32 tupdate; /* frequency with which the timer fires (us) */
+ u32 limit; /* number of packets that can be enqueued */
+ u32 alpha; /* alpha and beta are between -4 and 4 */
+ u32 beta; /* and are used for shift relative to 1 */
+ bool ecn; /* true if ecn is enabled */
+ bool bytemode; /* to scale drop early prob based on pkt size */
+};
+
+/* variables used */
+struct pie_vars {
+ u32 prob; /* probability but scaled by u32 limit. */
+ psched_time_t burst_time;
+ psched_time_t qdelay;
+ psched_time_t qdelay_old;
+ u64 dq_count; /* measured in bytes */
+ psched_time_t dq_tstamp; /* drain rate */
+ u32 avg_dq_rate; /* bytes per pschedtime tick,scaled */
+ u32 qlen_old; /* in bytes */
+};
+
+/* statistics gathering*/
+struct pie_stats {
+ u32 packets_in; /* total number of packets enqueued */
+ u32 dropped; /* packets dropped due to pie_action */
+ u32 overlimit; /* dropped due to lack of space in queue */
+ u32 maxq; /* maximum queue size */
+ u32 ecn_mark; /* packets marked with ECN */
+};
+
+/* private data for the Qdisc */
+struct pie_sched_data {
+ struct pie_params params;
+ struct pie_vars vars;
+ struct pie_stats stats;
+ struct timer_list adapt_timer;
+};
+
+static void pie_params_init(struct pie_params *params)
+{
+ params->alpha = 2;
+ params->beta = 20;
+ params->tupdate = 30 * USEC_PER_MSEC; /* 30 ms */
+ params->limit = 200; /* default of 200 packets */
+ params->target = PSCHED_NS2TICKS(20 * NSEC_PER_MSEC); /* 20 ms */
+ params->ecn = false;
+ params->bytemode = false;
+}
+
+static void pie_vars_init(struct pie_vars *vars)
+{
+ vars->dq_count = DQCOUNT_INVALID;
+ vars->avg_dq_rate = 0;
+ /* default of 100 ms in pschedtime */
+ vars->burst_time = PSCHED_NS2TICKS(100 * NSEC_PER_MSEC);
+}
+
+static bool drop_early(struct Qdisc *sch, u32 packet_size)
+{
+ struct pie_sched_data *q = qdisc_priv(sch);
+ u32 rnd;
+ u32 local_prob = q->vars.prob;
+ u32 mtu = psched_mtu(qdisc_dev(sch));
+
+ /* If there is still burst allowance left skip random early drop */
+ if (q->vars.burst_time > 0)
+ return false;
+
+ /* If current delay is less than half of target, and
+ * if drop prob is low already, disable early_drop
+ */
+ if ((q->vars.qdelay < q->params.target / 2)
+ && (q->vars.prob < MAX_PROB / 5))
+ return false;
+
+ /* If we have fewer than 2 mtu-sized packets, disable drop_early,
+ * similar to min_th in RED
+ */
+ if (sch->qstats.backlog < 2 * mtu)
+ return false;
+
+ /* If bytemode is turned on, use packet size to compute new
+ * probablity. Smaller packets will have lower drop prob in this case
+ */
+ if (q->params.bytemode && packet_size <= mtu)
+ local_prob = (local_prob / mtu) * packet_size;
+ else
+ local_prob = q->vars.prob;
+
+ rnd = net_random();
+ if (rnd < local_prob)
+ return true;
+
+ return false;
+}
+
+static int pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+{
+ struct pie_sched_data *q = qdisc_priv(sch);
+
+ if (unlikely(qdisc_qlen(sch) >= sch->limit))
+ goto out;
+
+ if (!drop_early(sch, skb->len)) {
+ /* we can enqueue the packet */
+ q->stats.packets_in++;
+
+ if (qdisc_qlen(sch) > q->stats.maxq)
+ q->stats.maxq = qdisc_qlen(sch);
+
+ return qdisc_enqueue_tail(skb, sch);
+ } else if (q->params.ecn && INET_ECN_set_ce(skb) &&
+ (q->vars.prob <= MAX_PROB / 10)) {
+ /* If packet is ecn capable, mark it if drop probability
+ * is lower than 10%, else drop it.
+ */
+ q->stats.ecn_mark++;
+ return qdisc_enqueue_tail(skb, sch);
+ }
+out:
+ q->stats.overlimit++;
+ return qdisc_drop(skb, sch);
+}
+
+static const struct nla_policy pie_policy[TCA_PIE_MAX + 1] = {
+ [TCA_PIE_TARGET] = {.type = NLA_U32},
+ [TCA_PIE_LIMIT] = {.type = NLA_U32},
+ [TCA_PIE_TUPDATE] = {.type = NLA_U32},
+ [TCA_PIE_ALPHA] = {.type = NLA_U32},
+ [TCA_PIE_BETA] = {.type = NLA_U32},
+ [TCA_PIE_ECN] = {.type = NLA_U32},
+ [TCA_PIE_BYTEMODE] = {.type = NLA_U32},
+};
+
+static int pie_change(struct Qdisc *sch, struct nlattr *opt)
+{
+ struct pie_sched_data *q = qdisc_priv(sch);
+ struct nlattr *tb[TCA_PIE_MAX + 1];
+ unsigned int qlen;
+ int err;
+
+ if (!opt)
+ return -EINVAL;
+
+ err = nla_parse_nested(tb, TCA_PIE_MAX, opt, pie_policy);
+ if (err < 0)
+ return err;
+
+ sch_tree_lock(sch);
+
+ /* convert from microseconds to pschedtime */
+ if (tb[TCA_PIE_TARGET]) {
+ /* target is in us */
+ u32 target = nla_get_u32(tb[TCA_PIE_TARGET]);
+ /* convert to pschedtime */
+ q->params.target = PSCHED_NS2TICKS((u64)target * NSEC_PER_USEC);
+ }
+
+ /* tupdate is in us */
+ if (tb[TCA_PIE_TUPDATE])
+ q->params.tupdate = nla_get_u32(tb[TCA_PIE_TUPDATE]);
+
+ if (tb[TCA_PIE_LIMIT]) {
+ u32 limit = nla_get_u32(tb[TCA_PIE_LIMIT]);
+ q->params.limit = limit;
+ sch->limit = limit;
+ }
+
+ if (tb[TCA_PIE_ALPHA])
+ q->params.alpha = nla_get_u32(tb[TCA_PIE_ALPHA]);
+
+ if (tb[TCA_PIE_BETA])
+ q->params.beta = nla_get_u32(tb[TCA_PIE_BETA]);
+
+ if (tb[TCA_PIE_ECN])
+ q->params.ecn = nla_get_u32(tb[TCA_PIE_ECN]);
+
+ if (tb[TCA_PIE_BYTEMODE])
+ q->params.bytemode = nla_get_u32(tb[TCA_PIE_BYTEMODE]);
+
+ /* Drop excess packets if new limit is lower */
+ qlen = sch->q.qlen;
+ while (sch->q.qlen > sch->limit) {
+ struct sk_buff *skb = __skb_dequeue(&sch->q);
+
+ sch->qstats.backlog -= qdisc_pkt_len(skb);
+ qdisc_drop(skb, sch);
+ }
+ qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen);
+
+ sch_tree_unlock(sch);
+ return 0;
+}
+
+static void pie_process_dequeue(struct Qdisc *sch, struct sk_buff *skb)
+{
+
+ struct pie_sched_data *q = qdisc_priv(sch);
+ int qlen = sch->qstats.backlog; /* current queue size in bytes */
+
+ /* If current queue is about 10 packets or more and dq_count is unset
+ * we have enough packets to calculate the drain rate. Save
+ * current time as dq_tstamp and start measurement cycle.
+ */
+ if (qlen >= QUEUE_THRESHOLD && q->vars.dq_count == DQCOUNT_INVALID) {
+ q->vars.dq_tstamp = psched_get_time();
+ q->vars.dq_count = 0;
+ }
+
+ /* Calculate the average drain rate from this value. If queue length
+ * has receded to a small value viz., <= QUEUE_THRESHOLD bytes,reset
+ * the dq_count to -1 as we don't have enough packets to calculate the
+ * drain rate anymore The following if block is entered only when we
+ * have a substantial queue built up (QUEUE_THRESHOLD bytes or more)
+ * and we calculate the drain rate for the threshold here. dq_count is
+ * in bytes, time difference in psched_time, hence rate is in
+ * bytes/psched_time.
+ */
+ if (q->vars.dq_count != DQCOUNT_INVALID) {
+
+ q->vars.dq_count += skb->len;
+
+ if (q->vars.dq_count >= QUEUE_THRESHOLD) {
+ psched_time_t now = psched_get_time();
+ u32 dtime = now - q->vars.dq_tstamp;
+ u32 count = q->vars.dq_count << PIE_SCALE;
+
+ if (dtime == 0)
+ return;
+
+ count = count / dtime;
+
+ if (q->vars.avg_dq_rate == 0)
+ q->vars.avg_dq_rate = count;
+ else
+ q->vars.avg_dq_rate =
+ (q->vars.avg_dq_rate -
+ (q->vars.avg_dq_rate >> 3)) + (count >> 3);
+
+ /* If the queue has receded below the threshold, we hold
+ * on to the last drain rate calculated, else we reset
+ * dq_count to 0 to re-enter the if block when the next
+ * packet is dequeued
+ */
+ if (qlen < QUEUE_THRESHOLD)
+ q->vars.dq_count = DQCOUNT_INVALID;
+ else {
+ q->vars.dq_count = 0;
+ q->vars.dq_tstamp = psched_get_time();
+ }
+
+ if (q->vars.burst_time > 0) {
+ if (q->vars.burst_time > dtime)
+ q->vars.burst_time -= dtime;
+ else
+ q->vars.burst_time = 0;
+ }
+ }
+ }
+}
+
+static void calculate_probability(struct Qdisc *sch)
+{
+ struct pie_sched_data *q = qdisc_priv(sch);
+ u32 qlen = sch->qstats.backlog; /* queue size in bytes */
+ psched_time_t qdelay = 0; /* in pschedtime */
+ psched_time_t qdelay_old = q->vars.qdelay; /* in pschedtime */
+ s32 delta = 0; /* determines the change in probability */
+ u32 oldprob;
+ u32 alpha, beta;
+ bool update_prob = true;
+
+ q->vars.qdelay_old = q->vars.qdelay;
+
+ if (q->vars.avg_dq_rate > 0)
+ qdelay = (qlen << PIE_SCALE) / q->vars.avg_dq_rate;
+ else
+ qdelay = 0;
+
+ /* If qdelay is zero and qlen is not, it means qlen is very small, less
+ * than dequeue_rate, so we do not update probabilty in this round
+ */
+ if (qdelay == 0 && qlen != 0)
+ update_prob = false;
+
+ /* Add ranges for alpha and beta, more aggressive for high dropping
+ * mode and gentle steps for light dropping mode
+ * In light dropping mode, take gentle steps; in medium dropping mode,
+ * take medium steps; in high dropping mode, take big steps.
+ */
+ if (q->vars.prob < MAX_PROB / 100) {
+ alpha =
+ (q->params.alpha * (MAX_PROB / PSCHED_TICKS_PER_SEC)) >> 7;
+ beta =
+ (q->params.beta * (MAX_PROB / PSCHED_TICKS_PER_SEC)) >> 7;
+ } else if (q->vars.prob < MAX_PROB / 10) {
+ alpha =
+ (q->params.alpha * (MAX_PROB / PSCHED_TICKS_PER_SEC)) >> 5;
+ beta =
+ (q->params.beta * (MAX_PROB / PSCHED_TICKS_PER_SEC)) >> 5;
+ } else {
+ alpha =
+ (q->params.alpha * (MAX_PROB / PSCHED_TICKS_PER_SEC)) >> 4;
+ beta =
+ (q->params.beta * (MAX_PROB / PSCHED_TICKS_PER_SEC)) >> 4;
+ }
+
+ /* alpha and beta should be between 0 and 32, in multiples of 1/16
+ */
+ delta += alpha * ((qdelay - q->params.target));
+ delta += beta * ((qdelay - qdelay_old));
+
+ oldprob = q->vars.prob;
+
+ /* addition to ensure we increase probability in steps of no
+ * more than 2%
+ */
+ if (delta > (s32) (MAX_PROB / (100 / 2))
+ && q->vars.prob >= MAX_PROB / 10)
+ delta = (MAX_PROB / 100) * 2;
+
+ /* Non-linear drop
+ * Tune drop probability to increase quickly for high delays
+ * (250ms and above)
+ * 250ms is derived through experiments and provides error protection
+ */
+
+ if (qdelay > (PSCHED_NS2TICKS(250 * NSEC_PER_MSEC)))
+ delta += MAX_PROB / (100 / 2);
+
+ q->vars.prob += delta;
+
+ if (delta > 0) {
+ /* prevent overflow */
+ if (q->vars.prob < oldprob) {
+ q->vars.prob = MAX_PROB;
+ /* Prevent normalization error
+ * If probability is the maximum value already,
+ * we normalize it here, and skip the
+ * check to do a non-linear drop in the next section
+ */
+ update_prob = false;
+ }
+ } else {
+ /* prevent underflow */
+ if (q->vars.prob > oldprob)
+ q->vars.prob = 0;
+ }
+
+ /* Non-linear drop in probability */
+ /* Reduce drop probability quickly if delay is 0 for 2 consecutive
+ * Tupdate periods
+ */
+ if ((qdelay == 0) && (qdelay_old == 0) && update_prob)
+ q->vars.prob = (q->vars.prob * 98) / 100;
+
+ q->vars.qdelay = qdelay;
+ q->vars.qlen_old = qlen;
+
+ /* we restart the measurement cycle if the following conditions are met
+ * 1. If the delay has been low for 2 consecutive Tupdate periods
+ * 2. Calculated drop probability is zero
+ * 3. We have atleast one estimate for the avg_dq_rate ie.,
+ * is a non-zero value
+ */
+ if ((q->vars.qdelay < q->params.target / 2)
+ && (q->vars.qdelay_old < q->params.target / 2)
+ && (q->vars.prob == 0)
+ && q->vars.avg_dq_rate > 0)
+ pie_vars_init(&q->vars);
+}
+
+static void pie_timer(unsigned long arg)
+{
+ struct Qdisc *sch = (struct Qdisc *)arg;
+ struct pie_sched_data *q = qdisc_priv(sch);
+ spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch));
+
+ spin_lock(root_lock);
+ calculate_probability(sch);
+
+ /* reset the timer to fire after 'tupdate'. Convert from us to jiffies.
+ */
+ if (q->params.tupdate)
+ mod_timer(&q->adapt_timer, jiffies +
+ usecs_to_jiffies(q->params.tupdate));
+ spin_unlock(root_lock);
+
+}
+
+static int pie_init(struct Qdisc *sch, struct nlattr *opt)
+{
+ struct pie_sched_data *q = qdisc_priv(sch);
+
+ pie_params_init(&q->params);
+ pie_vars_init(&q->vars);
+ sch->limit = q->params.limit;
+
+ setup_timer(&q->adapt_timer, pie_timer, (unsigned long)sch);
+ mod_timer(&q->adapt_timer, jiffies + HZ / 2);
+
+ if (opt) {
+ int err = pie_change(sch, opt);
+
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int pie_dump(struct Qdisc *sch, struct sk_buff *skb)
+{
+ struct pie_sched_data *q = qdisc_priv(sch);
+ struct nlattr *opts;
+
+ opts = nla_nest_start(skb, TCA_OPTIONS);
+ if (opts == NULL)
+ goto nla_put_failure;
+
+ /* convert target from pschedtime to us */
+ if (nla_put_u32(skb, TCA_PIE_TARGET,
+ ((u32) PSCHED_TICKS2NS(q->params.target)) /
+ NSEC_PER_USEC) ||
+ nla_put_u32(skb, TCA_PIE_LIMIT, sch->limit) ||
+ nla_put_u32(skb, TCA_PIE_TUPDATE, q->params.tupdate) ||
+ nla_put_u32(skb, TCA_PIE_ALPHA, q->params.alpha) ||
+ nla_put_u32(skb, TCA_PIE_BETA, q->params.beta) ||
+ nla_put_u32(skb, TCA_PIE_ECN, q->params.ecn) ||
+ nla_put_u32(skb, TCA_PIE_BYTEMODE, q->params.bytemode))
+ goto nla_put_failure;
+
+ return nla_nest_end(skb, opts);
+
+nla_put_failure:
+ nla_nest_cancel(skb, opts);
+ return -1;
+
+}
+
+static int pie_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
+{
+ struct pie_sched_data *q = qdisc_priv(sch);
+ struct tc_pie_xstats st = {
+ .prob = q->vars.prob,
+ .delay = ((u32) PSCHED_TICKS2NS(q->vars.qdelay)) /
+ NSEC_PER_USEC,
+ /* unscale and return dq_rate in bytes per sec */
+ .avg_dq_rate = q->vars.avg_dq_rate *
+ (PSCHED_TICKS_PER_SEC) >> PIE_SCALE,
+ .packets_in = q->stats.packets_in,
+ .overlimit = q->stats.overlimit,
+ .maxq = q->stats.maxq,
+ .dropped = q->stats.dropped,
+ .ecn_mark = q->stats.ecn_mark,
+ };
+
+ return gnet_stats_copy_app(d, &st, sizeof(st));
+}
+
+static struct sk_buff *pie_qdisc_dequeue(struct Qdisc *sch)
+{
+ struct sk_buff *skb;
+ skb = __qdisc_dequeue_head(sch, &sch->q);
+
+ if (!skb)
+ return NULL;
+
+ pie_process_dequeue(sch, skb);
+ return skb;
+}
+
+static void pie_reset(struct Qdisc *sch)
+{
+ struct pie_sched_data *q = qdisc_priv(sch);
+ qdisc_reset_queue(sch);
+ pie_vars_init(&q->vars);
+}
+
+static void pie_destroy(struct Qdisc *sch)
+{
+ struct pie_sched_data *q = qdisc_priv(sch);
+ q->params.tupdate = 0;
+ del_timer_sync(&q->adapt_timer);
+}
+
+static struct Qdisc_ops pie_qdisc_ops __read_mostly = {
+ .id = "pie",
+ .priv_size = sizeof(struct pie_sched_data),
+ .enqueue = pie_qdisc_enqueue,
+ .dequeue = pie_qdisc_dequeue,
+ .peek = qdisc_peek_dequeued,
+ .init = pie_init,
+ .destroy = pie_destroy,
+ .reset = pie_reset,
+ .change = pie_change,
+ .dump = pie_dump,
+ .dump_stats = pie_dump_stats,
+ .owner = THIS_MODULE,
+};
+
+static int __init pie_module_init(void)
+{
+ return register_qdisc(&pie_qdisc_ops);
+}
+
+static void __exit pie_module_exit(void)
+{
+ unregister_qdisc(&pie_qdisc_ops);
+}
+
+module_init(pie_module_init);
+module_exit(pie_module_exit);
+
+MODULE_DESCRIPTION("Proportional Integral controller Enhanced (PIE) scheduler");
+MODULE_AUTHOR("Vijay Subramanian");
+MODULE_AUTHOR("Mythili Prabhu");
+MODULE_LICENSE("GPL");
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH] x86: Run checksumming in parallel accross multiple alu's
From: Joe Perches @ 2013-11-01 20:26 UTC (permalink / raw)
To: Neil Horman
Cc: David Laight, Ben Hutchings, Doug Ledford, Ingo Molnar,
Eric Dumazet, linux-kernel, netdev
In-Reply-To: <20131101195850.GD8467@hmsreliant.think-freely.org>
On Fri, 2013-11-01 at 15:58 -0400, Neil Horman wrote:
> On Fri, Nov 01, 2013 at 12:45:29PM -0700, Joe Perches wrote:
> > On Fri, 2013-11-01 at 13:37 -0400, Neil Horman wrote:
> >
> > > I think it would be better if we just did the prefetch here
> > > and re-addressed this area when AVX (or addcx/addox) instructions were available
> > > for testing on hardware.
> >
> > Could there be a difference if only a single software
> > prefetch was done at the beginning of transfer before
> > the while loop and hardware prefetches did the rest?
> >
> I wouldn't think so. If hardware was going to do any prefetching based on
> memory access patterns it will do so regardless of the leading prefetch, and
> that first prefetch isn't helpful because we still wind up stalling on the adds
> while its completing
I imagine one benefit to be helping prevent
prefetching beyond the actual data required.
Maybe some hardware optimizes prefetch stride
better than 5*64.
I wonder also if using
if (count > some_length)
prefetch
while (...)
helps small lengths more than the test/jump cost.
^ permalink raw reply
* Re: [PATCH] x86: Run checksumming in parallel accross multiple alu's
From: Neil Horman @ 2013-11-01 19:58 UTC (permalink / raw)
To: Joe Perches
Cc: David Laight, Ben Hutchings, Doug Ledford, Ingo Molnar,
Eric Dumazet, linux-kernel, netdev
In-Reply-To: <1383335129.3042.10.camel@joe-AO722>
On Fri, Nov 01, 2013 at 12:45:29PM -0700, Joe Perches wrote:
> On Fri, 2013-11-01 at 13:37 -0400, Neil Horman wrote:
>
> > I think it would be better if we just did the prefetch here
> > and re-addressed this area when AVX (or addcx/addox) instructions were available
> > for testing on hardware.
>
> Could there be a difference if only a single software
> prefetch was done at the beginning of transfer before
> the while loop and hardware prefetches did the rest?
>
I wouldn't think so. If hardware was going to do any prefetching based on
memory access patterns it will do so regardless of the leading prefetch, and
that first prefetch isn't helpful because we still wind up stalling on the adds
while its completing
Neil
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCH] x86: Run checksumming in parallel accross multiple alu's
From: Joe Perches @ 2013-11-01 19:45 UTC (permalink / raw)
To: Neil Horman
Cc: David Laight, Ben Hutchings, Doug Ledford, Ingo Molnar,
Eric Dumazet, linux-kernel, netdev
In-Reply-To: <20131101173701.GC8467@hmsreliant.think-freely.org>
On Fri, 2013-11-01 at 13:37 -0400, Neil Horman wrote:
> I think it would be better if we just did the prefetch here
> and re-addressed this area when AVX (or addcx/addox) instructions were available
> for testing on hardware.
Could there be a difference if only a single software
prefetch was done at the beginning of transfer before
the while loop and hardware prefetches did the rest?
^ permalink raw reply
* Re: [PATCH net-next RFC 0/5] xen-netback: TX grant mapping instead of copy
From: Zoltan Kiss @ 2013-11-01 19:00 UTC (permalink / raw)
To: Ian Campbell; +Cc: wei.liu2, xen-devel, netdev, linux-kernel, jonathan.davies
In-Reply-To: <1383303035.672.29.camel@kazak.uk.xensource.com>
On 01/11/13 10:50, Ian Campbell wrote:
> Does this always avoid copying when bridging/openvswitching/forwarding
> (e.g. masquerading etc)? For both domU->domU and domU->physical NIC?
I've tested the domU->domU, domU->physical with bridge and openvswitch
usecase, and now I've created a new stat counter to see how often copy
happens (the callback's second parameter tells you whether the skb was
freed or copied). It doesn't do copy in all of these scenarios.
What do you mean by forwarding? The scenario when you use bridge and
iptables mangling with the packet, not just filtering?
> How does it deal with broadcast traffic?
Most of the real broadcast traffic actually small packets fit in the
PKT_PROT_LEN sized linear space, so it doesn't make any difference,
apart from doing a mapping before copy. But that will be eliminated
later on, I plan to add an incremental improvement to grant copy the
linear part.
I haven't spent too much time on that, but I couldn't find any broadcast
protocol which use large enough packets and easy to test, so I'm open to
ideas.
What I already know, skb_clone trigger a copy, and if the caller use the
original skb for every cloning, it will do several copy. I think that
could be fixed by using the first clone to do any further clones.
> Do you have any numbers for the dom0 cpu usage impact?
DomU->NIC: the vif took 40% according to top, I guess the bottleneck
there is the TLB flushing.
DomU->DomU: the vif of the RX side cause the bottleneck due to grant
copy to the guest
> Aggregate throughput for many guests would be a useful datapoint too.
I will do measurements about that.
>> Based on my investigations the packet get only copied if it is
delivered to
>>Dom0 stack, which is due to this patch:
>>https://lkml.org/lkml/2012/7/20/363
>>That's a bit unfortunate, but as far as I know for the huge majority
this use
>>case is not too important.
> Likely to be true, but it would still be interesting to know how badly
> this use case suffers with this change, and any increase in CPU usage
> would be interesting to know about as well.
I can't find my numbers, but as far as I remember it wasn't
significantly worse than grant copy. I will check that again.
Zoli
^ permalink raw reply
* Re: [RFC PATCH 1/2] ipv6: select oif corresponding to source address
From: Sergei Shtylyov @ 2013-11-01 19:42 UTC (permalink / raw)
To: Emmanuel Thierry, netdev@vger.kernel.org; +Cc: Hannes Frederic Sowa
In-Reply-To: <06F9072B-27A3-4A21-ADE2-A5B3FE817A1F@telecom-bretagne.eu>
Hello.
On 11/01/2013 08:17 PM, Emmanuel Thierry wrote:
> When selecting the next hop, prefer the interface to which the
> source address is associated. This preference fixes problems for
> IPv6 hosts in multi-interfaces setup.
> In the case where a host has:
> * multiple links, each providing internet connectivity
> * both links advertising prefix and default route via Router
> Advertisements
> The current route selection process completly ignores whether or
> not the next hop is consistent with the source address. This may
> lead to packets being sent with a specific source address on the
> wrong link, then dropped by a router enforcing reverse path
> filtering.
> This fix pre-selects the output interface corresponding to the
> source address bound to the socket. The fl6->flowi6_oif
> attribute gives to the route selection algorithm an hint about
> what interface to choose in case of a tie between several routes
> of equal preferences (equal netmask, equal metrics and equal
> RFC 4191 preference values).
> Signed-off-by: Emmanuel Thierry <emmanuel.thierry@telecom-bretagne.eu>
> ---
> net/ipv6/ip6_output.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 91fb4e8..1a05395 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -848,8 +848,14 @@ static int ip6_dst_lookup_tail(struct sock *sk,
> #endif
> int err;
>
> - if (*dst == NULL)
> + if (*dst == NULL) {
> + struct inet6_ifaddr *ifp;
Empty line needed between declaration and other code.
> + if (!ipv6_addr_any(&fl6->saddr)) {
> + ifp = ipv6_get_ifaddr(net, &fl6->saddr, NULL, 1);
> + fl6->flowi6_oif = ifp->idev->dev->ifindex;
> + }
> *dst = ip6_route_output(net, sk, fl6);
> + }
>
> if ((err = (*dst)->error))
> goto out_err_release;
WBR, Sergei
^ permalink raw reply
* Re: [RFC PATCH 1/2] ipv6: select oif corresponding to source address
From: Hannes Frederic Sowa @ 2013-11-01 18:04 UTC (permalink / raw)
To: Emmanuel Thierry; +Cc: netdev@vger.kernel.org
In-Reply-To: <06F9072B-27A3-4A21-ADE2-A5B3FE817A1F@telecom-bretagne.eu>
On Fri, Nov 01, 2013 at 06:17:36PM +0100, Emmanuel Thierry wrote:
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -848,8 +848,14 @@ static int ip6_dst_lookup_tail(struct sock *sk,
> #endif
> int err;
>
> - if (*dst == NULL)
> + if (*dst == NULL) {
> + struct inet6_ifaddr *ifp;
> + if (!ipv6_addr_any(&fl6->saddr)) {
> + ifp = ipv6_get_ifaddr(net, &fl6->saddr, NULL, 1);
> + fl6->flowi6_oif = ifp->idev->dev->ifindex;
Cool that you are working on this! :)
Small feedback that might help while you play with this:
You have to be careful here, ipv6_get_ifaddr increments the reference
count of the ifp, so you have to drop it again: in6_ifa_put(ifp);
For such short periods holding a reference does not always make sense. You
could check ipv6_get_lladdr/__ipv6_get_lladdr how you could do this
without touching the reference counters if you put this section in
a rcu_read_lock. This especially makes sense if this is a often used
code path.
> + }
> *dst = ip6_route_output(net, sk, fl6);
> + }
>
> if ((err = (*dst)->error))
> goto out_err_release;
I'll have to think about this carefully.
Thanks,
Hannes
^ permalink raw reply
* Re: [PATCH] x86: Run checksumming in parallel accross multiple alu's
From: Neil Horman @ 2013-11-01 17:37 UTC (permalink / raw)
To: David Laight
Cc: Ben Hutchings, Doug Ledford, Ingo Molnar, Eric Dumazet,
linux-kernel, netdev
In-Reply-To: <AE90C24D6B3A694183C094C60CF0A2F6026B73D3@saturn3.aculab.com>
On Fri, Nov 01, 2013 at 04:18:50PM -0000, David Laight wrote:
> > How would you suggest replacing the jumps in this case? I agree it would be
> > faster here, but I'm not sure how I would implement an increment using a single
> > conditional move.
>
> I think you need 3 instructions, move a 0, conditionally move a 1
> then add. I suspect it won't be a win!
>
> If you do 'win' it is probably very dependent on how the instructions
> get scheduled onto the execution units - which will probably make
> it very cpu type dependant.
>
> David
>
I agree, that sounds interesting, but very cpu dependent. Thanks for the
suggestion, Ben, but I think it would be better if we just did the prefetch here
and re-addressed this area when AVX (or addcx/addox) instructions were available
for testing on hardware.
Neil
^ permalink raw reply
* Re: [RFC PATCH 1/2] ipv6: select oif corresponding to source address
From: Emmanuel Thierry @ 2013-11-01 17:34 UTC (permalink / raw)
To: netdev@vger.kernel.org; +Cc: Hannes Frederic Sowa
In-Reply-To: <06F9072B-27A3-4A21-ADE2-A5B3FE817A1F@telecom-bretagne.eu>
Hello,
These two rfc patches fix problems stated in "ipv6: strange routing behaviors on a multi-interfaces setup" on 2013-09-25:
>
> I'm working on multi-interfaces setups on IPv6. I found several disturbing route behaviors which sounds like bugs to me.
>
> Both eth1 and eth2 interfaces receive RAs from distinct routers and autoconfigure:
> * their slaac address
> * their default route, both with the same priority
> Under these conditions, the following happen depending on the expiration time of each route.
>
>
> 1/ A ping with a specified source address and interface may go through the wrong interface.
>
> # ping6 -c 1 -I "<slaac_eth1>%eth1" <dest>
> … uses the right interface (eth1) with the right source address (<slaac_eth1>).
>
> # ping6 -c 1 -I "<slaac_eth2>%eth2" <dest>
> … uses the wrong interface (eth1) with the right source address (<slaac_eth2>).
>
> The ping6 utility performed a bind() on <slaac_ethx> with scope id set to <ifindex_ethx>.
> If we flush the routing cache between each ping, the routing is done as expected.
>
> As i could observe in the kernel. When ip6_pol_route() is called, oif is equal to <ifindex_eth2> but the flag RT6_LOOKUP_F_IFACE is not set. This makes routes through other interfaces to still be valid.
> Shouldn't we set the RT6_LOOKUP_F_IFACE flag when a scope id is specified ?
>
>
> 2/ A ping from a specified source address may go through the wrong interface.
>
> # ping6 -I "<slaac_eth2>" <dest>
> … may use eth1 with <slaac_eth2>.
>
> The ping6 utility performed a bind() on <slaac_eth2>
> This is a derivative from the first one, with the significative difference that it also happens if the routing cache is empty. The most recent default route is chosen regardless of the source address.
>
> Shouldn't we look in a first try for routes on the device corresponding to the source address, and in a second try for others ?
>
>
> 3/ A ping from a specified interface may go through the wrong interface with the wrong source address.
>
> # ping6 -I "eth2" <dest>
> … may use eth1 with <slaac_eth1>.
>
> The ping6 performs a setsockopt(IPV6_PKTINFO) with <ifindex_ethx>, then a connect() to the destination. In this case, source address selection is concerned, but also routing since source address selection depends on routing.
>
>
> I experienced these problems on a 3.11.1 kernel but they look to be quite recurrent in the past versions as well.
>
Best regards
Emmanuel Thierry
^ permalink raw reply
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