* [PATCH mlx5-next 03/18] net/mlx5: E-Switch, Use vport index when init rep
From: Saeed Mahameed @ 2019-06-28 22:35 UTC (permalink / raw)
To: Saeed Mahameed, Leon Romanovsky
Cc: netdev@vger.kernel.org, linux-rdma@vger.kernel.org, Bodong Wang,
Parav Pandit, Mark Bloch
In-Reply-To: <20190628223516.9368-1-saeedm@mellanox.com>
From: Bodong Wang <bodong@mellanox.com>
Driver is referring to the array index when doing rep initialization,
using vport is confusing as it's normally interpreted as vport number.
This patch doesn't change any functionality.
Signed-off-by: Bodong Wang <bodong@mellanox.com>
Reviewed-by: Parav Pandit <parav@mellanox.com>
Reviewed-by: Mark Bloch <markb@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
---
drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index 174b0ec4162f..bc639a846714 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -1399,7 +1399,7 @@ int esw_offloads_init_reps(struct mlx5_eswitch *esw)
struct mlx5_core_dev *dev = esw->dev;
struct mlx5_eswitch_rep *rep;
u8 hw_id[ETH_ALEN], rep_type;
- int vport;
+ int vport_index;
esw->offloads.vport_reps = kcalloc(total_vports,
sizeof(struct mlx5_eswitch_rep),
@@ -1409,8 +1409,8 @@ int esw_offloads_init_reps(struct mlx5_eswitch *esw)
mlx5_query_nic_vport_mac_address(dev, 0, hw_id);
- mlx5_esw_for_all_reps(esw, vport, rep) {
- rep->vport = mlx5_eswitch_index_to_vport_num(esw, vport);
+ mlx5_esw_for_all_reps(esw, vport_index, rep) {
+ rep->vport = mlx5_eswitch_index_to_vport_num(esw, vport_index);
ether_addr_copy(rep->hw_id, hw_id);
for (rep_type = 0; rep_type < NUM_REP_TYPES; rep_type++)
--
2.21.0
^ permalink raw reply related
* [PATCH mlx5-next 02/18] net/mlx5: Added MCQI and MCQS registers' description to ifc
From: Saeed Mahameed @ 2019-06-28 22:35 UTC (permalink / raw)
To: Saeed Mahameed, Leon Romanovsky
Cc: netdev@vger.kernel.org, linux-rdma@vger.kernel.org, Shay Agroskin
In-Reply-To: <20190628223516.9368-1-saeedm@mellanox.com>
From: Shay Agroskin <shayag@mellanox.com>
Given a fw component index, the MCQI register allows us to query
this component's information (e.g. its version and capabilities).
Given a fw component index, the MCQS register allows us to query the
status of a fw component, including its type and state
(e.g. PRESET/IN_USE).
It can be used to find the index of a component of a specific type, by
sequentially increasing the component index, and querying each time the
type of the returned component.
If max component index is reached, 'last_index_flag' is set by the HCA.
These registers' description was added to query the running and pending
fw version of the HCA.
Signed-off-by: Shay Agroskin <shayag@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
---
include/linux/mlx5/driver.h | 1 +
include/linux/mlx5/mlx5_ifc.h | 59 +++++++++++++++++++++++++++++++++--
2 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index 87f77ded78d4..2ff624a91e3d 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -138,6 +138,7 @@ enum {
MLX5_REG_MTPPS = 0x9053,
MLX5_REG_MTPPSE = 0x9054,
MLX5_REG_MPEGC = 0x9056,
+ MLX5_REG_MCQS = 0x9060,
MLX5_REG_MCQI = 0x9061,
MLX5_REG_MCC = 0x9062,
MLX5_REG_MCDA = 0x9063,
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index db00effaa83a..e2a77b5152a8 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -8542,7 +8542,7 @@ struct mlx5_ifc_mcam_access_reg_bits {
u8 mcda[0x1];
u8 mcc[0x1];
u8 mcqi[0x1];
- u8 reserved_at_1f[0x1];
+ u8 mcqs[0x1];
u8 regs_95_to_87[0x9];
u8 mpegc[0x1];
@@ -9034,6 +9034,24 @@ struct mlx5_ifc_mtppse_reg_bits {
u8 reserved_at_40[0x40];
};
+struct mlx5_ifc_mcqs_reg_bits {
+ u8 last_index_flag[0x1];
+ u8 reserved_at_1[0x7];
+ u8 fw_device[0x8];
+ u8 component_index[0x10];
+
+ u8 reserved_at_20[0x10];
+ u8 identifier[0x10];
+
+ u8 reserved_at_40[0x17];
+ u8 component_status[0x5];
+ u8 component_update_state[0x4];
+
+ u8 last_update_state_changer_type[0x4];
+ u8 last_update_state_changer_host_id[0x4];
+ u8 reserved_at_68[0x18];
+};
+
struct mlx5_ifc_mcqi_cap_bits {
u8 supported_info_bitmask[0x20];
@@ -9054,6 +9072,43 @@ struct mlx5_ifc_mcqi_cap_bits {
u8 reserved_at_86[0x1a];
};
+struct mlx5_ifc_mcqi_version_bits {
+ u8 reserved_at_0[0x2];
+ u8 build_time_valid[0x1];
+ u8 user_defined_time_valid[0x1];
+ u8 reserved_at_4[0x14];
+ u8 version_string_length[0x8];
+
+ u8 version[0x20];
+
+ u8 build_time[0x40];
+
+ u8 user_defined_time[0x40];
+
+ u8 build_tool_version[0x20];
+
+ u8 reserved_at_e0[0x20];
+
+ u8 version_string[92][0x8];
+};
+
+struct mlx5_ifc_mcqi_activation_method_bits {
+ u8 pending_server_ac_power_cycle[0x1];
+ u8 pending_server_dc_power_cycle[0x1];
+ u8 pending_server_reboot[0x1];
+ u8 pending_fw_reset[0x1];
+ u8 auto_activate[0x1];
+ u8 all_hosts_sync[0x1];
+ u8 device_hw_reset[0x1];
+ u8 reserved_at_7[0x19];
+};
+
+union mlx5_ifc_mcqi_reg_data_bits {
+ struct mlx5_ifc_mcqi_cap_bits mcqi_caps;
+ struct mlx5_ifc_mcqi_version_bits mcqi_version;
+ struct mlx5_ifc_mcqi_activation_method_bits mcqi_activation_mathod;
+};
+
struct mlx5_ifc_mcqi_reg_bits {
u8 read_pending_component[0x1];
u8 reserved_at_1[0xf];
@@ -9071,7 +9126,7 @@ struct mlx5_ifc_mcqi_reg_bits {
u8 reserved_at_a0[0x10];
u8 data_size[0x10];
- u8 data[0][0x20];
+ union mlx5_ifc_mcqi_reg_data_bits data[0];
};
struct mlx5_ifc_mcc_reg_bits {
--
2.21.0
^ permalink raw reply related
* [PATCH mlx5-next 01/18] net/mlx5: Add hardware definitions for sub functions
From: Saeed Mahameed @ 2019-06-28 22:35 UTC (permalink / raw)
To: Saeed Mahameed, Leon Romanovsky
Cc: netdev@vger.kernel.org, linux-rdma@vger.kernel.org, Parav Pandit,
Vu Pham
In-Reply-To: <20190628223516.9368-1-saeedm@mellanox.com>
From: Parav Pandit <parav@mellanox.com>
Update mlx5 device interface data structures for:
1. New command definitions for allocating, deallocating SF
2. Query SF partition
3. Eswitch SF fields
4. HCA CAP SF fields
5. Extend Eswitch functions command for SF
Signed-off-by: Parav Pandit <parav@mellanox.com>
Signed-off-by: Vu Pham <vuhuong@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
---
include/linux/mlx5/mlx5_ifc.h | 99 +++++++++++++++++++++++++++++++++--
1 file changed, 96 insertions(+), 3 deletions(-)
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index d4409654f760..db00effaa83a 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -106,6 +106,9 @@ enum {
MLX5_CMD_OP_QUERY_ISSI = 0x10a,
MLX5_CMD_OP_SET_ISSI = 0x10b,
MLX5_CMD_OP_SET_DRIVER_VERSION = 0x10d,
+ MLX5_CMD_OP_QUERY_SF_PARTITION = 0x111,
+ MLX5_CMD_OP_ALLOC_SF = 0x113,
+ MLX5_CMD_OP_DEALLOC_SF = 0x114,
MLX5_CMD_OP_CREATE_MKEY = 0x200,
MLX5_CMD_OP_QUERY_MKEY = 0x201,
MLX5_CMD_OP_DESTROY_MKEY = 0x202,
@@ -713,7 +716,11 @@ struct mlx5_ifc_e_switch_cap_bits {
u8 reserved_2b[0x6];
u8 max_encap_header_size[0xa];
- u8 reserved_40[0x7c0];
+ u8 reserved_at_40[0xb];
+ u8 log_max_esw_sf[0x5];
+ u8 esw_sf_base_id[0x10];
+
+ u8 reserved_at_60[0x7a0];
};
@@ -1330,13 +1337,24 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 reserved_at_640[0x10];
u8 num_q_monitor_counters[0x10];
- u8 reserved_at_660[0x40];
+ u8 reserved_at_660[0x20];
+
+ u8 sf[0x1];
+ u8 sf_set_partition[0x1];
+ u8 reserved_at_682[0x1];
+ u8 log_max_sf[0x5];
+ u8 reserved_at_688[0x8];
+ u8 log_min_sf_size[0x8];
+ u8 max_num_sf_partitions[0x8];
u8 uctx_cap[0x20];
u8 reserved_at_6c0[0x4];
u8 flex_parser_id_geneve_tlv_option_0[0x4];
- u8 reserved_at_6c8[0x138];
+ u8 reserved_at_6c8[0x28];
+ u8 sf_base_id[0x10];
+
+ u8 reserved_at_700[0x100];
};
enum mlx5_flow_destination_type {
@@ -9786,6 +9804,81 @@ struct mlx5_ifc_query_esw_functions_out_bits {
struct mlx5_ifc_host_params_context_bits host_params_context;
u8 reserved_at_280[0x180];
+ u8 host_sf_enable[0][0x40];
+};
+
+struct mlx5_ifc_sf_partition_bits {
+ u8 reserved_at_0[0x10];
+ u8 log_num_sf[0x8];
+ u8 log_sf_bar_size[0x8];
+};
+
+struct mlx5_ifc_query_sf_partitions_out_bits {
+ u8 status[0x8];
+ u8 reserved_at_8[0x18];
+
+ u8 syndrome[0x20];
+
+ u8 reserved_at_40[0x18];
+ u8 num_sf_partitions[0x8];
+
+ u8 reserved_at_60[0x20];
+
+ struct mlx5_ifc_sf_partition_bits sf_partition[0];
+};
+
+struct mlx5_ifc_query_sf_partitions_in_bits {
+ u8 opcode[0x10];
+ u8 reserved_at_10[0x10];
+
+ u8 reserved_at_20[0x10];
+ u8 op_mod[0x10];
+
+ u8 reserved_at_40[0x40];
+};
+
+struct mlx5_ifc_dealloc_sf_out_bits {
+ u8 status[0x8];
+ u8 reserved_at_8[0x18];
+
+ u8 syndrome[0x20];
+
+ u8 reserved_at_40[0x40];
+};
+
+struct mlx5_ifc_dealloc_sf_in_bits {
+ u8 opcode[0x10];
+ u8 reserved_at_10[0x10];
+
+ u8 reserved_at_20[0x10];
+ u8 op_mod[0x10];
+
+ u8 reserved_at_40[0x10];
+ u8 function_id[0x10];
+
+ u8 reserved_at_60[0x20];
+};
+
+struct mlx5_ifc_alloc_sf_out_bits {
+ u8 status[0x8];
+ u8 reserved_at_8[0x18];
+
+ u8 syndrome[0x20];
+
+ u8 reserved_at_40[0x40];
+};
+
+struct mlx5_ifc_alloc_sf_in_bits {
+ u8 opcode[0x10];
+ u8 reserved_at_10[0x10];
+
+ u8 reserved_at_20[0x10];
+ u8 op_mod[0x10];
+
+ u8 reserved_at_40[0x10];
+ u8 function_id[0x10];
+
+ u8 reserved_at_60[0x20];
};
#endif /* MLX5_IFC_H */
--
2.21.0
^ permalink raw reply related
* [PATCH mlx5-next 00/18] Mellanox, mlx5 E-Switch and low level updates
From: Saeed Mahameed @ 2019-06-28 22:35 UTC (permalink / raw)
To: Saeed Mahameed, Leon Romanovsky
Cc: netdev@vger.kernel.org, linux-rdma@vger.kernel.org
Hi All,
This series includes some low level updates mainly in the E-Switch
netdev and rdma vport representors areas.
From Parav and Huy:
1) Added hardware bits and structures definitions for sub-functions
2) Small code cleanup and improvement for PF pci driver.
From Bodong:
3) Use the correct name semantics of vport index and vport number
4) Cleanup the rep and netdev reference when unloading IB rep.
5) Bluefield (ECPF) updates and refactoring for better E-Switch
management on ECPF embedded CPU NIC:
5.1) Consolidate querying eswitch number of VFs
5.2) Register event handler at the correct E-Switch init stage
5.3) Setup PF's inline mode and vlan pop when the ECPF is the
E-Swtich manager ( the host PF is basically a VF ).
5.4) Handle Vport UC address changes in switchdev mode.
From Shay:
6) Add support for MCQI and MCQS hardware registers.
In case of no objections these patches will be applied to mlx5-next and
will be sent later as pull request to both rdma-next and net-next trees.
Thanks,
Saeed.
---
Bodong Wang (12):
net/mlx5: E-Switch, Use vport index when init rep
{IB, net}/mlx5: E-Switch, Use index of rep for vport to IB port
mapping
RDMA/mlx5: Cleanup rep when doing unload
net/mlx5: Don't handle VF func change if host PF is disabled
net/mlx5: E-Switch, Use correct flags when configuring vlan
net/mlx5: Handle host PF vport mac/guid for ECPF
net/mlx5: E-Switch, Refactor eswitch SR-IOV interface
net/mlx5: E-Switch, Consolidate eswitch function number of VFs
net/mlx5: E-Switch, Reg/unreg function changed event at correct stage
net/mlx5: E-Switch, Use iterator for vlan and min-inline setups
net/mlx5: E-Switch, Consider host PF for inline mode and vlan pop
net/mlx5: E-Switch, Handle UC address change in switchdev mode
Huy Nguyen (1):
net/mlx5: Rename mlx5_pci_dev_type to mlx5_coredev_type
Parav Pandit (4):
net/mlx5: Add hardware definitions for sub functions
net/mlx5: Move pci status reg access mutex to mlx5_pci_init
net/mlx5: Limit scope of mlx5_get_next_phys_dev() to PCI PF devices
net/mlx5: Reduce dependency on enabled_vfs counter and num_vfs
Shay Agroskin (1):
net/mlx5: Added MCQI and MCQS registers' description to ifc
drivers/infiniband/hw/mlx5/ib_rep.c | 22 +-
drivers/infiniband/hw/mlx5/ib_rep.h | 2 +-
drivers/infiniband/hw/mlx5/main.c | 2 +-
drivers/infiniband/hw/mlx5/mlx5_ib.h | 1 -
drivers/net/ethernet/mellanox/mlx5/core/dev.c | 9 +-
.../ethernet/mellanox/mlx5/core/en_dcbnl.c | 2 +-
.../net/ethernet/mellanox/mlx5/core/en_main.c | 4 +-
.../net/ethernet/mellanox/mlx5/core/en_rep.c | 8 +-
.../net/ethernet/mellanox/mlx5/core/en_tc.c | 2 +-
.../net/ethernet/mellanox/mlx5/core/eswitch.c | 151 ++++++++------
.../net/ethernet/mellanox/mlx5/core/eswitch.h | 44 +++-
.../mellanox/mlx5/core/eswitch_offloads.c | 196 ++++++++----------
.../ethernet/mellanox/mlx5/core/fpga/conn.c | 2 +-
drivers/net/ethernet/mellanox/mlx5/core/lag.c | 4 +-
.../net/ethernet/mellanox/mlx5/core/main.c | 7 +-
.../net/ethernet/mellanox/mlx5/core/rdma.c | 2 +-
.../net/ethernet/mellanox/mlx5/core/sriov.c | 27 +--
.../net/ethernet/mellanox/mlx5/core/vport.c | 28 ++-
include/linux/mlx5/driver.h | 13 +-
include/linux/mlx5/eswitch.h | 8 +-
include/linux/mlx5/mlx5_ifc.h | 161 +++++++++++++-
include/linux/mlx5/vport.h | 4 +-
22 files changed, 446 insertions(+), 253 deletions(-)
--
2.21.0
^ permalink raw reply
* Re: [PATCH iproute2 v2 1/2] devlink: fix format string warning for 32bit targets
From: Stephen Hemminger @ 2019-06-28 22:21 UTC (permalink / raw)
To: Baruch Siach; +Cc: Jiri Pirko, netdev, Aya Levin, Moshe Shemesh
In-Reply-To: <7a72ae0f9519e6a445d9712399d989fed648e6eb.1561660639.git.baruch@tkos.co.il>
On Thu, 27 Jun 2019 21:37:18 +0300
Baruch Siach <baruch@tkos.co.il> wrote:
> 32bit targets define uint64_t as long long unsigned. This leads to the
> following build warning:
>
> devlink.c: In function ‘pr_out_u64’:
> devlink.c:1729:11: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 4 has type ‘uint64_t {aka long long unsigned int}’ [-Wformat=]
> pr_out("%s %lu", name, val);
> ^
> devlink.c:59:21: note: in definition of macro ‘pr_out’
> fprintf(stdout, ##args); \
> ^~~~
>
> Use uint64_t specific conversion specifiers in the format string to fix
> that.
>
> Cc: Aya Levin <ayal@mellanox.com>
> Cc: Moshe Shemesh <moshe@mellanox.com>
> Signed-off-by: Baruch Siach <baruch@tkos.co.il>
Both patches applied.
^ permalink raw reply
* Re: [PATCH iproute2 v2 0/3] do not set IPv6-only options on IPv4 addresses
From: Stephen Hemminger @ 2019-06-28 22:20 UTC (permalink / raw)
To: Andrea Claudi; +Cc: netdev, dsahern
In-Reply-To: <cover.1561457597.git.aclaudi@redhat.com>
On Tue, 25 Jun 2019 12:29:54 +0200
Andrea Claudi <aclaudi@redhat.com> wrote:
> 'home', 'nodad' and 'mngtmpaddr' options are IPv6-only, but
> it is possible to set them on IPv4 addresses, too. This should
> not be possible.
>
> Fix this adding a check on the protocol family before setting
> the flags, and print warning messages on error to not break
> existing scripted setups.
>
> Andrea Claudi (3):
> ip address: do not set nodad option for IPv4 addresses
> ip address: do not set home option for IPv4 addresses
> ip address: do not set mngtmpaddr option for IPv4 addresses
>
> ip/ipaddress.c | 15 ++++++++++++---
> 1 file changed, 12 insertions(+), 3 deletions(-)
>
Series applied. Thanks
^ permalink raw reply
* [PATCH 1/3 bpf-next] net: add convert_to_xdp_frame_keep_zc function
From: Jonathan Lemon @ 2019-06-28 22:15 UTC (permalink / raw)
To: netdev, bjorn.topel, magnus.karlsson, jakub.kicinski,
jeffrey.t.kirsher
Cc: kernel-team
In-Reply-To: <20190628221555.3009654-1-jonathan.lemon@gmail.com>
Add a function which converts a ZC xdp_buff to a an xdp_frame, while
keeping the zc backing storage. This will be used to support TX of
received AF_XDP frames.
Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
---
include/net/xdp.h | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/include/net/xdp.h b/include/net/xdp.h
index 40c6d3398458..abe5f47ff0a5 100644
--- a/include/net/xdp.h
+++ b/include/net/xdp.h
@@ -82,6 +82,7 @@ struct xdp_frame {
*/
struct xdp_mem_info mem;
struct net_device *dev_rx; /* used by cpumap */
+ unsigned long handle;
};
/* Clear kernel pointers in xdp_frame */
@@ -95,15 +96,12 @@ struct xdp_frame *xdp_convert_zc_to_xdp_frame(struct xdp_buff *xdp);
/* Convert xdp_buff to xdp_frame */
static inline
-struct xdp_frame *convert_to_xdp_frame(struct xdp_buff *xdp)
+struct xdp_frame *__convert_to_xdp_frame(struct xdp_buff *xdp)
{
struct xdp_frame *xdp_frame;
int metasize;
int headroom;
- if (xdp->rxq->mem.type == MEM_TYPE_ZERO_COPY)
- return xdp_convert_zc_to_xdp_frame(xdp);
-
/* Assure headroom is available for storing info */
headroom = xdp->data - xdp->data_hard_start;
metasize = xdp->data - xdp->data_meta;
@@ -125,6 +123,20 @@ struct xdp_frame *convert_to_xdp_frame(struct xdp_buff *xdp)
return xdp_frame;
}
+static inline
+struct xdp_frame *convert_to_xdp_frame(struct xdp_buff *xdp)
+{
+ if (xdp->rxq->mem.type == MEM_TYPE_ZERO_COPY)
+ return xdp_convert_zc_to_xdp_frame(xdp);
+ return __convert_to_xdp_frame(xdp);
+}
+
+static inline
+struct xdp_frame *convert_to_xdp_frame_keep_zc(struct xdp_buff *xdp)
+{
+ return __convert_to_xdp_frame(xdp);
+}
+
void xdp_return_frame(struct xdp_frame *xdpf);
void xdp_return_frame_rx_napi(struct xdp_frame *xdpf);
void xdp_return_buff(struct xdp_buff *xdp);
--
2.17.1
^ permalink raw reply related
* [PATCH 3/3 bpf-next] ixgbe: Support zero-copy XDP_TX on the RX path for AF_XDP sockets.
From: Jonathan Lemon @ 2019-06-28 22:15 UTC (permalink / raw)
To: netdev, bjorn.topel, magnus.karlsson, jakub.kicinski,
jeffrey.t.kirsher
Cc: kernel-team
In-Reply-To: <20190628221555.3009654-1-jonathan.lemon@gmail.com>
When the XDP program attached to a zero-copy AF_XDP socket returns XDP_TX,
queue the umem frame on the XDP TX ring. Space on the recycle stack is
pre-allocated when the xsk is created. (taken from tx_ring, since the
xdp ring is not initialized yet)
Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe.h | 1 +
drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c | 74 +++++++++++++++++---
2 files changed, 67 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index 39e73ad60352..aca33e4773f5 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -150,6 +150,7 @@ enum ixgbe_tx_flags {
/* software defined flags */
IXGBE_TX_FLAGS_SW_VLAN = 0x80,
IXGBE_TX_FLAGS_FCOE = 0x100,
+ IXGBE_TX_FLAGS_ZC_FRAME = 0x200,
};
/* VLAN info */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c
index 65feb16200ea..c7a661736ab8 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c
@@ -77,7 +77,8 @@ static int ixgbe_xsk_umem_enable(struct ixgbe_adapter *adapter,
qid >= netdev->real_num_tx_queues)
return -EINVAL;
- if (!xsk_umem_recycle_alloc(umem, adapter->rx_ring[0]->count))
+ if (!xsk_umem_recycle_alloc(umem, adapter->rx_ring[0]->count +
+ adapter->tx_ring[0]->count))
return -ENOMEM;
err = ixgbe_xsk_umem_dma_map(adapter, umem);
@@ -135,13 +136,70 @@ int ixgbe_xsk_umem_setup(struct ixgbe_adapter *adapter, struct xdp_umem *umem,
ixgbe_xsk_umem_disable(adapter, qid);
}
+static int ixgbe_xmit_rcvd_zc(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *rx_ring,
+ struct xdp_buff *xdp)
+{
+ struct ixgbe_ring *ring = adapter->xdp_ring[smp_processor_id()];
+ struct ixgbe_tx_buffer *tx_buffer;
+ union ixgbe_adv_tx_desc *tx_desc;
+ struct xdp_frame *xdpf;
+ u32 len, cmd_type;
+ dma_addr_t dma;
+ u16 i;
+
+ if (unlikely(!ixgbe_desc_unused(ring)))
+ return IXGBE_XDP_CONSUMED;
+ xdpf = convert_to_xdp_frame_keep_zc(xdp);
+ if (unlikely(!xdpf))
+ return IXGBE_XDP_CONSUMED;
+ xdpf->handle = xdp->handle;
+ len = xdpf->len;
+
+ dma = xdp_umem_get_dma(rx_ring->xsk_umem, xdp->handle);
+
+ /* record the location of the first descriptor for this packet */
+ tx_buffer = &ring->tx_buffer_info[ring->next_to_use];
+ tx_buffer->bytecount = len;
+ tx_buffer->gso_segs = 1;
+ tx_buffer->protocol = 0;
+ tx_buffer->xdpf = xdpf;
+ tx_buffer->tx_flags = IXGBE_TX_FLAGS_ZC_FRAME;
+
+ i = ring->next_to_use;
+ tx_desc = IXGBE_TX_DESC(ring, i);
+
+ tx_desc->read.buffer_addr = cpu_to_le64(dma);
+
+ /* put descriptor type bits */
+ cmd_type = IXGBE_ADVTXD_DTYP_DATA |
+ IXGBE_ADVTXD_DCMD_DEXT |
+ IXGBE_ADVTXD_DCMD_IFCS;
+ cmd_type |= len | IXGBE_TXD_CMD;
+ tx_desc->read.cmd_type_len = cpu_to_le32(cmd_type);
+ tx_desc->read.olinfo_status =
+ cpu_to_le32(len << IXGBE_ADVTXD_PAYLEN_SHIFT);
+
+ /* Avoid any potential race with xdp_xmit and cleanup */
+ smp_wmb();
+
+ /* set next_to_watch value indicating a packet is present */
+ i++;
+ if (i == ring->count)
+ i = 0;
+
+ tx_buffer->next_to_watch = tx_desc;
+ ring->next_to_use = i;
+
+ return IXGBE_XDP_TX;
+}
+
static int ixgbe_run_xdp_zc(struct ixgbe_adapter *adapter,
struct ixgbe_ring *rx_ring,
struct xdp_buff *xdp)
{
int err, result = IXGBE_XDP_PASS;
struct bpf_prog *xdp_prog;
- struct xdp_frame *xdpf;
u32 act;
rcu_read_lock();
@@ -152,12 +210,7 @@ static int ixgbe_run_xdp_zc(struct ixgbe_adapter *adapter,
case XDP_PASS:
break;
case XDP_TX:
- xdpf = convert_to_xdp_frame(xdp);
- if (unlikely(!xdpf)) {
- result = IXGBE_XDP_CONSUMED;
- break;
- }
- result = ixgbe_xmit_xdp_ring(adapter, xdpf);
+ result = ixgbe_xmit_rcvd_zc(adapter, rx_ring, xdp);
break;
case XDP_REDIRECT:
err = xdp_do_redirect(rx_ring->netdev, xdp, xdp_prog);
@@ -576,6 +629,11 @@ static bool ixgbe_xmit_zc(struct ixgbe_ring *xdp_ring, unsigned int budget)
static void ixgbe_clean_xdp_tx_buffer(struct ixgbe_ring *tx_ring,
struct ixgbe_tx_buffer *tx_bi)
{
+ if (tx_bi->tx_flags & IXGBE_TX_FLAGS_ZC_FRAME) {
+ xsk_umem_recycle_addr(tx_ring->xsk_umem, tx_bi->xdpf->handle);
+ tx_bi->tx_flags = 0;
+ return;
+ }
xdp_return_frame(tx_bi->xdpf);
dma_unmap_single(tx_ring->dev,
dma_unmap_addr(tx_bi, dma),
--
2.17.1
^ permalink raw reply related
* [PATCH 2/3 bpf-next] i40e: Support zero-copy XDP_TX on the RX path for AF_XDP sockets.
From: Jonathan Lemon @ 2019-06-28 22:15 UTC (permalink / raw)
To: netdev, bjorn.topel, magnus.karlsson, jakub.kicinski,
jeffrey.t.kirsher
Cc: kernel-team
In-Reply-To: <20190628221555.3009654-1-jonathan.lemon@gmail.com>
When the XDP program attached to a zero-copy AF_XDP socket returns XDP_TX,
queue the umem frame on the XDP TX ring. Space on the recycle stack is
pre-allocated when the xsk is created. (taken from tx_ring, since the
xdp ring is not initialized yet)
Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
---
drivers/net/ethernet/intel/i40e/i40e_txrx.h | 1 +
drivers/net/ethernet/intel/i40e/i40e_xsk.c | 54 +++++++++++++++++++--
2 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
index 100e92d2982f..3e7954277737 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
@@ -274,6 +274,7 @@ static inline unsigned int i40e_txd_use_count(unsigned int size)
#define I40E_TX_FLAGS_TSYN BIT(8)
#define I40E_TX_FLAGS_FD_SB BIT(9)
#define I40E_TX_FLAGS_UDP_TUNNEL BIT(10)
+#define I40E_TX_FLAGS_ZC_FRAME BIT(11)
#define I40E_TX_FLAGS_VLAN_MASK 0xffff0000
#define I40E_TX_FLAGS_VLAN_PRIO_MASK 0xe0000000
#define I40E_TX_FLAGS_VLAN_PRIO_SHIFT 29
diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
index ce8650d06962..020f9859215d 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
@@ -91,7 +91,8 @@ static int i40e_xsk_umem_enable(struct i40e_vsi *vsi, struct xdp_umem *umem,
qid >= netdev->real_num_tx_queues)
return -EINVAL;
- if (!xsk_umem_recycle_alloc(umem, vsi->rx_rings[0]->count))
+ if (!xsk_umem_recycle_alloc(umem, vsi->rx_rings[0]->count +
+ vsi->tx_rings[0]->count))
return -ENOMEM;
err = i40e_xsk_umem_dma_map(vsi, umem);
@@ -175,6 +176,48 @@ int i40e_xsk_umem_setup(struct i40e_vsi *vsi, struct xdp_umem *umem,
i40e_xsk_umem_disable(vsi, qid);
}
+static int i40e_xmit_rcvd_zc(struct i40e_ring *rx_ring, struct xdp_buff *xdp)
+{
+ struct i40e_ring *xdp_ring;
+ struct i40e_tx_desc *tx_desc;
+ struct i40e_tx_buffer *tx_bi;
+ struct xdp_frame *xdpf;
+ dma_addr_t dma;
+
+ xdp_ring = rx_ring->vsi->xdp_rings[rx_ring->queue_index];
+
+ if (!unlikely(I40E_DESC_UNUSED(xdp_ring))) {
+ xdp_ring->tx_stats.tx_busy++;
+ return I40E_XDP_CONSUMED;
+ }
+ xdpf = convert_to_xdp_frame_keep_zc(xdp);
+ if (unlikely(!xdpf))
+ return I40E_XDP_CONSUMED;
+ xdpf->handle = xdp->handle;
+
+ dma = xdp_umem_get_dma(rx_ring->xsk_umem, xdp->handle);
+ tx_bi = &xdp_ring->tx_bi[xdp_ring->next_to_use];
+ tx_bi->bytecount = xdpf->len;
+ tx_bi->gso_segs = 1;
+ tx_bi->xdpf = xdpf;
+ tx_bi->tx_flags = I40E_TX_FLAGS_ZC_FRAME;
+
+ tx_desc = I40E_TX_DESC(xdp_ring, xdp_ring->next_to_use);
+ tx_desc->buffer_addr = cpu_to_le64(dma);
+ tx_desc->cmd_type_offset_bsz = build_ctob(I40E_TX_DESC_CMD_ICRC |
+ I40E_TX_DESC_CMD_EOP,
+ 0, xdpf->len, 0);
+ smp_wmb();
+
+ xdp_ring->next_to_use++;
+ if (xdp_ring->next_to_use == xdp_ring->count)
+ xdp_ring->next_to_use = 0;
+
+ tx_bi->next_to_watch = tx_desc;
+
+ return I40E_XDP_TX;
+}
+
/**
* i40e_run_xdp_zc - Executes an XDP program on an xdp_buff
* @rx_ring: Rx ring
@@ -187,7 +230,6 @@ int i40e_xsk_umem_setup(struct i40e_vsi *vsi, struct xdp_umem *umem,
static int i40e_run_xdp_zc(struct i40e_ring *rx_ring, struct xdp_buff *xdp)
{
int err, result = I40E_XDP_PASS;
- struct i40e_ring *xdp_ring;
struct bpf_prog *xdp_prog;
u32 act;
@@ -202,8 +244,7 @@ static int i40e_run_xdp_zc(struct i40e_ring *rx_ring, struct xdp_buff *xdp)
case XDP_PASS:
break;
case XDP_TX:
- xdp_ring = rx_ring->vsi->xdp_rings[rx_ring->queue_index];
- result = i40e_xmit_xdp_tx_ring(xdp, xdp_ring);
+ result = i40e_xmit_rcvd_zc(rx_ring, xdp);
break;
case XDP_REDIRECT:
err = xdp_do_redirect(rx_ring->netdev, xdp, xdp_prog);
@@ -628,6 +669,11 @@ static bool i40e_xmit_zc(struct i40e_ring *xdp_ring, unsigned int budget)
static void i40e_clean_xdp_tx_buffer(struct i40e_ring *tx_ring,
struct i40e_tx_buffer *tx_bi)
{
+ if (tx_bi->tx_flags & I40E_TX_FLAGS_ZC_FRAME) {
+ xsk_umem_recycle_addr(tx_ring->xsk_umem, tx_bi->xdpf->handle);
+ tx_bi->tx_flags = 0;
+ return;
+ }
xdp_return_frame(tx_bi->xdpf);
dma_unmap_single(tx_ring->dev,
dma_unmap_addr(tx_bi, dma),
--
2.17.1
^ permalink raw reply related
* [PATCH 0/3 bpf-next] intel: AF_XDP support for TX of RX packets
From: Jonathan Lemon @ 2019-06-28 22:15 UTC (permalink / raw)
To: netdev, bjorn.topel, magnus.karlsson, jakub.kicinski,
jeffrey.t.kirsher
Cc: kernel-team
NOTE: This patch depends on my previous "xsk: reuse cleanup" patch,
sent to netdev earlier.
The motivation is to have packets which were received on a zero-copy
AF_XDP socket, and which returned a TX verdict from the bpf program,
queued directly on the TX ring (if they're in the same napi context).
When these TX packets are completed, they are placed back onto the
reuse queue, as there isn't really any other place to handle them.
Space in the reuse queue is preallocated at init time for both the
RX and TX rings. Another option would have a smaller TX queue size
and count in-flight TX packets, dropping any which exceed the reuseq
size - this approach is omitted for simplicity.
Jonathan Lemon (3):
net: add convert_to_xdp_frame_keep_zc function
i40e: Support zero-copy XDP_TX on the RX path for AF_XDP sockets.
ixgbe: Support zero-copy XDP_TX on the RX path for AF_XDP sockets.
drivers/net/ethernet/intel/i40e/i40e_txrx.h | 1 +
drivers/net/ethernet/intel/i40e/i40e_xsk.c | 54 ++++++++++++--
drivers/net/ethernet/intel/ixgbe/ixgbe.h | 1 +
drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c | 74 +++++++++++++++++---
include/net/xdp.h | 20 ++++--
5 files changed, 134 insertions(+), 16 deletions(-)
--
2.17.1
^ permalink raw reply
* Re: [PATCH net-next v6 0/8] net/sched: Add txtime-assist support for taprio.
From: David Miller @ 2019-06-28 21:46 UTC (permalink / raw)
To: vedang.patel
Cc: netdev, jeffrey.t.kirsher, jhs, xiyou.wangcong, jiri,
intel-wired-lan, vinicius.gomes, l, jakub.kicinski, m-karicheri2,
sergei.shtylyov, eric.dumazet, aaron.f.brown
In-Reply-To: <1561500439-30276-1-git-send-email-vedang.patel@intel.com>
From: Vedang Patel <vedang.patel@intel.com>
Date: Tue, 25 Jun 2019 15:07:11 -0700
> Currently, we are seeing packets being transmitted outside their
> timeslices. We can confirm that the packets are being dequeued at the right
> time. So, the delay is induced after the packet is dequeued, because
> taprio, without any offloading, has no control of when a packet is actually
> transmitted.
>
> In order to solve this, we are making use of the txtime feature provided by
> ETF qdisc. Hardware offloading needs to be supported by the ETF qdisc in
> order to take advantage of this feature. The taprio qdisc will assign
> txtime (in skb->tstamp) for all the packets which do not have the txtime
> allocated via the SO_TXTIME socket option. For the packets which already
> have SO_TXTIME set, taprio will validate whether the packet will be
> transmitted in the correct interval.
>
> In order to support this, the following parameters have been added:
> - flags (taprio): This is added in order to support different offloading
> modes which will be added in the future.
> - txtime-delay (taprio): This indicates the minimum time it will take for
> the packet to hit the wire after it reaches taprio_enqueue(). This is
> useful in determining whether we can transmit the packet in the remaining
> time if the gate corresponding to the packet is currently open.
> - skip_skb_check (ETF): ETF currently drops any packet which does not have
> the SO_TXTIME socket option set. This check can be skipped by specifying
> this option.
...
Series applied, thanks.
^ permalink raw reply
* Re: [PATCH net v2] net: mvpp2: prs: Don't override the sign bit in SRAM parser shift
From: David Miller @ 2019-06-28 21:44 UTC (permalink / raw)
To: maxime.chevallier
Cc: netdev, linux-kernel, antoine.tenart, thomas.petazzoni,
gregory.clement, nadavh, stefanc, mw, walan
In-Reply-To: <20190625140412.7e8c84c4@bootlin.com>
From: Maxime Chevallier <maxime.chevallier@bootlin.com>
Date: Tue, 25 Jun 2019 14:04:12 +0200
> I see that this patch was set as "Accepted" on patchwork, but hasn't
> made it to -net, I was wondering if this patch slipped through the
> cracks :)
>
> https://patchwork.ozlabs.org/patch/1119311/
It should really be there now.
I don't know how that happened, honestly ;)
^ permalink raw reply
* Re: [PATCH] net: phylink: further documentation clarifications
From: David Miller @ 2019-06-28 21:43 UTC (permalink / raw)
To: rmk+kernel; +Cc: andrew, f.fainelli, hkallweit1, netdev
In-Reply-To: <E1hfi09-0007Zs-Vb@rmk-PC.armlinux.org.uk>
From: Russell King <rmk+kernel@armlinux.org.uk>
Date: Tue, 25 Jun 2019 10:44:33 +0100
> Clarify the validate() behaviour in a few cases which weren't mentioned
> in the documentation, but which are necessary for users to get the
> correct behaviour.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Applied.
^ permalink raw reply
* [PATCH v2 net-next 07/19] ionic: Add adminq action
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
Add AdminQ specific message requests and completion handling.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/ionic.h | 7 ++
.../net/ethernet/pensando/ionic/ionic_main.c | 106 ++++++++++++++++++
2 files changed, 113 insertions(+)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index c8c93e9efb39..87ab13aee89e 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -43,9 +43,16 @@ struct ionic {
DECLARE_BITMAP(intrs, INTR_CTRL_REGS_MAX);
};
+struct ionic_admin_ctx {
+ struct completion work;
+ union adminq_cmd cmd;
+ union adminq_comp comp;
+};
+
int ionic_napi(struct napi_struct *napi, int budget, ionic_cq_cb cb,
ionic_cq_done_cb done_cb, void *done_arg);
+int ionic_adminq_post_wait(struct lif *lif, struct ionic_admin_ctx *ctx);
int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_wait);
int ionic_set_dma_mask(struct ionic *ionic);
int ionic_setup(struct ionic *ionic);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index aa73ea81a4b2..3b44e1752258 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -169,6 +169,112 @@ static const char *ionic_opcode_to_str(enum cmd_opcode opcode)
}
}
+static void ionic_adminq_flush(struct lif *lif)
+{
+ struct queue *adminq = &lif->adminqcq->q;
+
+ spin_lock(&lif->adminq_lock);
+
+ while (adminq->tail != adminq->head) {
+ memset(adminq->tail->desc, 0, sizeof(union adminq_cmd));
+ adminq->tail->cb = NULL;
+ adminq->tail->cb_arg = NULL;
+ adminq->tail = adminq->tail->next;
+ }
+ spin_unlock(&lif->adminq_lock);
+}
+
+static int ionic_adminq_check_err(struct lif *lif, struct ionic_admin_ctx *ctx,
+ bool timeout)
+{
+ struct net_device *netdev = lif->netdev;
+ const char *opcode_str;
+ const char *status_str;
+ int err = 0;
+
+ if (ctx->comp.comp.status || timeout) {
+ opcode_str = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
+ status_str = ionic_error_to_str(ctx->comp.comp.status);
+ err = timeout ? -ETIMEDOUT :
+ ionic_error_to_errno(ctx->comp.comp.status);
+
+ netdev_err(netdev, "%s (%d) failed: %s (%d)\n",
+ opcode_str, ctx->cmd.cmd.opcode,
+ timeout ? "TIMEOUT" : status_str, err);
+
+ if (timeout)
+ ionic_adminq_flush(lif);
+ }
+
+ return err;
+}
+
+static void ionic_adminq_cb(struct queue *q, struct desc_info *desc_info,
+ struct cq_info *cq_info, void *cb_arg)
+{
+ struct ionic_admin_ctx *ctx = cb_arg;
+ struct admin_comp *comp = cq_info->cq_desc;
+ struct device *dev = &q->lif->netdev->dev;
+
+ if (!ctx)
+ return;
+
+ memcpy(&ctx->comp, comp, sizeof(*comp));
+
+ dev_dbg(dev, "comp admin queue command:\n");
+ dynamic_hex_dump("comp ", DUMP_PREFIX_OFFSET, 16, 1,
+ &ctx->comp, sizeof(ctx->comp), true);
+
+ complete_all(&ctx->work);
+}
+
+static int ionic_adminq_post(struct lif *lif, struct ionic_admin_ctx *ctx)
+{
+ struct queue *adminq = &lif->adminqcq->q;
+ int err = 0;
+
+ WARN_ON(in_interrupt());
+
+ spin_lock(&lif->adminq_lock);
+ if (!ionic_q_has_space(adminq, 1)) {
+ err = -ENOSPC;
+ goto err_out;
+ }
+
+ memcpy(adminq->head->desc, &ctx->cmd, sizeof(ctx->cmd));
+
+ dev_dbg(&lif->netdev->dev, "post admin queue command:\n");
+ dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1,
+ &ctx->cmd, sizeof(ctx->cmd), true);
+
+ ionic_q_post(adminq, true, ionic_adminq_cb, ctx);
+
+err_out:
+ spin_unlock(&lif->adminq_lock);
+
+ return err;
+}
+
+int ionic_adminq_post_wait(struct lif *lif, struct ionic_admin_ctx *ctx)
+{
+ struct net_device *netdev = lif->netdev;
+ unsigned long remaining;
+ const char *name;
+ int err;
+
+ err = ionic_adminq_post(lif, ctx);
+ if (err) {
+ name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
+ netdev_err(netdev, "Posting of %s (%d) failed: %d\n",
+ name, ctx->cmd.cmd.opcode, err);
+ return err;
+ }
+
+ remaining = wait_for_completion_timeout(&ctx->work,
+ HZ * (ulong)devcmd_timeout);
+ return ionic_adminq_check_err(lif, ctx, (remaining == 0));
+}
+
int ionic_napi(struct napi_struct *napi, int budget, ionic_cq_cb cb,
ionic_cq_done_cb done_cb, void *done_arg)
{
--
2.17.1
^ permalink raw reply related
* [PATCH v2 net-next 13/19] ionic: Add initial ethtool support
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
Add in the basic ethtool callbacks for device information
and control.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/Makefile | 2 +-
.../net/ethernet/pensando/ionic/ionic_dev.h | 3 +
.../ethernet/pensando/ionic/ionic_ethtool.c | 509 ++++++++++++++++++
.../ethernet/pensando/ionic/ionic_ethtool.h | 9 +
.../net/ethernet/pensando/ionic/ionic_lif.c | 2 +
.../net/ethernet/pensando/ionic/ionic_lif.h | 8 +
6 files changed, 532 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_ethtool.h
diff --git a/drivers/net/ethernet/pensando/ionic/Makefile b/drivers/net/ethernet/pensando/ionic/Makefile
index 7d9cdc5f02a1..9b19bf57a489 100644
--- a/drivers/net/ethernet/pensando/ionic/Makefile
+++ b/drivers/net/ethernet/pensando/ionic/Makefile
@@ -3,5 +3,5 @@
obj-$(CONFIG_IONIC) := ionic.o
-ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o \
+ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o ionic_ethtool.o \
ionic_lif.o ionic_rx_filter.o ionic_debugfs.o
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
index d44220c1d430..a95af4c4dbf0 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
@@ -12,6 +12,9 @@
#define IONIC_MIN_MTU ETH_MIN_MTU
#define IONIC_MAX_MTU 9194
+#define IONIC_MAX_TXRX_DESC 16384
+#define IONIC_MIN_TXRX_DESC 16
+#define IONIC_DEF_TXRX_DESC 4096
#define IONIC_LIFS_MAX 1024
struct ionic_dev_bar {
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
new file mode 100644
index 000000000000..2bbe5819387b
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include "ionic.h"
+#include "ionic_bus.h"
+#include "ionic_lif.h"
+#include "ionic_ethtool.h"
+
+static void ionic_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic *ionic = lif->ionic;
+ struct ionic_dev *idev = &ionic->idev;
+
+ strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version));
+ strlcpy(drvinfo->fw_version, idev->dev_info.fw_version,
+ sizeof(drvinfo->fw_version));
+ strlcpy(drvinfo->bus_info, ionic_bus_info(ionic),
+ sizeof(drvinfo->bus_info));
+}
+
+#define DEV_CMD_REG_VERSION 1
+#define DEV_INFO_REG_COUNT 32
+#define DEV_CMD_REG_COUNT 32
+static int ionic_get_regs_len(struct net_device *netdev)
+{
+ return (DEV_INFO_REG_COUNT + DEV_CMD_REG_COUNT) * sizeof(u32);
+}
+
+static void ionic_get_regs(struct net_device *netdev, struct ethtool_regs *regs,
+ void *p)
+{
+ struct lif *lif = netdev_priv(netdev);
+ unsigned int size;
+
+ regs->version = DEV_CMD_REG_VERSION;
+
+ size = DEV_INFO_REG_COUNT * sizeof(u32);
+ memcpy_fromio(p, lif->ionic->idev.dev_info_regs->words, size);
+
+ size = DEV_CMD_REG_COUNT * sizeof(u32);
+ memcpy_fromio(p, lif->ionic->idev.dev_cmd_regs->words, size);
+}
+
+static int ionic_get_link_ksettings(struct net_device *netdev,
+ struct ethtool_link_ksettings *ks)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic_dev *idev = &lif->ionic->idev;
+ int copper_seen = 0;
+
+ ethtool_link_ksettings_zero_link_mode(ks, supported);
+ ethtool_link_ksettings_zero_link_mode(ks, advertising);
+
+ switch (le16_to_cpu(idev->port_info->status.xcvr.pid)) {
+ /* Copper */
+ case XCVR_PID_QSFP_100G_CR4:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 100000baseCR4_Full);
+ copper_seen++;
+ break;
+ case XCVR_PID_QSFP_40GBASE_CR4:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 40000baseCR4_Full);
+ copper_seen++;
+ break;
+ case XCVR_PID_SFP_25GBASE_CR_S:
+ case XCVR_PID_SFP_25GBASE_CR_L:
+ case XCVR_PID_SFP_25GBASE_CR_N:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 25000baseCR_Full);
+ copper_seen++;
+ break;
+ case XCVR_PID_SFP_10GBASE_AOC:
+ case XCVR_PID_SFP_10GBASE_CU:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 10000baseCR_Full);
+ copper_seen++;
+ break;
+
+ /* Fibre */
+ case XCVR_PID_QSFP_100G_SR4:
+ case XCVR_PID_QSFP_100G_AOC:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 100000baseSR4_Full);
+ break;
+ case XCVR_PID_QSFP_100G_LR4:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 100000baseLR4_ER4_Full);
+ break;
+ case XCVR_PID_QSFP_100G_ER4:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 100000baseLR4_ER4_Full);
+ break;
+ case XCVR_PID_QSFP_40GBASE_SR4:
+ case XCVR_PID_QSFP_40GBASE_AOC:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 40000baseSR4_Full);
+ break;
+ case XCVR_PID_QSFP_40GBASE_LR4:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 40000baseLR4_Full);
+ break;
+ case XCVR_PID_SFP_25GBASE_SR:
+ case XCVR_PID_SFP_25GBASE_AOC:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 25000baseSR_Full);
+ break;
+ case XCVR_PID_SFP_10GBASE_SR:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 10000baseSR_Full);
+ break;
+ case XCVR_PID_SFP_10GBASE_LR:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 10000baseLR_Full);
+ break;
+ case XCVR_PID_SFP_10GBASE_LRM:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 10000baseLRM_Full);
+ break;
+ case XCVR_PID_SFP_10GBASE_ER:
+ ethtool_link_ksettings_add_link_mode(ks, supported,
+ 10000baseER_Full);
+ break;
+ case XCVR_PID_QSFP_100G_ACC:
+ case XCVR_PID_QSFP_40GBASE_ER4:
+ case XCVR_PID_SFP_25GBASE_LR:
+ case XCVR_PID_SFP_25GBASE_ER:
+ dev_info(lif->ionic->dev, "no decode bits for xcvr type pid=%d / 0x%x\n",
+ idev->port_info->status.xcvr.pid,
+ idev->port_info->status.xcvr.pid);
+ break;
+ case XCVR_PID_UNKNOWN:
+ break;
+ default:
+ dev_info(lif->ionic->dev, "unknown xcvr type pid=%d / 0x%x\n",
+ idev->port_info->status.xcvr.pid,
+ idev->port_info->status.xcvr.pid);
+ break;
+ }
+ ethtool_link_ksettings_add_link_mode(ks, supported, FEC_NONE);
+ ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS);
+ ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER);
+
+ memcpy(ks->link_modes.advertising, ks->link_modes.supported,
+ sizeof(ks->link_modes.advertising));
+
+ ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE);
+
+ if (ionic_is_pf(lif->ionic))
+ ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
+
+ ethtool_link_ksettings_add_link_mode(ks, supported, Pause);
+ if (idev->port_info->config.pause_type)
+ ethtool_link_ksettings_add_link_mode(ks, advertising, Pause);
+
+ if (idev->port_info->status.xcvr.phy == PHY_TYPE_COPPER ||
+ copper_seen) {
+ ks->base.port = PORT_DA;
+ } else if (idev->port_info->status.xcvr.phy == PHY_TYPE_FIBER) {
+ ks->base.port = PORT_FIBRE;
+ } else {
+ ks->base.port = PORT_OTHER;
+ }
+
+ ks->base.speed = le32_to_cpu(lif->info->status.link_speed);
+
+ if (idev->port_info->config.an_enable)
+ ks->base.autoneg = AUTONEG_ENABLE;
+
+ if (le16_to_cpu(lif->info->status.link_status))
+ ks->base.duplex = DUPLEX_FULL;
+ else
+ ks->base.duplex = DUPLEX_UNKNOWN;
+
+ return 0;
+}
+
+static int ionic_set_link_ksettings(struct net_device *netdev,
+ const struct ethtool_link_ksettings *ks)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic *ionic = lif->ionic;
+ struct ionic_dev *idev = &lif->ionic->idev;
+ u8 fec_type = PORT_FEC_TYPE_NONE;
+ u32 req_rs, req_fc;
+ int err = 0;
+
+ /* set autoneg */
+ if (ks->base.autoneg != idev->port_info->config.an_enable) {
+ mutex_lock(&ionic->dev_cmd_lock);
+ ionic_dev_cmd_port_autoneg(idev, ks->base.autoneg);
+ err = ionic_dev_cmd_wait(ionic, devcmd_timeout);
+ mutex_unlock(&ionic->dev_cmd_lock);
+ if (err)
+ return err;
+
+ idev->port_info->config.an_enable = ks->base.autoneg;
+ }
+
+ /* set speed */
+ if (ks->base.speed != le32_to_cpu(idev->port_info->config.speed)) {
+ mutex_lock(&ionic->dev_cmd_lock);
+ ionic_dev_cmd_port_speed(idev, ks->base.speed);
+ err = ionic_dev_cmd_wait(ionic, devcmd_timeout);
+ mutex_unlock(&ionic->dev_cmd_lock);
+ if (err)
+ return err;
+
+ idev->port_info->config.speed = cpu_to_le32(ks->base.speed);
+ }
+
+ /* set FEC */
+ req_rs = ethtool_link_ksettings_test_link_mode(ks, advertising, FEC_RS);
+ req_fc = ethtool_link_ksettings_test_link_mode(ks, advertising, FEC_BASER);
+ if (req_rs && req_fc) {
+ netdev_info(netdev, "Only select one FEC mode at a time\n");
+ return -EINVAL;
+
+ } else if (req_fc &&
+ idev->port_info->config.fec_type != PORT_FEC_TYPE_FC) {
+ fec_type = PORT_FEC_TYPE_FC;
+ } else if (req_rs &&
+ idev->port_info->config.fec_type != PORT_FEC_TYPE_RS) {
+ fec_type = PORT_FEC_TYPE_RS;
+ } else if (!(req_rs | req_fc) &&
+ idev->port_info->config.fec_type != PORT_FEC_TYPE_NONE) {
+ fec_type = PORT_FEC_TYPE_NONE;
+ }
+
+ if (fec_type != idev->port_info->config.fec_type) {
+ mutex_lock(&ionic->dev_cmd_lock);
+ ionic_dev_cmd_port_fec(idev, PORT_FEC_TYPE_NONE);
+ err = ionic_dev_cmd_wait(ionic, devcmd_timeout);
+ mutex_unlock(&ionic->dev_cmd_lock);
+ if (err)
+ return err;
+
+ idev->port_info->config.fec_type = fec_type;
+ }
+
+ return 0;
+}
+
+static void ionic_get_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *pause)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic_dev *idev = &lif->ionic->idev;
+ uint8_t pause_type = idev->port_info->config.pause_type;
+
+ pause->autoneg = idev->port_info->config.an_enable;
+
+ if (pause_type) {
+ pause->rx_pause = pause_type & IONIC_PAUSE_F_RX ? 1 : 0;
+ pause->tx_pause = pause_type & IONIC_PAUSE_F_TX ? 1 : 0;
+ }
+}
+
+static int ionic_set_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *pause)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic *ionic = lif->ionic;
+ struct ionic_dev *idev = &lif->ionic->idev;
+
+ u32 requested_pause;
+ u32 cur_autoneg;
+ int err;
+
+ cur_autoneg = idev->port_info->config.an_enable ? AUTONEG_ENABLE :
+ AUTONEG_DISABLE;
+ if (pause->autoneg != cur_autoneg) {
+ netdev_info(netdev, "Please use 'ethtool -s ...' to change autoneg\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* change both at the same time */
+ requested_pause = PORT_PAUSE_TYPE_LINK;
+ if (pause->rx_pause)
+ requested_pause |= IONIC_PAUSE_F_RX;
+ if (pause->tx_pause)
+ requested_pause |= IONIC_PAUSE_F_TX;
+
+ if (requested_pause == idev->port_info->config.pause_type)
+ return 0;
+
+ idev->port_info->config.pause_type = requested_pause;
+
+ mutex_lock(&ionic->dev_cmd_lock);
+ ionic_dev_cmd_port_pause(idev, requested_pause);
+ err = ionic_dev_cmd_wait(ionic, devcmd_timeout);
+ mutex_unlock(&ionic->dev_cmd_lock);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int ionic_get_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *coalesce)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ coalesce->tx_coalesce_usecs = lif->tx_coalesce_usecs;
+ coalesce->rx_coalesce_usecs = lif->rx_coalesce_usecs;
+
+ return 0;
+}
+
+static void ionic_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ ring->tx_max_pending = IONIC_MAX_TXRX_DESC;
+ ring->tx_pending = lif->ntxq_descs;
+ ring->rx_max_pending = IONIC_MAX_TXRX_DESC;
+ ring->rx_pending = lif->nrxq_descs;
+}
+
+static int ionic_set_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct lif *lif = netdev_priv(netdev);
+ bool running;
+ int i, j;
+
+ if (ring->rx_mini_pending || ring->rx_jumbo_pending) {
+ netdev_info(netdev, "Changing jumbo or mini descriptors not supported\n");
+ return -EINVAL;
+ }
+
+ i = ring->tx_pending & (ring->tx_pending - 1);
+ j = ring->rx_pending & (ring->rx_pending - 1);
+ if (i || j) {
+ netdev_info(netdev, "Descriptor count must be a power of 2\n");
+ return -EINVAL;
+ }
+
+ /* if nothing to do return success */
+ if (ring->tx_pending == lif->ntxq_descs &&
+ ring->rx_pending == lif->nrxq_descs)
+ return 0;
+
+ while (test_and_set_bit(LIF_QUEUE_RESET, lif->state))
+ usleep_range(200, 400);
+
+ running = test_bit(LIF_UP, lif->state);
+ if (running)
+ ionic_stop(netdev);
+
+ lif->ntxq_descs = ring->tx_pending;
+ lif->nrxq_descs = ring->rx_pending;
+
+ if (running)
+ ionic_open(netdev);
+ clear_bit(LIF_QUEUE_RESET, lif->state);
+
+ return 0;
+}
+
+static void ionic_get_channels(struct net_device *netdev,
+ struct ethtool_channels *ch)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ /* report maximum channels */
+ ch->max_combined = lif->ionic->ntxqs_per_lif;
+
+ /* report current channels */
+ ch->combined_count = lif->nxqs;
+}
+
+static int ionic_set_channels(struct net_device *netdev,
+ struct ethtool_channels *ch)
+{
+ struct lif *lif = netdev_priv(netdev);
+ bool running;
+
+ if (!ch->combined_count || ch->other_count ||
+ ch->rx_count || ch->tx_count)
+ return -EINVAL;
+
+ if (ch->combined_count == lif->nxqs)
+ return 0;
+
+ while (test_and_set_bit(LIF_QUEUE_RESET, lif->state))
+ usleep_range(200, 400);
+
+ running = test_bit(LIF_UP, lif->state);
+ if (running)
+ ionic_stop(netdev);
+
+ lif->nxqs = ch->combined_count;
+
+ if (running)
+ ionic_open(netdev);
+ clear_bit(LIF_QUEUE_RESET, lif->state);
+
+ return 0;
+}
+
+static int ionic_get_module_info(struct net_device *netdev,
+ struct ethtool_modinfo *modinfo)
+
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic_dev *idev = &lif->ionic->idev;
+ struct xcvr_status *xcvr;
+
+ xcvr = &idev->port_info->status.xcvr;
+
+ /* report the module data type and length */
+ switch (xcvr->sprom[0]) {
+ case 0x03: /* SFP */
+ modinfo->type = ETH_MODULE_SFF_8079;
+ modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
+ break;
+ case 0x0D: /* QSFP */
+ case 0x11: /* QSFP28 */
+ modinfo->type = ETH_MODULE_SFF_8436;
+ modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
+ break;
+ default:
+ netdev_info(netdev, "unknown xcvr type 0x%02x\n",
+ xcvr->sprom[0]);
+ break;
+ }
+
+ return 0;
+}
+
+static int ionic_get_module_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *ee,
+ u8 *data)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic_dev *idev = &lif->ionic->idev;
+ struct xcvr_status *xcvr;
+ u32 len;
+
+ /* copy the module bytes into data */
+ xcvr = &idev->port_info->status.xcvr;
+ len = min_t(u32, sizeof(xcvr->sprom), ee->len);
+ memcpy(data, xcvr->sprom, len);
+
+ dev_dbg(&lif->netdev->dev, "notifyblock eid=0x%llx link_status=0x%x link_speed=0x%x link_down_cnt=0x%x\n",
+ lif->info->status.eid,
+ lif->info->status.link_status,
+ lif->info->status.link_speed,
+ lif->info->status.link_down_count);
+ dev_dbg(&lif->netdev->dev, " port_status id=0x%x status=0x%x speed=0x%x\n",
+ idev->port_info->status.id,
+ idev->port_info->status.status,
+ idev->port_info->status.speed);
+ dev_dbg(&lif->netdev->dev, " xcvr status state=0x%x phy=0x%x pid=0x%x\n",
+ xcvr->state, xcvr->phy, xcvr->pid);
+ dev_dbg(&lif->netdev->dev, " port_config state=0x%x speed=0x%x mtu=0x%x an_enable=0x%x fec_type=0x%x pause_type=0x%x loopback_mode=0x%x\n",
+ idev->port_info->config.state,
+ idev->port_info->config.speed,
+ idev->port_info->config.mtu,
+ idev->port_info->config.an_enable,
+ idev->port_info->config.fec_type,
+ idev->port_info->config.pause_type,
+ idev->port_info->config.loopback_mode);
+
+ return 0;
+}
+
+static int ionic_nway_reset(struct net_device *netdev)
+{
+ struct lif *lif = netdev_priv(netdev);
+ int err = 0;
+
+ if (netif_running(netdev))
+ err = ionic_reset_queues(lif);
+
+ return err;
+}
+
+static const struct ethtool_ops ionic_ethtool_ops = {
+ .get_drvinfo = ionic_get_drvinfo,
+ .get_regs_len = ionic_get_regs_len,
+ .get_regs = ionic_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_link_ksettings = ionic_get_link_ksettings,
+ .get_coalesce = ionic_get_coalesce,
+ .get_ringparam = ionic_get_ringparam,
+ .set_ringparam = ionic_set_ringparam,
+ .get_channels = ionic_get_channels,
+ .set_channels = ionic_set_channels,
+ .get_module_info = ionic_get_module_info,
+ .get_module_eeprom = ionic_get_module_eeprom,
+ .get_pauseparam = ionic_get_pauseparam,
+ .set_pauseparam = ionic_set_pauseparam,
+ .set_link_ksettings = ionic_set_link_ksettings,
+ .nway_reset = ionic_nway_reset,
+};
+
+void ionic_ethtool_set_ops(struct net_device *netdev)
+{
+ netdev->ethtool_ops = &ionic_ethtool_ops;
+}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.h b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.h
new file mode 100644
index 000000000000..38b91b1d70ae
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#ifndef _IONIC_ETHTOOL_H_
+#define _IONIC_ETHTOOL_H_
+
+void ionic_ethtool_set_ops(struct net_device *netdev);
+
+#endif /* _IONIC_ETHTOOL_H_ */
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index be7ee002b11d..c14dee1b8b99 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -10,6 +10,7 @@
#include "ionic.h"
#include "ionic_bus.h"
#include "ionic_lif.h"
+#include "ionic_ethtool.h"
#include "ionic_debugfs.h"
static void ionic_lif_rx_mode(struct lif *lif, unsigned int rx_mode);
@@ -980,6 +981,7 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
lif->netdev = netdev;
ionic->master_lif = lif;
netdev->netdev_ops = &ionic_netdev_ops;
+ ionic_ethtool_set_ops(netdev);
netdev->watchdog_timeo = 2 * HZ;
netdev->min_mtu = IONIC_MIN_MTU;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 9930b9390c8a..d8589a306aa5 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -111,6 +111,8 @@ struct lif {
u64 last_eid;
unsigned int neqs;
unsigned int nxqs;
+ unsigned int ntxq_descs;
+ unsigned int nrxq_descs;
unsigned int rx_mode;
u64 hw_features;
bool mc_overflow;
@@ -124,6 +126,8 @@ struct lif {
struct rx_filters rx_filters;
struct ionic_deferred deferred;
+ u32 tx_coalesce_usecs;
+ u32 rx_coalesce_usecs;
unsigned long *dbid_inuse;
unsigned int dbid_count;
struct dentry *dentry;
@@ -165,6 +169,10 @@ int ionic_lif_identify(struct ionic *ionic, u8 lif_type,
union lif_identity *lif_ident);
int ionic_lifs_size(struct ionic *ionic);
+int ionic_open(struct net_device *netdev);
+int ionic_stop(struct net_device *netdev);
+int ionic_reset_queues(struct lif *lif);
+
static inline void debug_stats_napi_poll(struct qcq *qcq,
unsigned int work_done)
{
--
2.17.1
^ permalink raw reply related
* [PATCH v2 net-next 15/19] ionic: Add netdev-event handling
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
When the netdev gets a new name from userland, pass that name
down to the NIC for internal tracking.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/ionic.h | 2 +
.../net/ethernet/pensando/ionic/ionic_lif.c | 65 +++++++++++++++++++
2 files changed, 67 insertions(+)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index d7eee79b2a10..9b720187b549 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -42,6 +42,8 @@ struct ionic {
DECLARE_BITMAP(lifbits, IONIC_LIFS_MAX);
unsigned int nintrs;
DECLARE_BITMAP(intrs, INTR_CTRL_REGS_MAX);
+ struct work_struct nb_work;
+ struct notifier_block nb;
};
struct ionic_admin_ctx {
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index b47ed322496f..76974c61c306 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -1956,10 +1956,69 @@ int ionic_lifs_init(struct ionic *ionic)
return 0;
}
+static void ionic_lif_notify_work(struct work_struct *ws)
+{
+}
+
+static void ionic_lif_set_netdev_info(struct lif *lif)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.lif_setattr = {
+ .opcode = CMD_OPCODE_LIF_SETATTR,
+ .index = cpu_to_le16(lif->index),
+ .attr = IONIC_LIF_ATTR_NAME,
+ },
+ };
+
+ strlcpy(ctx.cmd.lif_setattr.name, lif->netdev->name,
+ sizeof(ctx.cmd.lif_setattr.name));
+
+ dev_info(lif->ionic->dev, "NETDEV_CHANGENAME %s %s\n",
+ lif->name, ctx.cmd.lif_setattr.name);
+
+ ionic_adminq_post_wait(lif, &ctx);
+}
+
+static struct lif *ionic_netdev_lif(struct net_device *netdev)
+{
+ if (!netdev || netdev->netdev_ops->ndo_start_xmit != ionic_start_xmit)
+ return NULL;
+
+ return netdev_priv(netdev);
+}
+
+static int ionic_lif_notify(struct notifier_block *nb,
+ unsigned long event, void *info)
+{
+ struct net_device *ndev = netdev_notifier_info_to_dev(info);
+ struct ionic *ionic = container_of(nb, struct ionic, nb);
+ struct lif *lif = ionic_netdev_lif(ndev);
+
+ if (!lif || lif->ionic != ionic)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_CHANGENAME:
+ ionic_lif_set_netdev_info(lif);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
int ionic_lifs_register(struct ionic *ionic)
{
int err;
+ INIT_WORK(&ionic->nb_work, ionic_lif_notify_work);
+
+ ionic->nb.notifier_call = ionic_lif_notify;
+
+ err = register_netdevice_notifier(&ionic->nb);
+ if (err)
+ ionic->nb.notifier_call = NULL;
+
/* only register LIF0 for now */
err = register_netdev(ionic->master_lif->netdev);
if (err) {
@@ -1975,6 +2034,12 @@ int ionic_lifs_register(struct ionic *ionic)
void ionic_lifs_unregister(struct ionic *ionic)
{
+ if (ionic->nb.notifier_call) {
+ unregister_netdevice_notifier(&ionic->nb);
+ cancel_work_sync(&ionic->nb_work);
+ ionic->nb.notifier_call = NULL;
+ }
+
/* There is only one lif ever registered in the
* current model, so don't bother searching the
* ionic->lif for candidates to unregister
--
2.17.1
^ permalink raw reply related
* [PATCH v2 net-next 14/19] ionic: Add Tx and Rx handling
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
Add both the Tx and Rx queue setup and handling. The related
stats display comes later. Instead of using the generic napi
routines used by the slow-path commands, the Tx and Rx paths
are simplified and inlined in one file in order to get better
compiler optimizations.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/Makefile | 2 +-
.../net/ethernet/pensando/ionic/ionic_lif.c | 387 ++++++++
.../net/ethernet/pensando/ionic/ionic_lif.h | 52 ++
.../net/ethernet/pensando/ionic/ionic_txrx.c | 880 ++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_txrx.h | 15 +
5 files changed, 1335 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_txrx.c
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_txrx.h
diff --git a/drivers/net/ethernet/pensando/ionic/Makefile b/drivers/net/ethernet/pensando/ionic/Makefile
index 9b19bf57a489..0e2dc53f08d4 100644
--- a/drivers/net/ethernet/pensando/ionic/Makefile
+++ b/drivers/net/ethernet/pensando/ionic/Makefile
@@ -4,4 +4,4 @@
obj-$(CONFIG_IONIC) := ionic.o
ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o ionic_ethtool.o \
- ionic_lif.o ionic_rx_filter.o ionic_debugfs.o
+ ionic_lif.o ionic_rx_filter.o ionic_txrx.o ionic_debugfs.o
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index c14dee1b8b99..b47ed322496f 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -10,6 +10,7 @@
#include "ionic.h"
#include "ionic_bus.h"
#include "ionic_lif.h"
+#include "ionic_txrx.h"
#include "ionic_ethtool.h"
#include "ionic_debugfs.h"
@@ -18,6 +19,13 @@ static int ionic_lif_addr_add(struct lif *lif, const u8 *addr);
static int ionic_lif_addr_del(struct lif *lif, const u8 *addr);
static void ionic_link_status_check(struct lif *lif);
+static int ionic_lif_stop(struct lif *lif);
+static int ionic_txrx_alloc(struct lif *lif);
+static int ionic_txrx_init(struct lif *lif);
+static void ionic_qcq_free(struct lif *lif, struct qcq *qcq);
+static int ionic_lif_txqs_init(struct lif *lif);
+static int ionic_lif_rxqs_init(struct lif *lif);
+static void ionic_lif_qcq_deinit(struct lif *lif, struct qcq *qcq);
static int ionic_set_nic_features(struct lif *lif, netdev_features_t features);
static int ionic_notifyq_clean(struct lif *lif, int budget);
@@ -66,12 +74,96 @@ static void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
schedule_work(&def->work);
}
+static int ionic_qcq_enable(struct qcq *qcq)
+{
+ struct queue *q = &qcq->q;
+ struct lif *lif = q->lif;
+ struct device *dev = lif->ionic->dev;
+ struct ionic_dev *idev = &lif->ionic->idev;
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.q_control = {
+ .opcode = CMD_OPCODE_Q_CONTROL,
+ .lif_index = cpu_to_le16(lif->index),
+ .type = q->type,
+ .index = cpu_to_le32(q->index),
+ .oper = IONIC_Q_ENABLE,
+ },
+ };
+
+ dev_dbg(dev, "q_enable.index %d q_enable.qtype %d\n",
+ ctx.cmd.q_control.index, ctx.cmd.q_control.type);
+
+ if (qcq->flags & QCQ_F_INTR) {
+ irq_set_affinity_hint(qcq->intr.vector,
+ &qcq->intr.affinity_mask);
+ napi_enable(&qcq->napi);
+ ionic_intr_clean(idev->intr_ctrl, qcq->intr.index);
+ ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
+ IONIC_INTR_MASK_CLEAR);
+ }
+
+ return ionic_adminq_post_wait(lif, &ctx);
+}
+
+static int ionic_qcq_disable(struct qcq *qcq)
+{
+ struct queue *q = &qcq->q;
+ struct lif *lif = q->lif;
+ struct device *dev = lif->ionic->dev;
+ struct ionic_dev *idev = &lif->ionic->idev;
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.q_control = {
+ .opcode = CMD_OPCODE_Q_CONTROL,
+ .lif_index = cpu_to_le16(lif->index),
+ .type = q->type,
+ .index = cpu_to_le32(q->index),
+ .oper = IONIC_Q_DISABLE,
+ },
+ };
+
+ dev_dbg(dev, "q_disable.index %d q_disable.qtype %d\n",
+ ctx.cmd.q_control.index, ctx.cmd.q_control.type);
+
+ if (qcq->flags & QCQ_F_INTR) {
+ ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
+ IONIC_INTR_MASK_SET);
+ synchronize_irq(qcq->intr.vector);
+ irq_set_affinity_hint(qcq->intr.vector, NULL);
+ napi_disable(&qcq->napi);
+ }
+
+ return ionic_adminq_post_wait(lif, &ctx);
+}
+
int ionic_open(struct net_device *netdev)
{
struct lif *lif = netdev_priv(netdev);
+ unsigned int i;
+ int err;
netif_carrier_off(netdev);
+ err = ionic_txrx_alloc(lif);
+ if (err)
+ return err;
+
+ err = ionic_txrx_init(lif);
+ if (err)
+ goto err_out;
+
+ for (i = 0; i < lif->nxqs; i++) {
+ err = ionic_qcq_enable(lif->txqcqs[i].qcq);
+ if (err)
+ goto err_out;
+
+ ionic_rx_fill(&lif->rxqcqs[i].qcq->q);
+ err = ionic_qcq_enable(lif->rxqcqs[i].qcq);
+ if (err)
+ goto err_out;
+ }
+
set_bit(LIF_UP, lif->state);
ionic_link_status_check(lif);
@@ -79,11 +171,16 @@ int ionic_open(struct net_device *netdev)
netif_tx_wake_all_queues(netdev);
return 0;
+
+err_out:
+ ionic_lif_stop(lif);
+ return err;
}
static int ionic_lif_stop(struct lif *lif)
{
struct net_device *ndev = lif->netdev;
+ unsigned int i;
int err = 0;
if (!test_bit(LIF_UP, lif->state)) {
@@ -100,6 +197,21 @@ static int ionic_lif_stop(struct lif *lif)
netif_tx_disable(ndev);
synchronize_rcu();
+ for (i = 0; i < lif->nxqs; i++) {
+ (void)ionic_qcq_disable(lif->txqcqs[i].qcq);
+ ionic_tx_flush(&lif->txqcqs[i].qcq->cq);
+ ionic_lif_qcq_deinit(lif, lif->txqcqs[i].qcq);
+ ionic_qcq_free(lif, lif->txqcqs[i].qcq);
+ lif->txqcqs[i].qcq = NULL;
+
+ (void)ionic_qcq_disable(lif->rxqcqs[i].qcq);
+ ionic_rx_flush(&lif->rxqcqs[i].qcq->cq);
+ ionic_lif_qcq_deinit(lif, lif->rxqcqs[i].qcq);
+ ionic_rx_empty(&lif->rxqcqs[i].qcq->q);
+ ionic_qcq_free(lif, lif->rxqcqs[i].qcq);
+ lif->rxqcqs[i].qcq = NULL;
+ }
+
return err;
}
@@ -694,6 +806,7 @@ static int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto,
static const struct net_device_ops ionic_netdev_ops = {
.ndo_open = ionic_open,
.ndo_stop = ionic_stop,
+ .ndo_start_xmit = ionic_start_xmit,
.ndo_get_stats64 = ionic_get_stats64,
.ndo_set_rx_mode = ionic_set_rx_mode,
.ndo_set_features = ionic_set_features,
@@ -909,10 +1022,83 @@ static void ionic_qcq_free(struct lif *lif, struct qcq *qcq)
devm_kfree(dev, qcq);
}
+static int ionic_txrx_alloc(struct lif *lif)
+{
+ unsigned int flags;
+ unsigned int i;
+ int err = 0;
+
+ flags = QCQ_F_TX_STATS | QCQ_F_SG;
+ for (i = 0; i < lif->nxqs; i++) {
+ err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags,
+ lif->ntxq_descs,
+ sizeof(struct txq_desc),
+ sizeof(struct txq_comp),
+ sizeof(struct txq_sg_desc),
+ lif->kern_pid, &lif->txqcqs[i].qcq);
+ if (err)
+ goto err_out_free_txqcqs;
+
+ lif->txqcqs[i].qcq->stats = lif->txqcqs[i].stats;
+ }
+
+ flags = QCQ_F_RX_STATS | QCQ_F_INTR;
+ for (i = 0; i < lif->nxqs; i++) {
+ err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags,
+ lif->nrxq_descs,
+ sizeof(struct rxq_desc),
+ sizeof(struct rxq_comp),
+ 0, lif->kern_pid, &lif->rxqcqs[i].qcq);
+ if (err)
+ goto err_out_free_rxqcqs;
+
+ lif->rxqcqs[i].qcq->stats = lif->rxqcqs[i].stats;
+
+ ionic_link_qcq_interrupts(lif->rxqcqs[i].qcq,
+ lif->txqcqs[i].qcq);
+ }
+
+ return 0;
+
+err_out_free_rxqcqs:
+ for (i = 0; i < lif->nxqs; i++)
+ ionic_qcq_free(lif, lif->rxqcqs[i].qcq);
+err_out_free_txqcqs:
+ for (i = 0; i < lif->nxqs; i++)
+ ionic_qcq_free(lif, lif->txqcqs[i].qcq);
+
+ return err;
+}
+
+static int ionic_txrx_init(struct lif *lif)
+{
+ int err;
+
+ err = ionic_lif_txqs_init(lif);
+ if (err)
+ return err;
+
+ err = ionic_lif_rxqs_init(lif);
+ if (err)
+ goto err_out;
+
+ ionic_set_rx_mode(lif->netdev);
+
+ return 0;
+
+err_out:
+ ionic_stop(lif->netdev);
+
+ return err;
+}
+
static int ionic_qcqs_alloc(struct lif *lif)
{
+ struct device *dev = lif->ionic->dev;
+ unsigned int q_list_size;
unsigned int flags;
int err;
+ int i;
flags = QCQ_F_INTR;
err = ionic_qcq_alloc(lif, IONIC_QTYPE_ADMINQ, 0, "admin", flags,
@@ -937,8 +1123,47 @@ static int ionic_qcqs_alloc(struct lif *lif)
ionic_link_qcq_interrupts(lif->adminqcq, lif->notifyqcq);
}
+ q_list_size = sizeof(*lif->txqcqs) * lif->nxqs;
+ err = -ENOMEM;
+ lif->txqcqs = devm_kzalloc(dev, q_list_size, GFP_KERNEL);
+ if (!lif->txqcqs)
+ goto err_out_free_notifyqcq;
+ for (i = 0; i < lif->nxqs; i++) {
+ lif->txqcqs[i].stats = devm_kzalloc(dev, sizeof(struct q_stats),
+ GFP_KERNEL);
+ if (!lif->txqcqs[i].stats)
+ goto err_out_free_tx_stats;
+ }
+
+ lif->rxqcqs = devm_kzalloc(dev, q_list_size, GFP_KERNEL);
+ if (!lif->rxqcqs)
+ goto err_out_free_tx_stats;
+ for (i = 0; i < lif->nxqs; i++) {
+ lif->rxqcqs[i].stats = devm_kzalloc(dev, sizeof(struct q_stats),
+ GFP_KERNEL);
+ if (!lif->rxqcqs[i].stats)
+ goto err_out_free_rx_stats;
+ }
+
return 0;
+err_out_free_rx_stats:
+ for (i = 0; i < lif->nxqs; i++)
+ if (lif->rxqcqs[i].stats)
+ devm_kfree(dev, lif->rxqcqs[i].stats);
+ devm_kfree(dev, lif->rxqcqs);
+ lif->rxqcqs = NULL;
+err_out_free_tx_stats:
+ for (i = 0; i < lif->nxqs; i++)
+ if (lif->txqcqs[i].stats)
+ devm_kfree(dev, lif->txqcqs[i].stats);
+ devm_kfree(dev, lif->txqcqs);
+ lif->txqcqs = NULL;
+err_out_free_notifyqcq:
+ if (lif->notifyqcq) {
+ ionic_qcq_free(lif, lif->notifyqcq);
+ lif->notifyqcq = NULL;
+ }
err_out_free_adminqcq:
ionic_qcq_free(lif, lif->adminqcq);
lif->adminqcq = NULL;
@@ -948,6 +1173,9 @@ static int ionic_qcqs_alloc(struct lif *lif)
static void ionic_qcqs_free(struct lif *lif)
{
+ struct device *dev = lif->ionic->dev;
+ unsigned int i;
+
if (lif->notifyqcq) {
ionic_qcq_free(lif, lif->notifyqcq);
lif->notifyqcq = NULL;
@@ -957,6 +1185,20 @@ static void ionic_qcqs_free(struct lif *lif)
ionic_qcq_free(lif, lif->adminqcq);
lif->adminqcq = NULL;
}
+
+ for (i = 0; i < lif->nxqs; i++)
+ if (lif->rxqcqs[i].stats)
+ devm_kfree(dev, lif->rxqcqs[i].stats);
+
+ devm_kfree(dev, lif->rxqcqs);
+ lif->rxqcqs = NULL;
+
+ for (i = 0; i < lif->nxqs; i++)
+ if (lif->txqcqs[i].stats)
+ devm_kfree(dev, lif->txqcqs[i].stats);
+
+ devm_kfree(dev, lif->txqcqs);
+ lif->txqcqs = NULL;
}
static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
@@ -992,6 +1234,8 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
lif->ionic = ionic;
lif->index = index;
+ lif->ntxq_descs = IONIC_DEF_TXRX_DESC;
+ lif->nrxq_descs = IONIC_DEF_TXRX_DESC;
snprintf(lif->name, sizeof(lif->name), "lif%u", index);
@@ -1434,6 +1678,147 @@ static int ionic_init_nic_features(struct lif *lif)
return 0;
}
+static int ionic_lif_txq_init(struct lif *lif, struct qcq *qcq)
+{
+ struct device *dev = lif->ionic->dev;
+ struct queue *q = &qcq->q;
+ struct cq *cq = &qcq->cq;
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.q_init = {
+ .opcode = CMD_OPCODE_Q_INIT,
+ .lif_index = cpu_to_le16(lif->index),
+ .type = q->type,
+ .index = cpu_to_le32(q->index),
+ .flags = cpu_to_le16(IONIC_QINIT_F_IRQ |
+ IONIC_QINIT_F_SG),
+ .intr_index = cpu_to_le16(lif->rxqcqs[q->index].qcq->intr.index),
+ .pid = cpu_to_le16(q->pid),
+ .ring_size = ilog2(q->num_descs),
+ .ring_base = cpu_to_le64(q->base_pa),
+ .cq_ring_base = cpu_to_le64(cq->base_pa),
+ .sg_ring_base = cpu_to_le64(q->sg_base_pa),
+ },
+ };
+ int err;
+
+ dev_dbg(dev, "txq_init.pid %d\n", ctx.cmd.q_init.pid);
+ dev_dbg(dev, "txq_init.index %d\n", ctx.cmd.q_init.index);
+ dev_dbg(dev, "txq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base);
+ dev_dbg(dev, "txq_init.ring_size %d\n", ctx.cmd.q_init.ring_size);
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ q->hw_type = ctx.comp.q_init.hw_type;
+ q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index);
+ q->dbval = IONIC_DBELL_QID(q->hw_index);
+
+ dev_dbg(dev, "txq->hw_type %d\n", q->hw_type);
+ dev_dbg(dev, "txq->hw_index %d\n", q->hw_index);
+
+ qcq->flags |= QCQ_F_INITED;
+
+ ionic_debugfs_add_qcq(lif, qcq);
+
+ return 0;
+}
+
+static int ionic_lif_txqs_init(struct lif *lif)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < lif->nxqs; i++) {
+ err = ionic_lif_txq_init(lif, lif->txqcqs[i].qcq);
+ if (err)
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ for (; i > 0; i--)
+ ionic_lif_qcq_deinit(lif, lif->txqcqs[i-1].qcq);
+
+ return err;
+}
+
+static int ionic_lif_rxq_init(struct lif *lif, struct qcq *qcq)
+{
+ struct device *dev = lif->ionic->dev;
+ struct queue *q = &qcq->q;
+ struct cq *cq = &qcq->cq;
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.q_init = {
+ .opcode = CMD_OPCODE_Q_INIT,
+ .lif_index = cpu_to_le16(lif->index),
+ .type = q->type,
+ .index = cpu_to_le32(q->index),
+ .flags = cpu_to_le16(IONIC_QINIT_F_IRQ),
+ .intr_index = cpu_to_le16(cq->bound_intr->index),
+ .pid = cpu_to_le16(q->pid),
+ .ring_size = ilog2(q->num_descs),
+ .ring_base = cpu_to_le64(q->base_pa),
+ .cq_ring_base = cpu_to_le64(cq->base_pa),
+ },
+ };
+ int err;
+
+ dev_dbg(dev, "rxq_init.pid %d\n", ctx.cmd.q_init.pid);
+ dev_dbg(dev, "rxq_init.index %d\n", ctx.cmd.q_init.index);
+ dev_dbg(dev, "rxq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base);
+ dev_dbg(dev, "rxq_init.ring_size %d\n", ctx.cmd.q_init.ring_size);
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ q->hw_type = ctx.comp.q_init.hw_type;
+ q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index);
+ q->dbval = IONIC_DBELL_QID(q->hw_index);
+
+ dev_dbg(dev, "rxq->hw_type %d\n", q->hw_type);
+ dev_dbg(dev, "rxq->hw_index %d\n", q->hw_index);
+
+ netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi,
+ NAPI_POLL_WEIGHT);
+
+ err = ionic_request_irq(lif, qcq);
+ if (err) {
+ netif_napi_del(&qcq->napi);
+ return err;
+ }
+
+ qcq->flags |= QCQ_F_INITED;
+
+ ionic_debugfs_add_qcq(lif, qcq);
+
+ return 0;
+}
+
+static int ionic_lif_rxqs_init(struct lif *lif)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < lif->nxqs; i++) {
+ err = ionic_lif_rxq_init(lif, lif->rxqcqs[i].qcq);
+ if (err)
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ for (; i > 0; i--)
+ ionic_lif_qcq_deinit(lif, lif->rxqcqs[i-1].qcq);
+
+ return err;
+}
+
static int ionic_station_set(struct lif *lif)
{
struct net_device *netdev = lif->netdev;
@@ -1533,6 +1918,8 @@ static int ionic_lif_init(struct lif *lif)
if (err)
goto err_out_notifyq_deinit;
+ lif->rx_copybreak = IONIC_RX_COPYBREAK_DEFAULT;
+
set_bit(LIF_INITED, lif->state);
ionic_link_status_check(lif);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index d8589a306aa5..88d5bf8f58a1 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -14,20 +14,38 @@
#define MAX_NUM_NAPI_CNTR (NAPI_POLL_WEIGHT + 1)
#define GET_SG_CNTR_IDX(num_sg_elems) (num_sg_elems)
#define MAX_NUM_SG_CNTR (IONIC_TX_MAX_SG_ELEMS + 1)
+#define IONIC_RX_COPYBREAK_DEFAULT 256
struct tx_stats {
+ u64 dma_map_err;
u64 pkts;
u64 bytes;
+ u64 clean;
+ u64 linearize;
+ u64 no_csum;
+ u64 csum;
+ u64 crc32_csum;
+ u64 tso;
+ u64 frags;
+ u64 sg_cntr[MAX_NUM_SG_CNTR];
};
struct rx_stats {
+ u64 dma_map_err;
+ u64 alloc_err;
u64 pkts;
u64 bytes;
+ u64 csum_none;
+ u64 csum_complete;
+ u64 csum_error;
+ u64 buffers_posted;
};
#define QCQ_F_INITED BIT(0)
#define QCQ_F_SG BIT(1)
#define QCQ_F_INTR BIT(2)
+#define QCQ_F_TX_STATS BIT(3)
+#define QCQ_F_RX_STATS BIT(4)
#define QCQ_F_NOTIFYQ BIT(5)
struct napi_stats {
@@ -56,7 +74,14 @@ struct qcq {
struct dentry *dentry;
};
+struct qcqst {
+ struct qcq *qcq;
+ struct q_stats *stats;
+};
+
#define q_to_qcq(q) container_of(q, struct qcq, q)
+#define q_to_tx_stats(q) (&q_to_qcq(q)->stats->tx)
+#define q_to_rx_stats(q) (&q_to_qcq(q)->stats->rx)
#define napi_to_qcq(napi) container_of(napi, struct qcq, napi)
#define napi_to_cq(napi) (&napi_to_qcq(napi)->cq)
@@ -108,11 +133,14 @@ struct lif {
spinlock_t adminq_lock; /* lock for AdminQ operations */
struct qcq *adminqcq;
struct qcq *notifyqcq;
+ struct qcqst *txqcqs;
+ struct qcqst *rxqcqs;
u64 last_eid;
unsigned int neqs;
unsigned int nxqs;
unsigned int ntxq_descs;
unsigned int nrxq_descs;
+ u32 rx_copybreak;
unsigned int rx_mode;
u64 hw_features;
bool mc_overflow;
@@ -134,6 +162,11 @@ struct lif {
u32 flags;
};
+#define lif_to_txqcq(lif, i) ((lif)->txqcqs[i].qcq)
+#define lif_to_rxqcq(lif, i) ((lif)->rxqcqs[i].qcq)
+#define lif_to_txq(lif, i) (&lif_to_txqcq((lif), i)->q)
+#define lif_to_rxq(lif, i) (&lif_to_txqcq((lif), i)->q)
+
static inline bool ionic_is_pf(struct ionic *ionic)
{
return ionic->pdev &&
@@ -173,6 +206,22 @@ int ionic_open(struct net_device *netdev);
int ionic_stop(struct net_device *netdev);
int ionic_reset_queues(struct lif *lif);
+static inline void debug_stats_txq_post(struct qcq *qcq,
+ struct txq_desc *desc, bool dbell)
+{
+ u8 num_sg_elems = ((le64_to_cpu(desc->cmd) >> IONIC_TXQ_DESC_NSGE_SHIFT)
+ & IONIC_TXQ_DESC_NSGE_MASK);
+ u8 sg_cntr_idx;
+
+ qcq->q.dbell_count += dbell;
+
+ sg_cntr_idx = GET_SG_CNTR_IDX(num_sg_elems);
+ if (sg_cntr_idx > (MAX_NUM_SG_CNTR - 1))
+ sg_cntr_idx = MAX_NUM_SG_CNTR - 1;
+
+ qcq->stats->tx.sg_cntr[sg_cntr_idx]++;
+}
+
static inline void debug_stats_napi_poll(struct qcq *qcq,
unsigned int work_done)
{
@@ -188,7 +237,10 @@ static inline void debug_stats_napi_poll(struct qcq *qcq,
}
#define DEBUG_STATS_CQE_CNT(cq) ((cq)->compl_count++)
+#define DEBUG_STATS_RX_BUFF_CNT(qcq) ((qcq)->stats->rx.buffers_posted++)
#define DEBUG_STATS_INTR_REARM(intr) ((intr)->rearm_count++)
+#define DEBUG_STATS_TXQ_POST(qcq, txdesc, dbell) \
+ debug_stats_txq_post(qcq, txdesc, dbell)
#define DEBUG_STATS_NAPI_POLL(qcq, work_done) \
debug_stats_napi_poll(qcq, work_done)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
new file mode 100644
index 000000000000..f95e67d3d634
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
@@ -0,0 +1,880 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/if_vlan.h>
+#include <net/ip6_checksum.h>
+
+#include "ionic.h"
+#include "ionic_lif.h"
+#include "ionic_txrx.h"
+
+static void ionic_tx_clean(struct queue *q, struct desc_info *desc_info,
+ struct cq_info *cq_info, void *cb_arg);
+static void ionic_rx_clean(struct queue *q, struct desc_info *desc_info,
+ struct cq_info *cq_info, void *cb_arg);
+
+static inline void ionic_txq_post(struct queue *q, bool ring_dbell,
+ desc_cb cb_func, void *cb_arg)
+{
+ DEBUG_STATS_TXQ_POST(q_to_qcq(q), q->head->desc, ring_dbell);
+
+ ionic_q_post(q, ring_dbell, cb_func, cb_arg);
+}
+
+static inline void ionic_rxq_post(struct queue *q, bool ring_dbell,
+ desc_cb cb_func, void *cb_arg)
+{
+ ionic_q_post(q, ring_dbell, cb_func, cb_arg);
+
+ DEBUG_STATS_RX_BUFF_CNT(q_to_qcq(q));
+}
+
+static void ionic_rx_recycle(struct queue *q, struct desc_info *desc_info,
+ struct sk_buff *skb)
+{
+ struct rxq_desc *old = desc_info->desc;
+ struct rxq_desc *new = q->head->desc;
+
+ new->addr = old->addr;
+ new->len = old->len;
+
+ ionic_rxq_post(q, true, ionic_rx_clean, skb);
+}
+
+static bool ionic_rx_copybreak(struct queue *q, struct desc_info *desc_info,
+ struct cq_info *cq_info, struct sk_buff **skb)
+{
+ struct net_device *netdev = q->lif->netdev;
+ struct device *dev = q->lif->ionic->dev;
+ struct rxq_desc *desc = desc_info->desc;
+ struct rxq_comp *comp = cq_info->cq_desc;
+ struct sk_buff *new_skb;
+ u16 clen, dlen;
+
+ clen = le16_to_cpu(comp->len);
+ dlen = le16_to_cpu(desc->len);
+ if (clen > q->lif->rx_copybreak) {
+ dma_unmap_single(dev, (dma_addr_t)le64_to_cpu(desc->addr),
+ dlen, DMA_FROM_DEVICE);
+ return false;
+ }
+
+ new_skb = netdev_alloc_skb_ip_align(netdev, clen);
+ if (!new_skb) {
+ dma_unmap_single(dev, (dma_addr_t)le64_to_cpu(desc->addr),
+ dlen, DMA_FROM_DEVICE);
+ return false;
+ }
+
+ dma_sync_single_for_cpu(dev, (dma_addr_t)le64_to_cpu(desc->addr),
+ clen, DMA_FROM_DEVICE);
+
+ memcpy(new_skb->data, (*skb)->data, clen);
+
+ ionic_rx_recycle(q, desc_info, *skb);
+ *skb = new_skb;
+
+ return true;
+}
+
+static void ionic_rx_clean(struct queue *q, struct desc_info *desc_info,
+ struct cq_info *cq_info, void *cb_arg)
+{
+ struct rxq_comp *comp = cq_info->cq_desc;
+ struct sk_buff *skb = cb_arg;
+ struct qcq *qcq = q_to_qcq(q);
+ struct net_device *netdev;
+ struct rx_stats *stats;
+
+ stats = q_to_rx_stats(q);
+ netdev = q->lif->netdev;
+
+ if (comp->status) {
+ // TODO record errors
+ ionic_rx_recycle(q, desc_info, skb);
+ return;
+ }
+
+ if (unlikely(test_bit(LIF_QUEUE_RESET, q->lif->state))) {
+ /* no packet processing while resetting */
+ ionic_rx_recycle(q, desc_info, skb);
+ return;
+ }
+
+ stats->pkts++;
+ stats->bytes += le16_to_cpu(comp->len);
+
+ ionic_rx_copybreak(q, desc_info, cq_info, &skb);
+
+ //prefetch(skb->data - NET_IP_ALIGN);
+
+ skb_put(skb, le16_to_cpu(comp->len));
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ skb_record_rx_queue(skb, q->index);
+
+ if (netdev->features & NETIF_F_RXHASH) {
+ switch (comp->pkt_type_color & IONIC_RXQ_COMP_PKT_TYPE_MASK) {
+ case PKT_TYPE_IPV4:
+ case PKT_TYPE_IPV6:
+ skb_set_hash(skb, le32_to_cpu(comp->rss_hash),
+ PKT_HASH_TYPE_L3);
+ break;
+ case PKT_TYPE_IPV4_TCP:
+ case PKT_TYPE_IPV6_TCP:
+ case PKT_TYPE_IPV4_UDP:
+ case PKT_TYPE_IPV6_UDP:
+ skb_set_hash(skb, le32_to_cpu(comp->rss_hash),
+ PKT_HASH_TYPE_L4);
+ break;
+ }
+ }
+
+ if (netdev->features & NETIF_F_RXCSUM) {
+ if (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_CALC) {
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ skb->csum = (__wsum)le16_to_cpu(comp->csum);
+ stats->csum_complete++;
+ }
+ } else {
+ stats->csum_none++;
+ }
+
+ if ((comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_TCP_BAD) ||
+ (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_UDP_BAD) ||
+ (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_IP_BAD))
+ stats->csum_error++;
+
+ if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
+ if (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_VLAN)
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ le16_to_cpu(comp->vlan_tci));
+ }
+
+ napi_gro_receive(&qcq->napi, skb);
+}
+
+static bool ionic_rx_service(struct cq *cq, struct cq_info *cq_info)
+{
+ struct rxq_comp *comp = cq_info->cq_desc;
+ struct queue *q = cq->bound_q;
+ struct desc_info *desc_info;
+
+ if (!color_match(comp->pkt_type_color, cq->done_color))
+ return false;
+
+ /* check for empty queue */
+ if (q->tail->index == q->head->index)
+ return false;
+
+ desc_info = q->tail;
+ if (desc_info->index != le16_to_cpu(comp->comp_index))
+ return false;
+
+ q->tail = desc_info->next;
+
+ /* clean the related q entry, only one per qc completion */
+ ionic_rx_clean(q, desc_info, cq_info, desc_info->cb_arg);
+
+ desc_info->cb = NULL;
+ desc_info->cb_arg = NULL;
+
+ return true;
+}
+
+static u32 ionic_rx_walk_cq(struct cq *rxcq, u32 limit)
+{
+ u32 work_done = 0;
+
+ while (ionic_rx_service(rxcq, rxcq->tail)) {
+ if (rxcq->tail->last)
+ rxcq->done_color = !rxcq->done_color;
+ rxcq->tail = rxcq->tail->next;
+ DEBUG_STATS_CQE_CNT(rxcq);
+
+ if (++work_done >= limit)
+ break;
+ }
+
+ return work_done;
+}
+
+void ionic_rx_flush(struct cq *cq)
+{
+ struct ionic_dev *idev = &cq->lif->ionic->idev;
+ u32 work_done;
+
+ work_done = ionic_rx_walk_cq(cq, cq->num_descs);
+
+ if (work_done)
+ ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index,
+ work_done, IONIC_INTR_CRED_RESET_COALESCE);
+}
+
+void ionic_tx_flush(struct cq *cq)
+{
+ struct ionic_dev *idev = &cq->lif->ionic->idev;
+ struct txq_comp *comp = cq->tail->cq_desc;
+ struct queue *q = cq->bound_q;
+ struct desc_info *desc_info;
+ unsigned int work_done = 0;
+
+ /* walk the completed cq entries */
+ while (work_done < cq->num_descs &&
+ color_match(comp->color, cq->done_color)) {
+
+ /* clean the related q entries, there could be
+ * several q entries completed for each cq completion
+ */
+ do {
+ desc_info = q->tail;
+ q->tail = desc_info->next;
+ ionic_tx_clean(q, desc_info, cq->tail,
+ desc_info->cb_arg);
+ desc_info->cb = NULL;
+ desc_info->cb_arg = NULL;
+ } while (desc_info->index != le16_to_cpu(comp->comp_index));
+
+ if (cq->tail->last)
+ cq->done_color = !cq->done_color;
+
+ cq->tail = cq->tail->next;
+ comp = cq->tail->cq_desc;
+ DEBUG_STATS_CQE_CNT(cq);
+
+ work_done++;
+ }
+
+ if (work_done)
+ ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index,
+ work_done, 0);
+}
+
+static struct sk_buff *ionic_rx_skb_alloc(struct queue *q, unsigned int len,
+ dma_addr_t *dma_addr)
+{
+ struct lif *lif = q->lif;
+ struct net_device *netdev = lif->netdev;
+ struct device *dev = lif->ionic->dev;
+ struct rx_stats *stats;
+ struct sk_buff *skb;
+
+ stats = q_to_rx_stats(q);
+ skb = netdev_alloc_skb_ip_align(netdev, len);
+ if (!skb) {
+ net_warn_ratelimited("%s: SKB alloc failed on %s!\n",
+ netdev->name, q->name);
+ stats->alloc_err++;
+ return NULL;
+ }
+
+ *dma_addr = dma_map_single(dev, skb->data, len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, *dma_addr)) {
+ dev_kfree_skb(skb);
+ net_warn_ratelimited("%s: DMA single map failed on %s!\n",
+ netdev->name, q->name);
+ stats->dma_map_err++;
+ return NULL;
+ }
+
+ return skb;
+}
+
+static void ionic_rx_skb_free(struct queue *q, struct sk_buff *skb,
+ unsigned int len, dma_addr_t dma_addr)
+{
+ struct device *dev = q->lif->ionic->dev;
+
+ dma_unmap_single(dev, dma_addr, len, DMA_FROM_DEVICE);
+ dev_kfree_skb(skb);
+}
+
+#define RX_RING_DOORBELL_STRIDE ((1 << 2) - 1)
+
+void ionic_rx_fill(struct queue *q)
+{
+ struct net_device *netdev = q->lif->netdev;
+ struct rxq_desc *desc;
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+ bool ring_doorbell;
+ unsigned int len;
+ unsigned int i;
+
+ len = netdev->mtu + ETH_HLEN;
+
+ for (i = ionic_q_space_avail(q); i; i--) {
+ skb = ionic_rx_skb_alloc(q, len, &dma_addr);
+ if (!skb)
+ return;
+
+ desc = q->head->desc;
+ desc->addr = cpu_to_le64(dma_addr);
+ desc->len = cpu_to_le16(len);
+ desc->opcode = RXQ_DESC_OPCODE_SIMPLE;
+
+ ring_doorbell = ((q->head->index + 1) &
+ RX_RING_DOORBELL_STRIDE) == 0;
+
+ ionic_rxq_post(q, ring_doorbell, ionic_rx_clean, skb);
+ }
+}
+
+static void ionic_rx_fill_cb(void *arg)
+{
+ ionic_rx_fill(arg);
+}
+
+void ionic_rx_empty(struct queue *q)
+{
+ struct desc_info *cur = q->tail;
+ struct rxq_desc *desc;
+
+ while (cur != q->head) {
+ desc = cur->desc;
+
+ ionic_rx_skb_free(q, cur->cb_arg, le16_to_cpu(desc->len),
+ le64_to_cpu(desc->addr));
+ cur->cb_arg = NULL;
+
+ cur = cur->next;
+ }
+}
+
+int ionic_rx_napi(struct napi_struct *napi, int budget)
+{
+ struct qcq *qcq = napi_to_qcq(napi);
+ struct cq *rxcq = napi_to_cq(napi);
+ unsigned int qi = rxcq->bound_q->index;
+ struct lif *lif = rxcq->bound_q->lif;
+ struct ionic_dev *idev = &lif->ionic->idev;
+ struct cq *txcq = &lif->txqcqs[qi].qcq->cq;
+ u32 work_done = 0;
+ u32 flags = 0;
+
+ ionic_tx_flush(txcq);
+
+ work_done = ionic_rx_walk_cq(rxcq, budget);
+
+ if (work_done)
+ ionic_rx_fill_cb(rxcq->bound_q);
+
+ if (work_done < budget && napi_complete_done(napi, work_done)) {
+ flags |= IONIC_INTR_CRED_UNMASK;
+ DEBUG_STATS_INTR_REARM(rxcq->bound_intr);
+ }
+
+ if (work_done || flags) {
+ flags |= IONIC_INTR_CRED_RESET_COALESCE;
+ ionic_intr_credits(idev->intr_ctrl, rxcq->bound_intr->index,
+ work_done, flags);
+ }
+
+ DEBUG_STATS_NAPI_POLL(qcq, work_done);
+
+ return work_done;
+}
+
+static dma_addr_t ionic_tx_map_single(struct queue *q, void *data, size_t len)
+{
+ struct tx_stats *stats = q_to_tx_stats(q);
+ struct device *dev = q->lif->ionic->dev;
+ dma_addr_t dma_addr;
+
+ dma_addr = dma_map_single(dev, data, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, dma_addr)) {
+ net_warn_ratelimited("%s: DMA single map failed on %s!\n",
+ q->lif->netdev->name, q->name);
+ stats->dma_map_err++;
+ return 0;
+ }
+ return dma_addr;
+}
+
+static dma_addr_t ionic_tx_map_frag(struct queue *q, const skb_frag_t *frag,
+ size_t offset, size_t len)
+{
+ struct tx_stats *stats = q_to_tx_stats(q);
+ struct device *dev = q->lif->ionic->dev;
+ dma_addr_t dma_addr;
+
+ dma_addr = skb_frag_dma_map(dev, frag, offset, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, dma_addr)) {
+ net_warn_ratelimited("%s: DMA frag map failed on %s!\n",
+ q->lif->netdev->name, q->name);
+ stats->dma_map_err++;
+ return 0;
+ }
+ return dma_addr;
+}
+
+static void ionic_tx_clean(struct queue *q, struct desc_info *desc_info,
+ struct cq_info *cq_info, void *cb_arg)
+{
+ struct txq_sg_desc *sg_desc = desc_info->sg_desc;
+ struct txq_sg_elem *elem = sg_desc->elems;
+ struct tx_stats *stats = q_to_tx_stats(q);
+ struct txq_desc *desc = desc_info->desc;
+ struct device *dev = q->lif->ionic->dev;
+ struct sk_buff *skb = cb_arg;
+ u8 opcode, flags, nsge;
+ u16 queue_index;
+ unsigned int i;
+ u64 addr;
+
+ decode_txq_desc_cmd(le64_to_cpu(desc->cmd),
+ &opcode, &flags, &nsge, &addr);
+
+ dma_unmap_page(dev, (dma_addr_t)addr,
+ le16_to_cpu(desc->len), DMA_TO_DEVICE);
+ for (i = 0; i < nsge; i++, elem++)
+ dma_unmap_page(dev, (dma_addr_t)le64_to_cpu(elem->addr),
+ le16_to_cpu(elem->len), DMA_TO_DEVICE);
+
+ if (skb) {
+ queue_index = skb_get_queue_mapping(skb);
+ if (unlikely(__netif_subqueue_stopped(q->lif->netdev,
+ queue_index))) {
+ netif_wake_subqueue(q->lif->netdev, queue_index);
+ q->wake++;
+ }
+ dev_kfree_skb_any(skb);
+ stats->clean++;
+ }
+}
+
+static void ionic_tx_tcp_inner_pseudo_csum(struct sk_buff *skb)
+{
+ skb_cow_head(skb, 0);
+
+ if (skb->protocol == cpu_to_be16(ETH_P_IP)) {
+ inner_ip_hdr(skb)->check = 0;
+ inner_tcp_hdr(skb)->check =
+ ~csum_tcpudp_magic(inner_ip_hdr(skb)->saddr,
+ inner_ip_hdr(skb)->daddr,
+ 0, IPPROTO_TCP, 0);
+ } else if (skb->protocol == cpu_to_be16(ETH_P_IPV6)) {
+ inner_tcp_hdr(skb)->check =
+ ~csum_ipv6_magic(&inner_ipv6_hdr(skb)->saddr,
+ &inner_ipv6_hdr(skb)->daddr,
+ 0, IPPROTO_TCP, 0);
+ }
+}
+
+static void ionic_tx_tcp_pseudo_csum(struct sk_buff *skb)
+{
+ skb_cow_head(skb, 0);
+
+ if (skb->protocol == cpu_to_be16(ETH_P_IP)) {
+ ip_hdr(skb)->check = 0;
+ tcp_hdr(skb)->check =
+ ~csum_tcpudp_magic(ip_hdr(skb)->saddr,
+ ip_hdr(skb)->daddr,
+ 0, IPPROTO_TCP, 0);
+ } else if (skb->protocol == cpu_to_be16(ETH_P_IPV6)) {
+ tcp_hdr(skb)->check =
+ ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+ 0, IPPROTO_TCP, 0);
+ }
+}
+
+static void ionic_tx_tso_post(struct queue *q, struct txq_desc *desc,
+ struct sk_buff *skb,
+ dma_addr_t addr, u8 nsge, u16 len,
+ unsigned int hdrlen, unsigned int mss,
+ bool outer_csum,
+ u16 vlan_tci, bool has_vlan,
+ bool start, bool done)
+{
+ u8 flags = 0;
+ u64 cmd;
+
+ flags |= has_vlan ? IONIC_TXQ_DESC_FLAG_VLAN : 0;
+ flags |= outer_csum ? IONIC_TXQ_DESC_FLAG_ENCAP : 0;
+ flags |= start ? IONIC_TXQ_DESC_FLAG_TSO_SOT : 0;
+ flags |= done ? IONIC_TXQ_DESC_FLAG_TSO_EOT : 0;
+
+ cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_TSO, flags, nsge, addr);
+ desc->cmd = cpu_to_le64(cmd);
+ desc->len = cpu_to_le16(len);
+ desc->vlan_tci = cpu_to_le16(vlan_tci);
+ desc->hdr_len = cpu_to_le16(hdrlen);
+ desc->mss = cpu_to_le16(mss);
+
+ if (done) {
+ skb_tx_timestamp(skb);
+ ionic_txq_post(q, !netdev_xmit_more(), ionic_tx_clean, skb);
+ } else {
+ ionic_txq_post(q, false, ionic_tx_clean, NULL);
+ }
+}
+
+static struct txq_desc *ionic_tx_tso_next(struct queue *q,
+ struct txq_sg_elem **elem)
+{
+ struct txq_sg_desc *sg_desc = q->head->sg_desc;
+ struct txq_desc *desc = q->head->desc;
+
+ *elem = sg_desc->elems;
+ return desc;
+}
+
+static int ionic_tx_tso(struct queue *q, struct sk_buff *skb)
+{
+ struct tx_stats *stats = q_to_tx_stats(q);
+ struct desc_info *abort = q->head;
+ struct desc_info *rewind = abort;
+ unsigned int frag_left = 0;
+ struct txq_sg_elem *elem;
+ unsigned int offset = 0;
+ unsigned int len_left;
+ struct txq_desc *desc;
+ dma_addr_t desc_addr;
+ unsigned int hdrlen;
+ unsigned int nfrags;
+ unsigned int seglen;
+ u64 total_bytes = 0;
+ u64 total_pkts = 0;
+ unsigned int left;
+ unsigned int len;
+ unsigned int mss;
+ skb_frag_t *frag;
+ bool start, done;
+ bool outer_csum;
+ bool has_vlan;
+ u16 desc_len;
+ u8 desc_nsge;
+ u16 vlan_tci;
+ bool encap;
+
+ mss = skb_shinfo(skb)->gso_size;
+ nfrags = skb_shinfo(skb)->nr_frags;
+ len_left = skb->len - skb_headlen(skb);
+ outer_csum = (skb_shinfo(skb)->gso_type & SKB_GSO_GRE_CSUM) ||
+ (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM);
+ has_vlan = !!skb_vlan_tag_present(skb);
+ vlan_tci = skb_vlan_tag_get(skb);
+ encap = skb->encapsulation;
+
+ /* Preload inner-most TCP csum field with IP pseudo hdr
+ * calculated with IP length set to zero. HW will later
+ * add in length to each TCP segment resulting from the TSO.
+ */
+
+ if (encap)
+ ionic_tx_tcp_inner_pseudo_csum(skb);
+ else
+ ionic_tx_tcp_pseudo_csum(skb);
+
+ if (encap)
+ hdrlen = skb_inner_transport_header(skb) - skb->data +
+ inner_tcp_hdrlen(skb);
+ else
+ hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb);
+
+ seglen = hdrlen + mss;
+ left = skb_headlen(skb);
+
+ desc = ionic_tx_tso_next(q, &elem);
+ start = true;
+
+ /* Chop skb->data up into desc segments */
+
+ while (left > 0) {
+ len = min(seglen, left);
+ frag_left = seglen - len;
+ desc_addr = ionic_tx_map_single(q, skb->data + offset, len);
+ if (!desc_addr)
+ goto err_out_abort;
+ desc_len = len;
+ desc_nsge = 0;
+ left -= len;
+ offset += len;
+ if (nfrags > 0 && frag_left > 0)
+ continue;
+ done = (nfrags == 0 && left == 0);
+ ionic_tx_tso_post(q, desc, skb,
+ desc_addr, desc_nsge, desc_len,
+ hdrlen, mss,
+ outer_csum,
+ vlan_tci, has_vlan,
+ start, done);
+ total_pkts++;
+ total_bytes += start ? len : len + hdrlen;
+ desc = ionic_tx_tso_next(q, &elem);
+ start = false;
+ seglen = mss;
+ }
+
+ /* Chop skb frags into desc segments */
+
+ for (frag = skb_shinfo(skb)->frags; len_left; frag++) {
+ offset = 0;
+ left = skb_frag_size(frag);
+ len_left -= left;
+ nfrags--;
+ stats->frags++;
+
+ while (left > 0) {
+ if (frag_left > 0) {
+ len = min(frag_left, left);
+ frag_left -= len;
+ elem->addr =
+ cpu_to_le64(ionic_tx_map_frag(q, frag,
+ offset, len));
+ if (!elem->addr)
+ goto err_out_abort;
+ elem->len = cpu_to_le16(len);
+ elem++;
+ desc_nsge++;
+ left -= len;
+ offset += len;
+ if (nfrags > 0 && frag_left > 0)
+ continue;
+ done = (nfrags == 0 && left == 0);
+ ionic_tx_tso_post(q, desc, skb, desc_addr,
+ desc_nsge, desc_len,
+ hdrlen, mss, outer_csum,
+ vlan_tci, has_vlan,
+ start, done);
+ total_pkts++;
+ total_bytes += start ? len : len + hdrlen;
+ desc = ionic_tx_tso_next(q, &elem);
+ start = false;
+ } else {
+ len = min(mss, left);
+ frag_left = mss - len;
+ desc_addr = ionic_tx_map_frag(q, frag,
+ offset, len);
+ if (!desc_addr)
+ goto err_out_abort;
+ desc_len = len;
+ desc_nsge = 0;
+ left -= len;
+ offset += len;
+ if (nfrags > 0 && frag_left > 0)
+ continue;
+ done = (nfrags == 0 && left == 0);
+ ionic_tx_tso_post(q, desc, skb, desc_addr,
+ desc_nsge, desc_len,
+ hdrlen, mss, outer_csum,
+ vlan_tci, has_vlan,
+ start, done);
+ total_pkts++;
+ total_bytes += start ? len : len + hdrlen;
+ desc = ionic_tx_tso_next(q, &elem);
+ start = false;
+ }
+ }
+ }
+
+ stats->pkts += total_pkts;
+ stats->bytes += total_bytes;
+ stats->tso++;
+
+ return 0;
+
+err_out_abort:
+ while (rewind->desc != q->head->desc) {
+ ionic_tx_clean(q, rewind, NULL, NULL);
+ rewind = rewind->next;
+ }
+ q->head = abort;
+
+ return -ENOMEM;
+}
+
+static int ionic_tx_calc_csum(struct queue *q, struct sk_buff *skb)
+{
+ struct tx_stats *stats = q_to_tx_stats(q);
+ struct txq_desc *desc = q->head->desc;
+ dma_addr_t addr;
+ bool has_vlan;
+ u8 flags = 0;
+ bool encap;
+ u64 cmd;
+
+ has_vlan = !!skb_vlan_tag_present(skb);
+ encap = skb->encapsulation;
+
+ addr = ionic_tx_map_single(q, skb->data, skb_headlen(skb));
+ if (!addr)
+ return -ENOMEM;
+
+ flags |= has_vlan ? IONIC_TXQ_DESC_FLAG_VLAN : 0;
+ flags |= encap ? IONIC_TXQ_DESC_FLAG_ENCAP : 0;
+
+ cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_CSUM_PARTIAL,
+ flags, skb_shinfo(skb)->nr_frags, addr);
+ desc->cmd = cpu_to_le64(cmd);
+ desc->len = cpu_to_le16(skb_headlen(skb));
+ desc->vlan_tci = cpu_to_le16(skb_vlan_tag_get(skb));
+ desc->csum_start = cpu_to_le16(skb_checksum_start_offset(skb));
+ desc->csum_offset = cpu_to_le16(skb->csum_offset);
+
+ if (skb->csum_not_inet)
+ stats->crc32_csum++;
+ else
+ stats->csum++;
+
+ return 0;
+}
+
+static int ionic_tx_calc_no_csum(struct queue *q, struct sk_buff *skb)
+{
+ struct tx_stats *stats = q_to_tx_stats(q);
+ struct txq_desc *desc = q->head->desc;
+ dma_addr_t addr;
+ bool has_vlan;
+ u8 flags = 0;
+ bool encap;
+ u64 cmd;
+
+ has_vlan = !!skb_vlan_tag_present(skb);
+ encap = skb->encapsulation;
+
+ addr = ionic_tx_map_single(q, skb->data, skb_headlen(skb));
+ if (!addr)
+ return -ENOMEM;
+
+ flags |= has_vlan ? IONIC_TXQ_DESC_FLAG_VLAN : 0;
+ flags |= encap ? IONIC_TXQ_DESC_FLAG_ENCAP : 0;
+
+ cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_CSUM_NONE,
+ flags, skb_shinfo(skb)->nr_frags, addr);
+ desc->cmd = cpu_to_le64(cmd);
+ desc->len = cpu_to_le16(skb_headlen(skb));
+ desc->vlan_tci = cpu_to_le16(skb_vlan_tag_get(skb));
+
+ stats->no_csum++;
+
+ return 0;
+}
+
+static int ionic_tx_skb_frags(struct queue *q, struct sk_buff *skb)
+{
+ unsigned int len_left = skb->len - skb_headlen(skb);
+ struct txq_sg_desc *sg_desc = q->head->sg_desc;
+ struct txq_sg_elem *elem = sg_desc->elems;
+ struct tx_stats *stats = q_to_tx_stats(q);
+ dma_addr_t dma_addr;
+ skb_frag_t *frag;
+ u16 len;
+
+ for (frag = skb_shinfo(skb)->frags; len_left; frag++, elem++) {
+ len = skb_frag_size(frag);
+ elem->len = cpu_to_le16(len);
+ dma_addr = ionic_tx_map_frag(q, frag, 0, len);
+ if (!dma_addr)
+ return -ENOMEM;
+ elem->addr = cpu_to_le64(dma_addr);
+ len_left -= len;
+ stats->frags++;
+ }
+
+ return 0;
+}
+
+static int ionic_tx(struct queue *q, struct sk_buff *skb)
+{
+ struct tx_stats *stats = q_to_tx_stats(q);
+ int err;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ err = ionic_tx_calc_csum(q, skb);
+ else
+ err = ionic_tx_calc_no_csum(q, skb);
+ if (err)
+ return err;
+
+ err = ionic_tx_skb_frags(q, skb);
+ if (err)
+ return err;
+
+ skb_tx_timestamp(skb);
+ stats->pkts++;
+ stats->bytes += skb->len;
+
+ ionic_txq_post(q, !netdev_xmit_more(), ionic_tx_clean, skb);
+
+ return 0;
+}
+
+static int ionic_tx_descs_needed(struct queue *q, struct sk_buff *skb)
+{
+ struct tx_stats *stats = q_to_tx_stats(q);
+ int err;
+
+ /* If TSO, need roundup(skb->len/mss) descs */
+ if (skb_is_gso(skb))
+ return (skb->len / skb_shinfo(skb)->gso_size) + 1;
+
+ /* If non-TSO, just need 1 desc and nr_frags sg elems */
+ if (skb_shinfo(skb)->nr_frags <= IONIC_TX_MAX_SG_ELEMS)
+ return 1;
+
+ /* Too many frags, so linearize */
+ err = skb_linearize(skb);
+ if (err)
+ return err;
+
+ stats->linearize++;
+
+ /* Need 1 desc and zero sg elems */
+ return 1;
+}
+
+netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ u16 queue_index = skb_get_queue_mapping(skb);
+ struct lif *lif = netdev_priv(netdev);
+ struct queue *q;
+ int ndescs;
+ int err;
+
+ if (unlikely(!test_bit(LIF_UP, lif->state))) {
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (likely(lif_to_txqcq(lif, queue_index)))
+ q = lif_to_txq(lif, queue_index);
+ else
+ q = lif_to_txq(lif, 0);
+
+ ndescs = ionic_tx_descs_needed(q, skb);
+ if (ndescs < 0)
+ goto err_out_drop;
+
+ if (!ionic_q_has_space(q, ndescs)) {
+ netif_stop_subqueue(netdev, queue_index);
+ q->stop++;
+
+ /* Might race with ionic_tx_clean, check again */
+ smp_rmb();
+ if (ionic_q_has_space(q, ndescs)) {
+ netif_wake_subqueue(netdev, queue_index);
+ q->wake++;
+ } else {
+ return NETDEV_TX_BUSY;
+ }
+ }
+
+ if (skb_is_gso(skb))
+ err = ionic_tx_tso(q, skb);
+ else
+ err = ionic_tx(q, skb);
+
+ if (err)
+ goto err_out_drop;
+
+ return NETDEV_TX_OK;
+
+err_out_drop:
+ q->drop++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.h b/drivers/net/ethernet/pensando/ionic/ionic_txrx.h
new file mode 100644
index 000000000000..2391a0eec65a
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#ifndef _IONIC_TXRX_H_
+#define _IONIC_TXRX_H_
+
+void ionic_rx_flush(struct cq *cq);
+void ionic_tx_flush(struct cq *cq);
+
+void ionic_rx_fill(struct queue *q);
+void ionic_rx_empty(struct queue *q);
+int ionic_rx_napi(struct napi_struct *napi, int budget);
+netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev);
+
+#endif /* _IONIC_TXRX_H_ */
--
2.17.1
^ permalink raw reply related
* [PATCH v2 net-next 19/19] ionic: Add basic devlink interface
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
Add a devlink interface for access to information that isn't
normally available through ethtool or the iplink interface.
Example:
$ ./devlink -j -p dev info pci/0000:b6:00.0
{
"info": {
"pci/0000:b6:00.0": {
"driver": "ionic",
"serial_number": "FLM18420073",
"versions": {
"fixed": {
"fw_version": "0.11.0-50",
"fw_status": "0x1",
"fw_heartbeat": "0x716ce",
"asic_type": "0x0",
"asic_rev": "0x0"
}
}
}
}
}
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/Makefile | 2 +-
drivers/net/ethernet/pensando/ionic/ionic.h | 1 +
.../ethernet/pensando/ionic/ionic_bus_pci.c | 7 ++
.../ethernet/pensando/ionic/ionic_devlink.c | 89 +++++++++++++++++++
.../ethernet/pensando/ionic/ionic_devlink.h | 12 +++
5 files changed, 110 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_devlink.c
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_devlink.h
diff --git a/drivers/net/ethernet/pensando/ionic/Makefile b/drivers/net/ethernet/pensando/ionic/Makefile
index 4f3cfbf36c23..ce187c7b33a8 100644
--- a/drivers/net/ethernet/pensando/ionic/Makefile
+++ b/drivers/net/ethernet/pensando/ionic/Makefile
@@ -5,4 +5,4 @@ obj-$(CONFIG_IONIC) := ionic.o
ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o ionic_ethtool.o \
ionic_lif.o ionic_rx_filter.o ionic_txrx.o ionic_debugfs.o \
- ionic_stats.o
+ ionic_stats.o ionic_devlink.o
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index cd08166f73a9..a0034bc5b4a1 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -44,6 +44,7 @@ struct ionic {
DECLARE_BITMAP(intrs, INTR_CTRL_REGS_MAX);
struct work_struct nb_work;
struct notifier_block nb;
+ struct devlink *dl;
};
struct ionic_admin_ctx {
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
index 98c12b770c7f..a8c99254489f 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
@@ -10,6 +10,7 @@
#include "ionic_bus.h"
#include "ionic_lif.h"
#include "ionic_debugfs.h"
+#include "ionic_devlink.h"
/* Supported devices */
static const struct pci_device_id ionic_id_table[] = {
@@ -212,9 +213,14 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_deinit_lifs;
}
+ err = ionic_devlink_register(ionic);
+ if (err)
+ dev_err(dev, "Cannot register devlink (ignored): %d\n", err);
+
return 0;
err_out_deinit_lifs:
+ ionic_devlink_unregister(ionic);
ionic_lifs_deinit(ionic);
err_out_free_lifs:
ionic_lifs_free(ionic);
@@ -247,6 +253,7 @@ static void ionic_remove(struct pci_dev *pdev)
struct ionic *ionic = pci_get_drvdata(pdev);
if (ionic) {
+ ionic_devlink_unregister(ionic);
ionic_lifs_unregister(ionic);
ionic_lifs_deinit(ionic);
ionic_lifs_free(ionic);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
new file mode 100644
index 000000000000..fbbfcdde292f
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include "ionic.h"
+#include "ionic_bus.h"
+#include "ionic_lif.h"
+#include "ionic_devlink.h"
+
+struct ionic_devlink {
+ struct ionic *ionic;
+};
+
+static int ionic_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
+ struct netlink_ext_ack *extack)
+{
+ struct ionic *ionic = *(struct ionic **)devlink_priv(dl);
+ struct ionic_dev *idev = &ionic->idev;
+ char buf[16];
+ u32 val;
+
+ devlink_info_driver_name_put(req, DRV_NAME);
+
+ devlink_info_version_fixed_put(req, "fw_version",
+ idev->dev_info.fw_version);
+
+ val = ioread8(&idev->dev_info_regs->fw_status);
+ snprintf(buf, sizeof(buf), "0x%x", val);
+ devlink_info_version_fixed_put(req, "fw_status", buf);
+
+ val = ioread32(&idev->dev_info_regs->fw_heartbeat);
+ snprintf(buf, sizeof(buf), "0x%x", val);
+ devlink_info_version_fixed_put(req, "fw_heartbeat", buf);
+
+ snprintf(buf, sizeof(buf), "0x%x", idev->dev_info.asic_type);
+ devlink_info_version_fixed_put(req, "asic_type", buf);
+
+ snprintf(buf, sizeof(buf), "0x%x", idev->dev_info.asic_rev);
+ devlink_info_version_fixed_put(req, "asic_rev", buf);
+
+ devlink_info_serial_number_put(req, idev->dev_info.serial_num);
+
+ return 0;
+}
+
+static const struct devlink_ops ionic_dl_ops = {
+ .info_get = ionic_dl_info_get,
+};
+
+int ionic_devlink_register(struct ionic *ionic)
+{
+ struct devlink *dl;
+ struct ionic **ip;
+ int err;
+
+ dl = devlink_alloc(&ionic_dl_ops, sizeof(struct ionic *));
+ if (!dl) {
+ dev_warn(ionic->dev, "devlink_alloc failed");
+ return -ENOMEM;
+ }
+
+ ip = (struct ionic **)devlink_priv(dl);
+ *ip = ionic;
+ ionic->dl = dl;
+
+ err = devlink_register(dl, ionic->dev);
+ if (err) {
+ dev_warn(ionic->dev, "devlink_register failed: %d\n", err);
+ goto err_dl_free;
+ }
+
+ return 0;
+
+err_dl_free:
+ ionic->dl = NULL;
+ devlink_free(dl);
+ return err;
+}
+
+void ionic_devlink_unregister(struct ionic *ionic)
+{
+ if (!ionic->dl)
+ return;
+
+ devlink_unregister(ionic->dl);
+ devlink_free(ionic->dl);
+}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.h b/drivers/net/ethernet/pensando/ionic/ionic_devlink.h
new file mode 100644
index 000000000000..35528884e29f
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#ifndef _IONIC_DEVLINK_H_
+#define _IONIC_DEVLINK_H_
+
+#include <net/devlink.h>
+
+int ionic_devlink_register(struct ionic *ionic);
+void ionic_devlink_unregister(struct ionic *ionic);
+
+#endif /* _IONIC_DEVLINK_H_ */
--
2.17.1
^ permalink raw reply related
* [PATCH v2 net-next 16/19] ionic: Add driver stats
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
Add in the detailed statistics for ethtool -S that the driver
keeps as it processes packets. Display of the additional
debug statistics can be enabled through the ethtool priv-flags
feature.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/Makefile | 3 +-
.../ethernet/pensando/ionic/ionic_ethtool.c | 109 ++++++
.../net/ethernet/pensando/ionic/ionic_lif.h | 14 +
.../net/ethernet/pensando/ionic/ionic_stats.c | 333 ++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_stats.h | 53 +++
5 files changed, 511 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_stats.c
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_stats.h
diff --git a/drivers/net/ethernet/pensando/ionic/Makefile b/drivers/net/ethernet/pensando/ionic/Makefile
index 0e2dc53f08d4..4f3cfbf36c23 100644
--- a/drivers/net/ethernet/pensando/ionic/Makefile
+++ b/drivers/net/ethernet/pensando/ionic/Makefile
@@ -4,4 +4,5 @@
obj-$(CONFIG_IONIC) := ionic.o
ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o ionic_ethtool.o \
- ionic_lif.o ionic_rx_filter.o ionic_txrx.o ionic_debugfs.o
+ ionic_lif.o ionic_rx_filter.o ionic_txrx.o ionic_debugfs.o \
+ ionic_stats.o
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
index 2bbe5819387b..518e79c90fca 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
@@ -8,6 +8,84 @@
#include "ionic_bus.h"
#include "ionic_lif.h"
#include "ionic_ethtool.h"
+#include "ionic_stats.h"
+
+static const char ionic_priv_flags_strings[][ETH_GSTRING_LEN] = {
+#define PRIV_F_SW_DBG_STATS BIT(0)
+ "sw-dbg-stats",
+};
+#define PRIV_FLAGS_COUNT ARRAY_SIZE(ionic_priv_flags_strings)
+
+static void ionic_get_stats_strings(struct lif *lif, u8 *buf)
+{
+ u32 i;
+
+ for (i = 0; i < ionic_num_stats_grps; i++)
+ ionic_stats_groups[i].get_strings(lif, &buf);
+}
+
+static void ionic_get_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, u64 *buf)
+{
+ struct lif *lif;
+ u32 i;
+
+ lif = netdev_priv(netdev);
+
+ memset(buf, 0, stats->n_stats * sizeof(*buf));
+ for (i = 0; i < ionic_num_stats_grps; i++)
+ ionic_stats_groups[i].get_values(lif, &buf);
+}
+
+static int ionic_get_stats_count(struct lif *lif)
+{
+ int i, num_stats = 0;
+
+ for (i = 0; i < ionic_num_stats_grps; i++)
+ num_stats += ionic_stats_groups[i].get_count(lif);
+
+ return num_stats;
+}
+
+static int ionic_get_sset_count(struct net_device *netdev, int sset)
+{
+ struct lif *lif = netdev_priv(netdev);
+ int count = 0;
+
+ switch (sset) {
+ case ETH_SS_STATS:
+ count = ionic_get_stats_count(lif);
+ break;
+ case ETH_SS_TEST:
+ break;
+ case ETH_SS_PRIV_FLAGS:
+ count = PRIV_FLAGS_COUNT;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return count;
+}
+
+static void ionic_get_strings(struct net_device *netdev,
+ u32 sset, u8 *buf)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ switch (sset) {
+ case ETH_SS_STATS:
+ ionic_get_stats_strings(lif, buf);
+ break;
+ case ETH_SS_PRIV_FLAGS:
+ memcpy(buf, ionic_priv_flags_strings,
+ PRIV_FLAGS_COUNT * ETH_GSTRING_LEN);
+ break;
+ case ETH_SS_TEST:
+ // IONIC_TODO
+ default:
+ netdev_err(netdev, "Invalid sset %d\n", sset);
+ }
+}
static void ionic_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *drvinfo)
@@ -406,6 +484,32 @@ static int ionic_set_channels(struct net_device *netdev,
return 0;
}
+static u32 ionic_get_priv_flags(struct net_device *netdev)
+{
+ struct lif *lif = netdev_priv(netdev);
+ u32 priv_flags = 0;
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state))
+ priv_flags |= PRIV_F_SW_DBG_STATS;
+
+ return priv_flags;
+}
+
+static int ionic_set_priv_flags(struct net_device *netdev, u32 priv_flags)
+{
+ struct lif *lif = netdev_priv(netdev);
+ u32 flags = lif->flags;
+
+ clear_bit(LIF_SW_DEBUG_STATS, lif->state);
+ if (priv_flags & PRIV_F_SW_DBG_STATS)
+ set_bit(LIF_SW_DEBUG_STATS, lif->state);
+
+ if (flags != lif->flags)
+ lif->flags = flags;
+
+ return 0;
+}
+
static int ionic_get_module_info(struct net_device *netdev,
struct ethtool_modinfo *modinfo)
@@ -495,6 +599,11 @@ static const struct ethtool_ops ionic_ethtool_ops = {
.set_ringparam = ionic_set_ringparam,
.get_channels = ionic_get_channels,
.set_channels = ionic_set_channels,
+ .get_strings = ionic_get_strings,
+ .get_ethtool_stats = ionic_get_stats,
+ .get_sset_count = ionic_get_sset_count,
+ .get_priv_flags = ionic_get_priv_flags,
+ .set_priv_flags = ionic_set_priv_flags,
.get_module_info = ionic_get_module_info,
.get_module_eeprom = ionic_get_module_eeprom,
.get_pauseparam = ionic_get_pauseparam,
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 88d5bf8f58a1..f93bfa2b4393 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -108,8 +108,22 @@ struct ionic_deferred {
struct work_struct work;
};
+struct lif_sw_stats {
+ u64 tx_packets;
+ u64 tx_bytes;
+ u64 rx_packets;
+ u64 rx_bytes;
+ u64 tx_tso;
+ u64 tx_no_csum;
+ u64 tx_csum;
+ u64 rx_csum_none;
+ u64 rx_csum_complete;
+ u64 rx_csum_error;
+};
+
enum lif_state_flags {
LIF_INITED,
+ LIF_SW_DEBUG_STATS,
LIF_UP,
LIF_LINK_CHECK_NEEDED,
LIF_QUEUE_RESET,
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.c b/drivers/net/ethernet/pensando/ionic/ionic_stats.c
new file mode 100644
index 000000000000..978a9f89804a
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/netdevice.h>
+
+#include "ionic.h"
+#include "ionic_lif.h"
+#include "ionic_stats.h"
+
+static const struct ionic_stat_desc ionic_lif_stats_desc[] = {
+ IONIC_LIF_STAT_DESC(tx_packets),
+ IONIC_LIF_STAT_DESC(tx_bytes),
+ IONIC_LIF_STAT_DESC(rx_packets),
+ IONIC_LIF_STAT_DESC(rx_bytes),
+ IONIC_LIF_STAT_DESC(tx_tso),
+ IONIC_LIF_STAT_DESC(tx_no_csum),
+ IONIC_LIF_STAT_DESC(tx_csum),
+ IONIC_LIF_STAT_DESC(rx_csum_none),
+ IONIC_LIF_STAT_DESC(rx_csum_complete),
+ IONIC_LIF_STAT_DESC(rx_csum_error),
+};
+
+static const struct ionic_stat_desc ionic_tx_stats_desc[] = {
+ IONIC_TX_STAT_DESC(pkts),
+ IONIC_TX_STAT_DESC(bytes),
+ IONIC_TX_STAT_DESC(clean),
+ IONIC_TX_STAT_DESC(dma_map_err),
+ IONIC_TX_STAT_DESC(linearize),
+ IONIC_TX_STAT_DESC(frags),
+};
+
+static const struct ionic_stat_desc ionic_rx_stats_desc[] = {
+ IONIC_RX_STAT_DESC(pkts),
+ IONIC_RX_STAT_DESC(bytes),
+ IONIC_RX_STAT_DESC(dma_map_err),
+ IONIC_RX_STAT_DESC(alloc_err),
+ IONIC_RX_STAT_DESC(csum_none),
+ IONIC_RX_STAT_DESC(csum_complete),
+ IONIC_RX_STAT_DESC(csum_error),
+};
+
+static const struct ionic_stat_desc ionic_txq_stats_desc[] = {
+ IONIC_TX_Q_STAT_DESC(stop),
+ IONIC_TX_Q_STAT_DESC(wake),
+ IONIC_TX_Q_STAT_DESC(drop),
+ IONIC_TX_Q_STAT_DESC(dbell_count),
+};
+
+static const struct ionic_stat_desc ionic_dbg_cq_stats_desc[] = {
+ IONIC_CQ_STAT_DESC(compl_count),
+};
+
+static const struct ionic_stat_desc ionic_dbg_intr_stats_desc[] = {
+ IONIC_INTR_STAT_DESC(rearm_count),
+};
+
+static const struct ionic_stat_desc ionic_dbg_napi_stats_desc[] = {
+ IONIC_NAPI_STAT_DESC(poll_count),
+};
+
+#define IONIC_NUM_LIF_STATS ARRAY_SIZE(ionic_lif_stats_desc)
+#define IONIC_NUM_TX_STATS ARRAY_SIZE(ionic_tx_stats_desc)
+#define IONIC_NUM_RX_STATS ARRAY_SIZE(ionic_rx_stats_desc)
+#define IONIC_NUM_TX_Q_STATS ARRAY_SIZE(ionic_txq_stats_desc)
+#define IONIC_NUM_DBG_CQ_STATS ARRAY_SIZE(ionic_dbg_cq_stats_desc)
+#define IONIC_NUM_DBG_INTR_STATS ARRAY_SIZE(ionic_dbg_intr_stats_desc)
+#define IONIC_NUM_DBG_NAPI_STATS ARRAY_SIZE(ionic_dbg_napi_stats_desc)
+
+#define MAX_Q(lif) ((lif)->netdev->real_num_tx_queues)
+
+static void ionic_get_lif_stats(struct lif *lif, struct lif_sw_stats *stats)
+{
+ struct tx_stats *tstats;
+ struct rx_stats *rstats;
+ struct qcq *txqcq;
+ struct qcq *rxqcq;
+ int q_num;
+
+ memset(stats, 0, sizeof(*stats));
+
+ for (q_num = 0; q_num < MAX_Q(lif); q_num++) {
+ txqcq = lif_to_txqcq(lif, q_num);
+ if (txqcq && txqcq->stats) {
+ tstats = &txqcq->stats->tx;
+ stats->tx_packets += tstats->pkts;
+ stats->tx_bytes += tstats->bytes;
+ stats->tx_tso += tstats->tso;
+ stats->tx_no_csum += tstats->no_csum;
+ stats->tx_csum += tstats->csum;
+ }
+
+ rxqcq = lif_to_rxqcq(lif, q_num);
+ if (rxqcq && rxqcq->stats) {
+ rstats = &rxqcq->stats->rx;
+ stats->rx_packets += rstats->pkts;
+ stats->rx_bytes += rstats->bytes;
+ stats->rx_csum_none += rstats->csum_none;
+ stats->rx_csum_complete += rstats->csum_complete;
+ stats->rx_csum_error += rstats->csum_error;
+ }
+ }
+}
+
+static u64 ionic_sw_stats_get_count(struct lif *lif)
+{
+ u64 total = 0;
+
+ /* lif stats */
+ total += IONIC_NUM_LIF_STATS;
+
+ /* tx stats */
+ total += MAX_Q(lif) * IONIC_NUM_TX_STATS;
+
+ /* rx stats */
+ total += MAX_Q(lif) * IONIC_NUM_RX_STATS;
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state)) {
+ /* tx debug stats */
+ total += MAX_Q(lif) * (IONIC_NUM_DBG_CQ_STATS +
+ IONIC_NUM_TX_Q_STATS +
+ IONIC_NUM_DBG_INTR_STATS +
+ IONIC_NUM_DBG_NAPI_STATS +
+ MAX_NUM_NAPI_CNTR +
+ MAX_NUM_SG_CNTR);
+
+ /* rx debug stats */
+ total += MAX_Q(lif) * (IONIC_NUM_DBG_CQ_STATS +
+ IONIC_NUM_DBG_INTR_STATS +
+ IONIC_NUM_DBG_NAPI_STATS +
+ MAX_NUM_NAPI_CNTR);
+ }
+
+ return total;
+}
+
+static void ionic_sw_stats_get_strings(struct lif *lif, u8 **buf)
+{
+ int i, q_num;
+
+ for (i = 0; i < IONIC_NUM_LIF_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN, ionic_lif_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (q_num = 0; q_num < MAX_Q(lif); q_num++) {
+ for (i = 0; i < IONIC_NUM_TX_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN, "tx_%d_%s",
+ q_num, ionic_tx_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state)) {
+ for (i = 0; i < IONIC_NUM_TX_Q_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_%s",
+ q_num,
+ ionic_txq_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_CQ_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_cq_%s",
+ q_num,
+ ionic_dbg_cq_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_INTR_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_intr_%s",
+ q_num,
+ ionic_dbg_intr_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_NAPI_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_napi_%s",
+ q_num,
+ ionic_dbg_napi_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < MAX_NUM_NAPI_CNTR; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_napi_work_done_%d",
+ q_num, i);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < MAX_NUM_SG_CNTR; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "txq_%d_sg_cntr_%d",
+ q_num, i);
+ *buf += ETH_GSTRING_LEN;
+ }
+ }
+ }
+ for (q_num = 0; q_num < MAX_Q(lif); q_num++) {
+ for (i = 0; i < IONIC_NUM_RX_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "rx_%d_%s",
+ q_num, ionic_rx_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state)) {
+ for (i = 0; i < IONIC_NUM_DBG_CQ_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "rxq_%d_cq_%s",
+ q_num,
+ ionic_dbg_cq_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_INTR_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "rxq_%d_intr_%s",
+ q_num,
+ ionic_dbg_intr_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_NAPI_STATS; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "rxq_%d_napi_%s",
+ q_num,
+ ionic_dbg_napi_stats_desc[i].name);
+ *buf += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < MAX_NUM_NAPI_CNTR; i++) {
+ snprintf(*buf, ETH_GSTRING_LEN,
+ "rxq_%d_napi_work_done_%d",
+ q_num, i);
+ *buf += ETH_GSTRING_LEN;
+ }
+ }
+ }
+}
+
+static void ionic_sw_stats_get_values(struct lif *lif, u64 **buf)
+{
+ struct lif_sw_stats lif_stats;
+ struct qcq *txqcq, *rxqcq;
+ int i, q_num;
+
+ ionic_get_lif_stats(lif, &lif_stats);
+
+ for (i = 0; i < IONIC_NUM_LIF_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&lif_stats, &ionic_lif_stats_desc[i]);
+ (*buf)++;
+ }
+
+ for (q_num = 0; q_num < MAX_Q(lif); q_num++) {
+ txqcq = lif_to_txqcq(lif, q_num);
+
+ for (i = 0; i < IONIC_NUM_TX_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&txqcq->stats->tx,
+ &ionic_tx_stats_desc[i]);
+ (*buf)++;
+ }
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state)) {
+ for (i = 0; i < IONIC_NUM_TX_Q_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&txqcq->q,
+ &ionic_txq_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_CQ_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&txqcq->cq,
+ &ionic_dbg_cq_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_INTR_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&txqcq->intr,
+ &ionic_dbg_intr_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_NAPI_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&txqcq->napi_stats,
+ &ionic_dbg_napi_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < MAX_NUM_NAPI_CNTR; i++) {
+ **buf = txqcq->napi_stats.work_done_cntr[i];
+ (*buf)++;
+ }
+ for (i = 0; i < MAX_NUM_SG_CNTR; i++) {
+ **buf = txqcq->stats->tx.sg_cntr[i];
+ (*buf)++;
+ }
+ }
+ }
+
+ for (q_num = 0; q_num < MAX_Q(lif); q_num++) {
+ rxqcq = lif_to_rxqcq(lif, q_num);
+
+ for (i = 0; i < IONIC_NUM_RX_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&rxqcq->stats->rx,
+ &ionic_rx_stats_desc[i]);
+ (*buf)++;
+ }
+
+ if (test_bit(LIF_SW_DEBUG_STATS, lif->state)) {
+ for (i = 0; i < IONIC_NUM_DBG_CQ_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&rxqcq->cq,
+ &ionic_dbg_cq_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_INTR_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&rxqcq->intr,
+ &ionic_dbg_intr_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < IONIC_NUM_DBG_NAPI_STATS; i++) {
+ **buf = IONIC_READ_STAT64(&rxqcq->napi_stats,
+ &ionic_dbg_napi_stats_desc[i]);
+ (*buf)++;
+ }
+ for (i = 0; i < MAX_NUM_NAPI_CNTR; i++) {
+ **buf = rxqcq->napi_stats.work_done_cntr[i];
+ (*buf)++;
+ }
+ }
+ }
+}
+
+const struct ionic_stats_group_intf ionic_stats_groups[] = {
+ /* SW Stats group */
+ {
+ .get_strings = ionic_sw_stats_get_strings,
+ .get_values = ionic_sw_stats_get_values,
+ .get_count = ionic_sw_stats_get_count,
+ },
+ /* Add more stat groups here */
+};
+
+const int ionic_num_stats_grps = ARRAY_SIZE(ionic_stats_groups);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.h b/drivers/net/ethernet/pensando/ionic/ionic_stats.h
new file mode 100644
index 000000000000..b5487e7fd4fb
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#ifndef _IONIC_STATS_H_
+#define _IONIC_STATS_H_
+
+#define IONIC_STAT_TO_OFFSET(type, stat_name) (offsetof(type, stat_name))
+
+#define IONIC_STAT_DESC(type, stat_name) { \
+ .name = #stat_name, \
+ .offset = IONIC_STAT_TO_OFFSET(type, stat_name) \
+}
+
+#define IONIC_LIF_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct lif_sw_stats, stat_name)
+
+#define IONIC_TX_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct tx_stats, stat_name)
+
+#define IONIC_RX_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct rx_stats, stat_name)
+
+#define IONIC_TX_Q_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct queue, stat_name)
+
+#define IONIC_CQ_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct cq, stat_name)
+
+#define IONIC_INTR_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct intr, stat_name)
+
+#define IONIC_NAPI_STAT_DESC(stat_name) \
+ IONIC_STAT_DESC(struct napi_stats, stat_name)
+
+/* Interface structure for a particalar stats group */
+struct ionic_stats_group_intf {
+ void (*get_strings)(struct lif *lif, u8 **buf);
+ void (*get_values)(struct lif *lif, u64 **buf);
+ u64 (*get_count)(struct lif *lif);
+};
+
+extern const struct ionic_stats_group_intf ionic_stats_groups[];
+extern const int ionic_num_stats_grps;
+
+#define IONIC_READ_STAT64(base_ptr, desc_ptr) \
+ (*((u64 *)(((u8 *)(base_ptr)) + (desc_ptr)->offset)))
+
+struct ionic_stat_desc {
+ char name[ETH_GSTRING_LEN];
+ u64 offset;
+};
+
+#endif // _IONIC_STATS_H_
--
2.17.1
^ permalink raw reply related
* [PATCH v2 net-next 18/19] ionic: Add coalesce and other features
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
Interrupt coalescing, tunable copybreak value, and
tx timeout.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/ionic.h | 2 +-
.../ethernet/pensando/ionic/ionic_ethtool.c | 105 ++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_lif.c | 13 ++-
.../net/ethernet/pensando/ionic/ionic_lif.h | 1 +
4 files changed, 119 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index 9b720187b549..cd08166f73a9 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -11,7 +11,7 @@ struct lif;
#define DRV_NAME "ionic"
#define DRV_DESCRIPTION "Pensando Ethernet NIC Driver"
-#define DRV_VERSION "0.11.0-k"
+#define DRV_VERSION "0.11.0-44-k"
#define PCI_VENDOR_ID_PENSANDO 0x1dd8
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
index cd4c0651096a..86ff65f2f195 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
@@ -391,6 +391,75 @@ static int ionic_get_coalesce(struct net_device *netdev,
return 0;
}
+static int ionic_set_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *coalesce)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct identity *ident = &lif->ionic->ident;
+ struct ionic_dev *idev = &lif->ionic->idev;
+ u32 tx_coal, rx_coal;
+ struct qcq *qcq;
+ unsigned int i;
+
+ if (coalesce->rx_max_coalesced_frames ||
+ coalesce->rx_coalesce_usecs_irq ||
+ coalesce->rx_max_coalesced_frames_irq ||
+ coalesce->tx_max_coalesced_frames ||
+ coalesce->tx_coalesce_usecs_irq ||
+ coalesce->tx_max_coalesced_frames_irq ||
+ coalesce->stats_block_coalesce_usecs ||
+ coalesce->use_adaptive_rx_coalesce ||
+ coalesce->use_adaptive_tx_coalesce ||
+ coalesce->pkt_rate_low ||
+ coalesce->rx_coalesce_usecs_low ||
+ coalesce->rx_max_coalesced_frames_low ||
+ coalesce->tx_coalesce_usecs_low ||
+ coalesce->tx_max_coalesced_frames_low ||
+ coalesce->pkt_rate_high ||
+ coalesce->rx_coalesce_usecs_high ||
+ coalesce->rx_max_coalesced_frames_high ||
+ coalesce->tx_coalesce_usecs_high ||
+ coalesce->tx_max_coalesced_frames_high ||
+ coalesce->rate_sample_interval)
+ return -EINVAL;
+
+ if (ident->dev.intr_coal_div == 0)
+ return -EIO;
+
+ /* Convert from usecs to device units */
+ tx_coal = coalesce->tx_coalesce_usecs *
+ le32_to_cpu(ident->dev.intr_coal_mult) /
+ le32_to_cpu(ident->dev.intr_coal_div);
+ rx_coal = coalesce->rx_coalesce_usecs *
+ le32_to_cpu(ident->dev.intr_coal_mult) /
+ le32_to_cpu(ident->dev.intr_coal_div);
+
+ if (tx_coal > INTR_CTRL_COAL_MAX || rx_coal > INTR_CTRL_COAL_MAX)
+ return -ERANGE;
+
+ if (coalesce->tx_coalesce_usecs != lif->tx_coalesce_usecs) {
+ for (i = 0; i < lif->nxqs; i++) {
+ qcq = lif->txqcqs[i].qcq;
+ ionic_intr_coal_init(idev->intr_ctrl,
+ qcq->intr.index,
+ tx_coal);
+ }
+ lif->tx_coalesce_usecs = coalesce->tx_coalesce_usecs;
+ }
+
+ if (coalesce->rx_coalesce_usecs != lif->rx_coalesce_usecs) {
+ for (i = 0; i < lif->nxqs; i++) {
+ qcq = lif->rxqcqs[i].qcq;
+ ionic_intr_coal_init(idev->intr_ctrl,
+ qcq->intr.index,
+ rx_coal);
+ }
+ lif->rx_coalesce_usecs = coalesce->rx_coalesce_usecs;
+ }
+
+ return 0;
+}
+
static void ionic_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring)
{
@@ -578,6 +647,39 @@ static int ionic_set_priv_flags(struct net_device *netdev, u32 priv_flags)
return 0;
}
+static int ionic_set_tunable(struct net_device *dev,
+ const struct ethtool_tunable *tuna,
+ const void *data)
+{
+ struct lif *lif = netdev_priv(dev);
+
+ switch (tuna->id) {
+ case ETHTOOL_RX_COPYBREAK:
+ lif->rx_copybreak = *(u32 *)data;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ionic_get_tunable(struct net_device *netdev,
+ const struct ethtool_tunable *tuna, void *data)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ switch (tuna->id) {
+ case ETHTOOL_RX_COPYBREAK:
+ *(u32 *)data = lif->rx_copybreak;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static int ionic_get_module_info(struct net_device *netdev,
struct ethtool_modinfo *modinfo)
@@ -663,6 +765,7 @@ static const struct ethtool_ops ionic_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_link_ksettings = ionic_get_link_ksettings,
.get_coalesce = ionic_get_coalesce,
+ .set_coalesce = ionic_set_coalesce,
.get_ringparam = ionic_get_ringparam,
.set_ringparam = ionic_set_ringparam,
.get_channels = ionic_get_channels,
@@ -677,6 +780,8 @@ static const struct ethtool_ops ionic_ethtool_ops = {
.set_rxfh = ionic_set_rxfh,
.get_priv_flags = ionic_get_priv_flags,
.set_priv_flags = ionic_set_priv_flags,
+ .get_tunable = ionic_get_tunable,
+ .set_tunable = ionic_set_tunable,
.get_module_info = ionic_get_module_info,
.get_module_eeprom = ionic_get_module_eeprom,
.get_pauseparam = ionic_get_pauseparam,
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 42c0827255a5..c541076eac1d 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -744,9 +744,19 @@ static int ionic_change_mtu(struct net_device *netdev, int new_mtu)
return err;
}
+static void ionic_tx_timeout_work(struct work_struct *ws)
+{
+ struct lif *lif = container_of(ws, struct lif, tx_timeout_work);
+
+ netdev_info(lif->netdev, "Tx Timeout recovery\n");
+ ionic_reset_queues(lif);
+}
+
static void ionic_tx_timeout(struct net_device *netdev)
{
- netdev_info(netdev, "%s: stubbed\n", __func__);
+ struct lif *lif = netdev_priv(netdev);
+
+ schedule_work(&lif->tx_timeout_work);
}
static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
@@ -2013,6 +2023,7 @@ static int ionic_lif_init(struct lif *lif)
ionic_link_status_check(lif);
+ INIT_WORK(&lif->tx_timeout_work, ionic_tx_timeout_work);
return 0;
err_out_notifyq_deinit:
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 0e6908f959f2..76cc519acd5a 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -180,6 +180,7 @@ struct lif {
unsigned int dbid_count;
struct dentry *dentry;
u32 flags;
+ struct work_struct tx_timeout_work;
};
#define lif_to_txqcq(lif, i) ((lif)->txqcqs[i].qcq)
--
2.17.1
^ permalink raw reply related
* [PATCH v2 net-next 17/19] ionic: Add RSS support
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
Add code to manipulate through ethtool the RSS configuration
used by the NIC.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
.../ethernet/pensando/ionic/ionic_ethtool.c | 73 +++++++++++++++
.../net/ethernet/pensando/ionic/ionic_lif.c | 89 +++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_lif.h | 8 ++
3 files changed, 170 insertions(+)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
index 518e79c90fca..cd4c0651096a 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
@@ -484,6 +484,74 @@ static int ionic_set_channels(struct net_device *netdev,
return 0;
}
+static int ionic_get_rxnfc(struct net_device *netdev,
+ struct ethtool_rxnfc *info, u32 *rules)
+{
+ struct lif *lif = netdev_priv(netdev);
+ int err = 0;
+
+ switch (info->cmd) {
+ case ETHTOOL_GRXRINGS:
+ info->data = lif->nxqs;
+ break;
+ default:
+ netdev_err(netdev, "Command parameter %d is not supported\n",
+ info->cmd);
+ err = -EOPNOTSUPP;
+ }
+
+ return err;
+}
+
+static u32 ionic_get_rxfh_indir_size(struct net_device *netdev)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ return le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
+}
+
+static u32 ionic_get_rxfh_key_size(struct net_device *netdev)
+{
+ return IONIC_RSS_HASH_KEY_SIZE;
+}
+
+static int ionic_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
+ u8 *hfunc)
+{
+ struct lif *lif = netdev_priv(netdev);
+ unsigned int i, tbl_sz;
+
+ if (indir) {
+ tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
+ for (i = 0; i < tbl_sz; i++)
+ indir[i] = lif->rss_ind_tbl[i];
+ }
+
+ if (key)
+ memcpy(key, lif->rss_hash_key, IONIC_RSS_HASH_KEY_SIZE);
+
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+
+ return 0;
+}
+
+static int ionic_set_rxfh(struct net_device *netdev, const u32 *indir,
+ const u8 *key, const u8 hfunc)
+{
+ struct lif *lif = netdev_priv(netdev);
+ int err;
+
+ if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
+ return -EOPNOTSUPP;
+
+ err = ionic_lif_rss_config(lif, lif->rss_types, key, indir);
+ if (err)
+ return err;
+
+ return 0;
+}
+
static u32 ionic_get_priv_flags(struct net_device *netdev)
{
struct lif *lif = netdev_priv(netdev);
@@ -602,6 +670,11 @@ static const struct ethtool_ops ionic_ethtool_ops = {
.get_strings = ionic_get_strings,
.get_ethtool_stats = ionic_get_stats,
.get_sset_count = ionic_get_sset_count,
+ .get_rxnfc = ionic_get_rxnfc,
+ .get_rxfh_indir_size = ionic_get_rxfh_indir_size,
+ .get_rxfh_key_size = ionic_get_rxfh_key_size,
+ .get_rxfh = ionic_get_rxfh,
+ .set_rxfh = ionic_set_rxfh,
.get_priv_flags = ionic_get_priv_flags,
.set_priv_flags = ionic_set_priv_flags,
.get_module_info = ionic_get_module_info,
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 76974c61c306..42c0827255a5 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -26,6 +26,8 @@ static void ionic_qcq_free(struct lif *lif, struct qcq *qcq);
static int ionic_lif_txqs_init(struct lif *lif);
static int ionic_lif_rxqs_init(struct lif *lif);
static void ionic_lif_qcq_deinit(struct lif *lif, struct qcq *qcq);
+static int ionic_lif_rss_init(struct lif *lif);
+static int ionic_lif_rss_deinit(struct lif *lif);
static int ionic_set_nic_features(struct lif *lif, netdev_features_t features);
static int ionic_notifyq_clean(struct lif *lif, int budget);
@@ -1082,6 +1084,9 @@ static int ionic_txrx_init(struct lif *lif)
if (err)
goto err_out;
+ if (lif->netdev->features & NETIF_F_RXHASH)
+ ionic_lif_rss_init(lif);
+
ionic_set_rx_mode(lif->netdev);
return 0;
@@ -1206,6 +1211,7 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
struct device *dev = ionic->dev;
struct net_device *netdev;
struct lif *lif;
+ int tbl_sz;
int err;
netdev = alloc_etherdev_mqs(sizeof(*lif),
@@ -1260,10 +1266,24 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
if (err)
goto err_out_free_lif_info;
+ /* allocate rss indirection table */
+ tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
+ lif->rss_ind_tbl_sz = sizeof(*lif->rss_ind_tbl) * tbl_sz;
+ lif->rss_ind_tbl = dma_alloc_coherent(dev, lif->rss_ind_tbl_sz,
+ &lif->rss_ind_tbl_pa,
+ GFP_KERNEL);
+
+ if (!lif->rss_ind_tbl) {
+ dev_err(dev, "Failed to allocate rss indirection table, aborting\n");
+ goto err_out_free_qcqs;
+ }
+
list_add_tail(&lif->list, &ionic->lifs);
return lif;
+err_out_free_qcqs:
+ ionic_qcqs_free(lif);
err_out_free_lif_info:
dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa);
lif->info = NULL;
@@ -1302,6 +1322,14 @@ static void ionic_lif_free(struct lif *lif)
{
struct device *dev = lif->ionic->dev;
+ /* free rss indirection table */
+ if (lif->rss_ind_tbl) {
+ dma_free_coherent(dev, lif->rss_ind_tbl_sz, lif->rss_ind_tbl,
+ lif->rss_ind_tbl_pa);
+ lif->rss_ind_tbl = NULL;
+ lif->rss_ind_tbl_pa = 0;
+ }
+
/* free queues */
ionic_qcqs_free(lif);
ionic_lif_reset(lif);
@@ -1339,6 +1367,66 @@ void ionic_lifs_free(struct ionic *ionic)
}
}
+int ionic_lif_rss_config(struct lif *lif, const u16 types,
+ const u8 *key, const u32 *indir)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.lif_setattr = {
+ .opcode = CMD_OPCODE_LIF_SETATTR,
+ .attr = IONIC_LIF_ATTR_RSS,
+ .rss.types = cpu_to_le16(types),
+ .rss.addr = cpu_to_le64(lif->rss_ind_tbl_pa),
+ },
+ };
+ unsigned int i, tbl_sz;
+
+ lif->rss_types = types;
+
+ if (key)
+ memcpy(lif->rss_hash_key, key, IONIC_RSS_HASH_KEY_SIZE);
+
+ if (indir) {
+ tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
+ for (i = 0; i < tbl_sz; i++)
+ lif->rss_ind_tbl[i] = indir[i];
+ }
+
+ memcpy(ctx.cmd.lif_setattr.rss.key, lif->rss_hash_key,
+ IONIC_RSS_HASH_KEY_SIZE);
+
+ return ionic_adminq_post_wait(lif, &ctx);
+}
+
+static int ionic_lif_rss_init(struct lif *lif)
+{
+ u8 rss_key[IONIC_RSS_HASH_KEY_SIZE];
+ unsigned int tbl_sz;
+ unsigned int i;
+
+ netdev_rss_key_fill(rss_key, IONIC_RSS_HASH_KEY_SIZE);
+
+ lif->rss_types = IONIC_RSS_TYPE_IPV4 |
+ IONIC_RSS_TYPE_IPV4_TCP |
+ IONIC_RSS_TYPE_IPV4_UDP |
+ IONIC_RSS_TYPE_IPV6 |
+ IONIC_RSS_TYPE_IPV6_TCP |
+ IONIC_RSS_TYPE_IPV6_UDP;
+
+ /* Fill indirection table with 'default' values */
+ tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz);
+ for (i = 0; i < tbl_sz; i++)
+ lif->rss_ind_tbl[i] = ethtool_rxfh_indir_default(i, lif->nxqs);
+
+ return ionic_lif_rss_config(lif, lif->rss_types, rss_key, NULL);
+}
+
+static int ionic_lif_rss_deinit(struct lif *lif)
+{
+ /* Disable RSS on the NIC */
+ return ionic_lif_rss_config(lif, 0x0, NULL, NULL);
+}
+
static void ionic_lif_qcq_deinit(struct lif *lif, struct qcq *qcq)
{
struct ionic_dev *idev = &lif->ionic->idev;
@@ -1371,6 +1459,7 @@ static void ionic_lif_deinit(struct lif *lif)
clear_bit(LIF_INITED, lif->state);
ionic_rx_filters_deinit(lif);
+ ionic_lif_rss_deinit(lif);
napi_disable(&lif->adminqcq->napi);
ionic_lif_qcq_deinit(lif, lif->notifyqcq);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index f93bfa2b4393..0e6908f959f2 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -166,6 +166,12 @@ struct lif {
dma_addr_t info_pa;
u32 info_sz;
+ u16 rss_types;
+ u8 rss_hash_key[IONIC_RSS_HASH_KEY_SIZE];
+ u8 *rss_ind_tbl;
+ dma_addr_t rss_ind_tbl_pa;
+ u32 rss_ind_tbl_sz;
+
struct rx_filters rx_filters;
struct ionic_deferred deferred;
u32 tx_coalesce_usecs;
@@ -215,6 +221,8 @@ void ionic_lifs_unregister(struct ionic *ionic);
int ionic_lif_identify(struct ionic *ionic, u8 lif_type,
union lif_identity *lif_ident);
int ionic_lifs_size(struct ionic *ionic);
+int ionic_lif_rss_config(struct lif *lif, u16 types,
+ const u8 *key, const u32 *indir);
int ionic_open(struct net_device *netdev);
int ionic_stop(struct net_device *netdev);
--
2.17.1
^ permalink raw reply related
* [PATCH v2 net-next 09/19] ionic: Add the basic NDO callbacks for netdev support
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
Set up the initial NDO structure and callbacks for netdev
to use, and register the netdev. This will allow us to do
a few basic operations on the device, but no traffic yet.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/ionic.h | 1 +
.../ethernet/pensando/ionic/ionic_bus_pci.c | 9 +
.../net/ethernet/pensando/ionic/ionic_dev.h | 2 +
.../net/ethernet/pensando/ionic/ionic_lif.c | 348 ++++++++++++++++++
.../net/ethernet/pensando/ionic/ionic_lif.h | 5 +
5 files changed, 365 insertions(+)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index 87ab13aee89e..d7eee79b2a10 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -34,6 +34,7 @@ struct ionic {
unsigned int num_bars;
struct identity ident;
struct list_head lifs;
+ struct lif *master_lif;
unsigned int nnqs_per_lif;
unsigned int neqs_per_lif;
unsigned int ntxqs_per_lif;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
index 59d1ae7ce532..98c12b770c7f 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
@@ -206,8 +206,16 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_free_lifs;
}
+ err = ionic_lifs_register(ionic);
+ if (err) {
+ dev_err(dev, "Cannot register LIFs: %d, aborting\n", err);
+ goto err_out_deinit_lifs;
+ }
+
return 0;
+err_out_deinit_lifs:
+ ionic_lifs_deinit(ionic);
err_out_free_lifs:
ionic_lifs_free(ionic);
err_out_free_irqs:
@@ -239,6 +247,7 @@ static void ionic_remove(struct pci_dev *pdev)
struct ionic *ionic = pci_get_drvdata(pdev);
if (ionic) {
+ ionic_lifs_unregister(ionic);
ionic_lifs_deinit(ionic);
ionic_lifs_free(ionic);
ionic_bus_free_irq_vectors(ionic);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
index 7014acd70b98..d44220c1d430 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
@@ -10,6 +10,8 @@
#include "ionic_if.h"
#include "ionic_regs.h"
+#define IONIC_MIN_MTU ETH_MIN_MTU
+#define IONIC_MAX_MTU 9194
#define IONIC_LIFS_MAX 1024
struct ionic_dev_bar {
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index b59cf5e739fc..e32a92af22cf 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -12,8 +12,74 @@
#include "ionic_lif.h"
#include "ionic_debugfs.h"
+static int ionic_set_nic_features(struct lif *lif, netdev_features_t features);
static int ionic_notifyq_clean(struct lif *lif, int budget);
+int ionic_open(struct net_device *netdev)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ netif_carrier_off(netdev);
+
+ set_bit(LIF_UP, lif->state);
+
+ if (netif_carrier_ok(netdev))
+ netif_tx_wake_all_queues(netdev);
+
+ return 0;
+}
+
+static int ionic_lif_stop(struct lif *lif)
+{
+ struct net_device *ndev = lif->netdev;
+ int err = 0;
+
+ if (!test_bit(LIF_UP, lif->state)) {
+ dev_dbg(lif->ionic->dev, "%s: %s state=DOWN\n",
+ __func__, lif->name);
+ return 0;
+ }
+ dev_dbg(lif->ionic->dev, "%s: %s state=UP\n", __func__, lif->name);
+ clear_bit(LIF_UP, lif->state);
+
+ /* carrier off before disabling queues to avoid watchdog timeout */
+ netif_carrier_off(ndev);
+ netif_tx_stop_all_queues(ndev);
+ netif_tx_disable(ndev);
+ synchronize_rcu();
+
+ return err;
+}
+
+int ionic_stop(struct net_device *netdev)
+{
+ struct lif *lif = netdev_priv(netdev);
+
+ return ionic_lif_stop(lif);
+}
+
+int ionic_reset_queues(struct lif *lif)
+{
+ bool running;
+ int err = 0;
+
+ /* Put off the next watchdog timeout */
+ netif_trans_update(lif->netdev);
+
+ while (test_and_set_bit(LIF_QUEUE_RESET, lif->state))
+ usleep_range(100, 200);
+
+ running = netif_running(lif->netdev);
+ if (running)
+ err = ionic_stop(lif->netdev);
+ if (!err && running)
+ ionic_open(lif->netdev);
+
+ clear_bit(LIF_QUEUE_RESET, lif->state);
+
+ return err;
+}
+
static bool ionic_adminq_service(struct cq *cq, struct cq_info *cq_info)
{
struct admin_comp *comp = cq_info->cq_desc;
@@ -114,6 +180,81 @@ static int ionic_notifyq_clean(struct lif *lif, int budget)
return work_done;
}
+static int ionic_set_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ struct lif *lif = netdev_priv(netdev);
+ int err;
+
+ netdev_dbg(netdev, "%s: lif->features=0x%08llx new_features=0x%08llx\n",
+ __func__, (u64)lif->netdev->features, (u64)features);
+
+ err = ionic_set_nic_features(lif, features);
+
+ return err;
+}
+
+static int ionic_set_mac_address(struct net_device *netdev, void *sa)
+{
+ netdev_info(netdev, "%s: stubbed\n", __func__);
+ return 0;
+}
+
+static int ionic_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.lif_setattr = {
+ .opcode = CMD_OPCODE_LIF_SETATTR,
+ .index = cpu_to_le16(lif->index),
+ .attr = IONIC_LIF_ATTR_MTU,
+ .mtu = cpu_to_le32(new_mtu),
+ },
+ };
+ int err;
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ netdev->mtu = new_mtu;
+ err = ionic_reset_queues(lif);
+
+ return err;
+}
+
+static void ionic_tx_timeout(struct net_device *netdev)
+{
+ netdev_info(netdev, "%s: stubbed\n", __func__);
+}
+
+static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
+ u16 vid)
+{
+ netdev_info(netdev, "%s: stubbed\n", __func__);
+ return 0;
+}
+
+static int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto,
+ u16 vid)
+{
+ netdev_info(netdev, "%s: stubbed\n", __func__);
+ return 0;
+}
+
+static const struct net_device_ops ionic_netdev_ops = {
+ .ndo_open = ionic_open,
+ .ndo_stop = ionic_stop,
+ .ndo_set_features = ionic_set_features,
+ .ndo_set_mac_address = ionic_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_tx_timeout = ionic_tx_timeout,
+ .ndo_change_mtu = ionic_change_mtu,
+ .ndo_vlan_rx_add_vid = ionic_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = ionic_vlan_rx_kill_vid,
+};
+
static irqreturn_t ionic_isr(int irq, void *data)
{
struct napi_struct *napi = data;
@@ -388,6 +529,12 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
lif = netdev_priv(netdev);
lif->netdev = netdev;
+ ionic->master_lif = lif;
+ netdev->netdev_ops = &ionic_netdev_ops;
+
+ netdev->watchdog_timeo = 2 * HZ;
+ netdev->min_mtu = IONIC_MIN_MTU;
+ netdev->max_mtu = IONIC_MAX_MTU;
lif->neqs = ionic->neqs_per_lif;
lif->nxqs = ionic->ntxqs_per_lif;
@@ -657,6 +804,177 @@ static int ionic_lif_notifyq_init(struct lif *lif)
return 0;
}
+static __le64 ionic_netdev_features_to_nic(netdev_features_t features)
+{
+ u64 wanted = 0;
+
+ if (features & NETIF_F_HW_VLAN_CTAG_TX)
+ wanted |= ETH_HW_VLAN_TX_TAG;
+ if (features & NETIF_F_HW_VLAN_CTAG_RX)
+ wanted |= ETH_HW_VLAN_RX_STRIP;
+ if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
+ wanted |= ETH_HW_VLAN_RX_FILTER;
+ if (features & NETIF_F_RXHASH)
+ wanted |= ETH_HW_RX_HASH;
+ if (features & NETIF_F_RXCSUM)
+ wanted |= ETH_HW_RX_CSUM;
+ if (features & NETIF_F_SG)
+ wanted |= ETH_HW_TX_SG;
+ if (features & NETIF_F_HW_CSUM)
+ wanted |= ETH_HW_TX_CSUM;
+ if (features & NETIF_F_TSO)
+ wanted |= ETH_HW_TSO;
+ if (features & NETIF_F_TSO6)
+ wanted |= ETH_HW_TSO_IPV6;
+ if (features & NETIF_F_TSO_ECN)
+ wanted |= ETH_HW_TSO_ECN;
+ if (features & NETIF_F_GSO_GRE)
+ wanted |= ETH_HW_TSO_GRE;
+ if (features & NETIF_F_GSO_GRE_CSUM)
+ wanted |= ETH_HW_TSO_GRE_CSUM;
+ if (features & NETIF_F_GSO_IPXIP4)
+ wanted |= ETH_HW_TSO_IPXIP4;
+ if (features & NETIF_F_GSO_IPXIP6)
+ wanted |= ETH_HW_TSO_IPXIP6;
+ if (features & NETIF_F_GSO_UDP_TUNNEL)
+ wanted |= ETH_HW_TSO_UDP;
+ if (features & NETIF_F_GSO_UDP_TUNNEL_CSUM)
+ wanted |= ETH_HW_TSO_UDP_CSUM;
+
+ return cpu_to_le64(wanted);
+}
+
+static int ionic_set_nic_features(struct lif *lif, netdev_features_t features)
+{
+ struct device *dev = lif->ionic->dev;
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.lif_setattr = {
+ .opcode = CMD_OPCODE_LIF_SETATTR,
+ .index = cpu_to_le16(lif->index),
+ .attr = IONIC_LIF_ATTR_FEATURES,
+ },
+ };
+ u64 vlan_flags = ETH_HW_VLAN_TX_TAG |
+ ETH_HW_VLAN_RX_STRIP |
+ ETH_HW_VLAN_RX_FILTER;
+ int err;
+
+ ctx.cmd.lif_setattr.features = ionic_netdev_features_to_nic(features);
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ lif->hw_features = le64_to_cpu(ctx.cmd.lif_setattr.features &
+ ctx.comp.lif_setattr.features);
+
+ if ((vlan_flags & features) &&
+ !(vlan_flags & le64_to_cpu(ctx.comp.lif_setattr.features)))
+ dev_info_once(lif->ionic->dev, "NIC is not supporting vlan offload, likely in SmartNIC mode\n");
+
+ if (lif->hw_features & ETH_HW_VLAN_TX_TAG)
+ dev_dbg(dev, "feature ETH_HW_VLAN_TX_TAG\n");
+ if (lif->hw_features & ETH_HW_VLAN_RX_STRIP)
+ dev_dbg(dev, "feature ETH_HW_VLAN_RX_STRIP\n");
+ if (lif->hw_features & ETH_HW_VLAN_RX_FILTER)
+ dev_dbg(dev, "feature ETH_HW_VLAN_RX_FILTER\n");
+ if (lif->hw_features & ETH_HW_RX_HASH)
+ dev_dbg(dev, "feature ETH_HW_RX_HASH\n");
+ if (lif->hw_features & ETH_HW_TX_SG)
+ dev_dbg(dev, "feature ETH_HW_TX_SG\n");
+ if (lif->hw_features & ETH_HW_TX_CSUM)
+ dev_dbg(dev, "feature ETH_HW_TX_CSUM\n");
+ if (lif->hw_features & ETH_HW_RX_CSUM)
+ dev_dbg(dev, "feature ETH_HW_RX_CSUM\n");
+ if (lif->hw_features & ETH_HW_TSO)
+ dev_dbg(dev, "feature ETH_HW_TSO\n");
+ if (lif->hw_features & ETH_HW_TSO_IPV6)
+ dev_dbg(dev, "feature ETH_HW_TSO_IPV6\n");
+ if (lif->hw_features & ETH_HW_TSO_ECN)
+ dev_dbg(dev, "feature ETH_HW_TSO_ECN\n");
+ if (lif->hw_features & ETH_HW_TSO_GRE)
+ dev_dbg(dev, "feature ETH_HW_TSO_GRE\n");
+ if (lif->hw_features & ETH_HW_TSO_GRE_CSUM)
+ dev_dbg(dev, "feature ETH_HW_TSO_GRE_CSUM\n");
+ if (lif->hw_features & ETH_HW_TSO_IPXIP4)
+ dev_dbg(dev, "feature ETH_HW_TSO_IPXIP4\n");
+ if (lif->hw_features & ETH_HW_TSO_IPXIP6)
+ dev_dbg(dev, "feature ETH_HW_TSO_IPXIP6\n");
+ if (lif->hw_features & ETH_HW_TSO_UDP)
+ dev_dbg(dev, "feature ETH_HW_TSO_UDP\n");
+ if (lif->hw_features & ETH_HW_TSO_UDP_CSUM)
+ dev_dbg(dev, "feature ETH_HW_TSO_UDP_CSUM\n");
+
+ return 0;
+}
+
+static int ionic_init_nic_features(struct lif *lif)
+{
+ struct net_device *netdev = lif->netdev;
+ netdev_features_t features;
+ int err;
+
+ /* set up what we expect to support by default */
+ features = NETIF_F_HW_VLAN_CTAG_TX |
+ NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_RXHASH |
+ NETIF_F_SG |
+ NETIF_F_HW_CSUM |
+ NETIF_F_RXCSUM |
+ NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_TSO_ECN;
+
+ err = ionic_set_nic_features(lif, features);
+ if (err)
+ return err;
+
+ /* tell the netdev what we actually can support */
+ netdev->features |= NETIF_F_HIGHDMA;
+
+ if (lif->hw_features & ETH_HW_VLAN_TX_TAG)
+ netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;
+ if (lif->hw_features & ETH_HW_VLAN_RX_STRIP)
+ netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
+ if (lif->hw_features & ETH_HW_VLAN_RX_FILTER)
+ netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ if (lif->hw_features & ETH_HW_RX_HASH)
+ netdev->hw_features |= NETIF_F_RXHASH;
+ if (lif->hw_features & ETH_HW_TX_SG)
+ netdev->hw_features |= NETIF_F_SG;
+
+ if (lif->hw_features & ETH_HW_TX_CSUM)
+ netdev->hw_enc_features |= NETIF_F_HW_CSUM;
+ if (lif->hw_features & ETH_HW_RX_CSUM)
+ netdev->hw_enc_features |= NETIF_F_RXCSUM;
+ if (lif->hw_features & ETH_HW_TSO)
+ netdev->hw_enc_features |= NETIF_F_TSO;
+ if (lif->hw_features & ETH_HW_TSO_IPV6)
+ netdev->hw_enc_features |= NETIF_F_TSO6;
+ if (lif->hw_features & ETH_HW_TSO_ECN)
+ netdev->hw_enc_features |= NETIF_F_TSO_ECN;
+ if (lif->hw_features & ETH_HW_TSO_GRE)
+ netdev->hw_enc_features |= NETIF_F_GSO_GRE;
+ if (lif->hw_features & ETH_HW_TSO_GRE_CSUM)
+ netdev->hw_enc_features |= NETIF_F_GSO_GRE_CSUM;
+ if (lif->hw_features & ETH_HW_TSO_IPXIP4)
+ netdev->hw_enc_features |= NETIF_F_GSO_IPXIP4;
+ if (lif->hw_features & ETH_HW_TSO_IPXIP6)
+ netdev->hw_enc_features |= NETIF_F_GSO_IPXIP6;
+ if (lif->hw_features & ETH_HW_TSO_UDP)
+ netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL;
+ if (lif->hw_features & ETH_HW_TSO_UDP_CSUM)
+ netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;
+
+ netdev->hw_features |= netdev->hw_enc_features;
+ netdev->features |= netdev->hw_features;
+
+ netdev->priv_flags |= IFF_UNICAST_FLT;
+
+ return 0;
+}
+
static int ionic_lif_init(struct lif *lif)
{
struct ionic_dev *idev = &lif->ionic->idev;
@@ -713,6 +1031,10 @@ static int ionic_lif_init(struct lif *lif)
goto err_out_notifyq_deinit;
}
+ err = ionic_init_nic_features(lif);
+ if (err)
+ goto err_out_notifyq_deinit;
+
set_bit(LIF_INITED, lif->state);
return 0;
@@ -747,6 +1069,32 @@ int ionic_lifs_init(struct ionic *ionic)
return 0;
}
+int ionic_lifs_register(struct ionic *ionic)
+{
+ int err;
+
+ /* only register LIF0 for now */
+ err = register_netdev(ionic->master_lif->netdev);
+ if (err) {
+ dev_err(ionic->dev, "Cannot register net device, aborting\n");
+ return err;
+ }
+
+ ionic->master_lif->registered = true;
+
+ return 0;
+}
+
+void ionic_lifs_unregister(struct ionic *ionic)
+{
+ /* There is only one lif ever registered in the
+ * current model, so don't bother searching the
+ * ionic->lif for candidates to unregister
+ */
+ if (ionic->master_lif->netdev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(ionic->master_lif->netdev);
+}
+
int ionic_lif_identify(struct ionic *ionic, u8 lif_type,
union lif_identity *lid)
{
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 80eec0778f40..ef3f7340a277 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -61,6 +61,8 @@ struct qcq {
enum lif_state_flags {
LIF_INITED,
+ LIF_UP,
+ LIF_QUEUE_RESET,
/* leave this as last */
LIF_STATE_SIZE
@@ -84,6 +86,7 @@ struct lif {
u64 last_eid;
unsigned int neqs;
unsigned int nxqs;
+ u64 hw_features;
struct lif_info *info;
dma_addr_t info_pa;
@@ -124,6 +127,8 @@ int ionic_lifs_alloc(struct ionic *ionic);
void ionic_lifs_free(struct ionic *ionic);
void ionic_lifs_deinit(struct ionic *ionic);
int ionic_lifs_init(struct ionic *ionic);
+int ionic_lifs_register(struct ionic *ionic);
+void ionic_lifs_unregister(struct ionic *ionic);
int ionic_lif_identify(struct ionic *ionic, u8 lif_type,
union lif_identity *lif_ident);
int ionic_lifs_size(struct ionic *ionic);
--
2.17.1
^ permalink raw reply related
* [PATCH v2 net-next 10/19] ionic: Add management of rx filters
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
Set up the infrastructure for managing Rx filters. We can't ask the
hardware for what filters it has, so we keep a local list of filters
that we've pushed into the HW.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
drivers/net/ethernet/pensando/ionic/Makefile | 4 +-
.../net/ethernet/pensando/ionic/ionic_lif.c | 6 +
.../net/ethernet/pensando/ionic/ionic_lif.h | 2 +
.../ethernet/pensando/ionic/ionic_rx_filter.c | 142 ++++++++++++++++++
.../ethernet/pensando/ionic/ionic_rx_filter.h | 34 +++++
5 files changed, 186 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c
create mode 100644 drivers/net/ethernet/pensando/ionic/ionic_rx_filter.h
diff --git a/drivers/net/ethernet/pensando/ionic/Makefile b/drivers/net/ethernet/pensando/ionic/Makefile
index 04d519d00be6..7d9cdc5f02a1 100644
--- a/drivers/net/ethernet/pensando/ionic/Makefile
+++ b/drivers/net/ethernet/pensando/ionic/Makefile
@@ -3,5 +3,5 @@
obj-$(CONFIG_IONIC) := ionic.o
-ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o ionic_debugfs.o \
- ionic_lif.o
+ionic-y := ionic_main.o ionic_bus_pci.o ionic_dev.o \
+ ionic_lif.o ionic_rx_filter.o ionic_debugfs.o
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index e32a92af22cf..8f7abcebb1a5 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -669,6 +669,8 @@ static void ionic_lif_deinit(struct lif *lif)
clear_bit(LIF_INITED, lif->state);
+ ionic_rx_filters_deinit(lif);
+
napi_disable(&lif->adminqcq->napi);
ionic_lif_qcq_deinit(lif, lif->notifyqcq);
ionic_lif_qcq_deinit(lif, lif->adminqcq);
@@ -1035,6 +1037,10 @@ static int ionic_lif_init(struct lif *lif)
if (err)
goto err_out_notifyq_deinit;
+ err = ionic_rx_filters_init(lif);
+ if (err)
+ goto err_out_notifyq_deinit;
+
set_bit(LIF_INITED, lif->state);
return 0;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index ef3f7340a277..9f112aa69033 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -5,6 +5,7 @@
#define _IONIC_LIF_H_
#include <linux/pci.h>
+#include "ionic_rx_filter.h"
#define IONIC_ADMINQ_LENGTH 16 /* must be a power of two */
#define IONIC_NOTIFYQ_LENGTH 64 /* must be a power of two */
@@ -92,6 +93,7 @@ struct lif {
dma_addr_t info_pa;
u32 info_sz;
+ struct rx_filters rx_filters;
unsigned long *dbid_inuse;
unsigned int dbid_count;
struct dentry *dentry;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c
new file mode 100644
index 000000000000..621e0159aba8
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include "ionic.h"
+#include "ionic_lif.h"
+#include "ionic_rx_filter.h"
+
+void ionic_rx_filter_free(struct lif *lif, struct rx_filter *f)
+{
+ struct device *dev = lif->ionic->dev;
+
+ hlist_del(&f->by_id);
+ hlist_del(&f->by_hash);
+ devm_kfree(dev, f);
+}
+
+int ionic_rx_filter_del(struct lif *lif, struct rx_filter *f)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_filter_del = {
+ .opcode = CMD_OPCODE_RX_FILTER_DEL,
+ .filter_id = cpu_to_le32(f->filter_id),
+ },
+ };
+
+ return ionic_adminq_post_wait(lif, &ctx);
+}
+
+int ionic_rx_filters_init(struct lif *lif)
+{
+ unsigned int i;
+
+ spin_lock_init(&lif->rx_filters.lock);
+
+ for (i = 0; i < RX_FILTER_HLISTS; i++) {
+ INIT_HLIST_HEAD(&lif->rx_filters.by_hash[i]);
+ INIT_HLIST_HEAD(&lif->rx_filters.by_id[i]);
+ }
+
+ return 0;
+}
+
+void ionic_rx_filters_deinit(struct lif *lif)
+{
+ struct hlist_head *head;
+ struct hlist_node *tmp;
+ struct rx_filter *f;
+ unsigned int i;
+
+ for (i = 0; i < RX_FILTER_HLISTS; i++) {
+ head = &lif->rx_filters.by_id[i];
+ hlist_for_each_entry_safe(f, tmp, head, by_id)
+ ionic_rx_filter_free(lif, f);
+ }
+}
+
+int ionic_rx_filter_save(struct lif *lif, u32 flow_id, u16 rxq_index,
+ u32 hash, struct ionic_admin_ctx *ctx)
+{
+ struct device *dev = lif->ionic->dev;
+ struct rx_filter_add_cmd *ac;
+ struct hlist_head *head;
+ struct rx_filter *f;
+ unsigned int key;
+
+ ac = (struct rx_filter_add_cmd *)&ctx->cmd;
+
+ switch (le16_to_cpu(ac->match)) {
+ case RX_FILTER_MATCH_VLAN:
+ key = le16_to_cpu(ac->vlan.vlan) & RX_FILTER_HLISTS_MASK;
+ break;
+ case RX_FILTER_MATCH_MAC:
+ key = *(u32 *)ac->mac.addr & RX_FILTER_HLISTS_MASK;
+ break;
+ case RX_FILTER_MATCH_MAC_VLAN:
+ key = le16_to_cpu(ac->mac_vlan.vlan) & RX_FILTER_HLISTS_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f = devm_kzalloc(dev, sizeof(*f), GFP_KERNEL);
+ if (!f)
+ return -ENOMEM;
+
+ f->flow_id = flow_id;
+ f->filter_id = le32_to_cpu(ctx->comp.rx_filter_add.filter_id);
+ f->rxq_index = rxq_index;
+ memcpy(&f->cmd, ac, sizeof(f->cmd));
+
+ INIT_HLIST_NODE(&f->by_hash);
+ INIT_HLIST_NODE(&f->by_id);
+
+ spin_lock_bh(&lif->rx_filters.lock);
+
+ head = &lif->rx_filters.by_hash[key];
+ hlist_add_head(&f->by_hash, head);
+
+ key = f->filter_id & RX_FILTER_HLISTS_MASK;
+ head = &lif->rx_filters.by_id[key];
+ hlist_add_head(&f->by_id, head);
+
+ spin_unlock_bh(&lif->rx_filters.lock);
+
+ return 0;
+}
+
+struct rx_filter *ionic_rx_filter_by_vlan(struct lif *lif, u16 vid)
+{
+ unsigned int key = vid & RX_FILTER_HLISTS_MASK;
+ struct hlist_head *head = &lif->rx_filters.by_hash[key];
+ struct rx_filter *f;
+
+ hlist_for_each_entry(f, head, by_hash) {
+ if (le16_to_cpu(f->cmd.match) != RX_FILTER_MATCH_VLAN)
+ continue;
+ if (le16_to_cpu(f->cmd.vlan.vlan) == vid)
+ return f;
+ }
+
+ return NULL;
+}
+
+struct rx_filter *ionic_rx_filter_by_addr(struct lif *lif, const u8 *addr)
+{
+ unsigned int key = *(u32 *)addr & RX_FILTER_HLISTS_MASK;
+ struct hlist_head *head = &lif->rx_filters.by_hash[key];
+ struct rx_filter *f;
+
+ hlist_for_each_entry(f, head, by_hash) {
+ if (le16_to_cpu(f->cmd.match) != RX_FILTER_MATCH_MAC)
+ continue;
+ if (memcmp(addr, f->cmd.mac.addr, ETH_ALEN) == 0)
+ return f;
+ }
+
+ return NULL;
+}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.h b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.h
new file mode 100644
index 000000000000..c8688124465a
--- /dev/null
+++ b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
+
+#ifndef _IONIC_RX_FILTER_H_
+#define _IONIC_RX_FILTER_H_
+
+#define RXQ_INDEX_ANY (0xFFFF)
+struct rx_filter {
+ u32 flow_id;
+ u32 filter_id;
+ u16 rxq_index;
+ struct rx_filter_add_cmd cmd;
+ struct hlist_node by_hash;
+ struct hlist_node by_id;
+};
+
+#define RX_FILTER_HLISTS BIT(10)
+#define RX_FILTER_HLISTS_MASK (RX_FILTER_HLISTS - 1)
+struct rx_filters {
+ spinlock_t lock; /* filter list lock */
+ struct hlist_head by_hash[RX_FILTER_HLISTS]; /* by skb hash */
+ struct hlist_head by_id[RX_FILTER_HLISTS]; /* by filter_id */
+};
+
+void ionic_rx_filter_free(struct lif *lif, struct rx_filter *f);
+int ionic_rx_filter_del(struct lif *lif, struct rx_filter *f);
+int ionic_rx_filters_init(struct lif *lif);
+void ionic_rx_filters_deinit(struct lif *lif);
+int ionic_rx_filter_save(struct lif *lif, u32 flow_id, u16 rxq_index,
+ u32 hash, struct ionic_admin_ctx *ctx);
+struct rx_filter *ionic_rx_filter_by_vlan(struct lif *lif, u16 vid);
+struct rx_filter *ionic_rx_filter_by_addr(struct lif *lif, const u8 *addr);
+
+#endif /* _IONIC_RX_FILTER_H_ */
--
2.17.1
^ permalink raw reply related
* [PATCH v2 net-next 11/19] ionic: Add Rx filter and rx_mode ndo support
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
Add the Rx filtering and rx_mode NDO callbacks. Also add
the deferred work thread handling needed to manage the filter
requests otuside of the netif_addr_lock spinlock.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
.../net/ethernet/pensando/ionic/ionic_lif.c | 389 +++++++++++++++++-
.../net/ethernet/pensando/ionic/ionic_lif.h | 29 ++
2 files changed, 412 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 8f7abcebb1a5..dbe35249ab76 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -12,9 +12,55 @@
#include "ionic_lif.h"
#include "ionic_debugfs.h"
+static void ionic_lif_rx_mode(struct lif *lif, unsigned int rx_mode);
+static int ionic_lif_addr_add(struct lif *lif, const u8 *addr);
+static int ionic_lif_addr_del(struct lif *lif, const u8 *addr);
+
static int ionic_set_nic_features(struct lif *lif, netdev_features_t features);
static int ionic_notifyq_clean(struct lif *lif, int budget);
+static void ionic_lif_deferred_work(struct work_struct *work)
+{
+ struct lif *lif = container_of(work, struct lif, deferred.work);
+ struct ionic_deferred *def = &lif->deferred;
+ struct ionic_deferred_work *w = NULL;
+
+ spin_lock_bh(&def->lock);
+ if (!list_empty(&def->list)) {
+ w = list_first_entry(&def->list,
+ struct ionic_deferred_work, list);
+ list_del(&w->list);
+ }
+ spin_unlock_bh(&def->lock);
+
+ if (w) {
+ switch (w->type) {
+ case DW_TYPE_RX_MODE:
+ ionic_lif_rx_mode(lif, w->rx_mode);
+ break;
+ case DW_TYPE_RX_ADDR_ADD:
+ ionic_lif_addr_add(lif, w->addr);
+ break;
+ case DW_TYPE_RX_ADDR_DEL:
+ ionic_lif_addr_del(lif, w->addr);
+ break;
+ default:
+ break;
+ }
+ kfree(w);
+ schedule_work(&def->work);
+ }
+}
+
+static void ionic_lif_deferred_enqueue(struct ionic_deferred *def,
+ struct ionic_deferred_work *work)
+{
+ spin_lock_bh(&def->lock);
+ list_add_tail(&work->list, &def->list);
+ spin_unlock_bh(&def->lock);
+ schedule_work(&def->work);
+}
+
int ionic_open(struct net_device *netdev)
{
struct lif *lif = netdev_priv(netdev);
@@ -180,6 +226,235 @@ static int ionic_notifyq_clean(struct lif *lif, int budget)
return work_done;
}
+static int ionic_lif_addr_add(struct lif *lif, const u8 *addr)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_filter_add = {
+ .opcode = CMD_OPCODE_RX_FILTER_ADD,
+ .lif_index = cpu_to_le16(lif->index),
+ .match = cpu_to_le16(RX_FILTER_MATCH_MAC),
+ },
+ };
+ struct rx_filter *f;
+ int err;
+
+ /* don't bother if we already have it */
+ spin_lock_bh(&lif->rx_filters.lock);
+ f = ionic_rx_filter_by_addr(lif, addr);
+ spin_unlock_bh(&lif->rx_filters.lock);
+ if (f)
+ return 0;
+
+ netdev_dbg(lif->netdev, "rx_filter add ADDR %pM (id %d)\n", addr,
+ ctx.comp.rx_filter_add.filter_id);
+
+ memcpy(ctx.cmd.rx_filter_add.mac.addr, addr, ETH_ALEN);
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ return ionic_rx_filter_save(lif, 0, RXQ_INDEX_ANY, 0, &ctx);
+}
+
+static int ionic_lif_addr_del(struct lif *lif, const u8 *addr)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_filter_del = {
+ .opcode = CMD_OPCODE_RX_FILTER_DEL,
+ .lif_index = cpu_to_le16(lif->index),
+ },
+ };
+ struct rx_filter *f;
+ int err;
+
+ spin_lock_bh(&lif->rx_filters.lock);
+ f = ionic_rx_filter_by_addr(lif, addr);
+ if (!f) {
+ spin_unlock_bh(&lif->rx_filters.lock);
+ return -ENOENT;
+ }
+
+ ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
+ ionic_rx_filter_free(lif, f);
+ spin_unlock_bh(&lif->rx_filters.lock);
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ netdev_dbg(lif->netdev, "rx_filter del ADDR %pM (id %d)\n", addr,
+ ctx.cmd.rx_filter_del.filter_id);
+
+ return 0;
+}
+
+static int ionic_lif_addr(struct lif *lif, const u8 *addr, bool add)
+{
+ struct ionic *ionic = lif->ionic;
+ struct ionic_deferred_work *work;
+ unsigned int nmfilters;
+ unsigned int nufilters;
+
+ if (add) {
+ /* Do we have space for this filter? We test the counters
+ * here before checking the need for deferral so that we
+ * can return an overflow error to the stack.
+ */
+ nmfilters = le32_to_cpu(ionic->ident.lif.eth.max_mcast_filters);
+ nufilters = le32_to_cpu(ionic->ident.lif.eth.max_ucast_filters);
+
+ if ((is_multicast_ether_addr(addr) && lif->nmcast < nmfilters))
+ lif->nmcast++;
+ else if (!is_multicast_ether_addr(addr) &&
+ lif->nucast < nufilters)
+ lif->nucast++;
+ else
+ return -ENOSPC;
+ } else {
+ if (is_multicast_ether_addr(addr) && lif->nmcast)
+ lif->nmcast--;
+ else if (!is_multicast_ether_addr(addr) && lif->nucast)
+ lif->nucast--;
+ }
+
+ if (in_interrupt()) {
+ work = kzalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work) {
+ netdev_err(lif->netdev, "%s OOM\n", __func__);
+ return -ENOMEM;
+ }
+ work->type = add ? DW_TYPE_RX_ADDR_ADD : DW_TYPE_RX_ADDR_DEL;
+ memcpy(work->addr, addr, ETH_ALEN);
+ netdev_dbg(lif->netdev, "deferred: rx_filter %s %pM\n",
+ add ? "add" : "del", addr);
+ ionic_lif_deferred_enqueue(&lif->deferred, work);
+ } else {
+ netdev_dbg(lif->netdev, "rx_filter %s %pM\n",
+ add ? "add" : "del", addr);
+ if (add)
+ return ionic_lif_addr_add(lif, addr);
+ else
+ return ionic_lif_addr_del(lif, addr);
+ }
+
+ return 0;
+}
+
+static int ionic_addr_add(struct net_device *netdev, const u8 *addr)
+{
+ return ionic_lif_addr(netdev_priv(netdev), addr, true);
+}
+
+static int ionic_addr_del(struct net_device *netdev, const u8 *addr)
+{
+ return ionic_lif_addr(netdev_priv(netdev), addr, false);
+}
+
+static void ionic_lif_rx_mode(struct lif *lif, unsigned int rx_mode)
+{
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_mode_set = {
+ .opcode = CMD_OPCODE_RX_MODE_SET,
+ .lif_index = cpu_to_le16(lif->index),
+ .rx_mode = cpu_to_le16(rx_mode),
+ },
+ };
+ char buf[128];
+ int err;
+ int i;
+#define REMAIN(__x) (sizeof(buf) - (__x))
+
+ i = snprintf(buf, sizeof(buf), "rx_mode 0x%04x -> 0x%04x:",
+ lif->rx_mode, rx_mode);
+ if (rx_mode & RX_MODE_F_UNICAST)
+ i += snprintf(&buf[i], REMAIN(i), " RX_MODE_F_UNICAST");
+ if (rx_mode & RX_MODE_F_MULTICAST)
+ i += snprintf(&buf[i], REMAIN(i), " RX_MODE_F_MULTICAST");
+ if (rx_mode & RX_MODE_F_BROADCAST)
+ i += snprintf(&buf[i], REMAIN(i), " RX_MODE_F_BROADCAST");
+ if (rx_mode & RX_MODE_F_PROMISC)
+ i += snprintf(&buf[i], REMAIN(i), " RX_MODE_F_PROMISC");
+ if (rx_mode & RX_MODE_F_ALLMULTI)
+ i += snprintf(&buf[i], REMAIN(i), " RX_MODE_F_ALLMULTI");
+ netdev_dbg(lif->netdev, "lif%d %s\n", lif->index, buf);
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ netdev_warn(lif->netdev, "set rx_mode 0x%04x failed: %d\n",
+ rx_mode, err);
+ else
+ lif->rx_mode = rx_mode;
+}
+
+static void _ionic_lif_rx_mode(struct lif *lif, unsigned int rx_mode)
+{
+ struct ionic_deferred_work *work;
+
+ if (in_interrupt()) {
+ work = kzalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work) {
+ netdev_err(lif->netdev, "%s OOM\n", __func__);
+ return;
+ }
+ work->type = DW_TYPE_RX_MODE;
+ work->rx_mode = rx_mode;
+ netdev_dbg(lif->netdev, "deferred: rx_mode\n");
+ ionic_lif_deferred_enqueue(&lif->deferred, work);
+ } else {
+ ionic_lif_rx_mode(lif, rx_mode);
+ }
+}
+
+static void ionic_set_rx_mode(struct net_device *netdev)
+{
+ struct lif *lif = netdev_priv(netdev);
+ struct identity *ident = &lif->ionic->ident;
+ unsigned int nfilters;
+ unsigned int rx_mode;
+
+ rx_mode = RX_MODE_F_UNICAST;
+ rx_mode |= (netdev->flags & IFF_MULTICAST) ? RX_MODE_F_MULTICAST : 0;
+ rx_mode |= (netdev->flags & IFF_BROADCAST) ? RX_MODE_F_BROADCAST : 0;
+ rx_mode |= (netdev->flags & IFF_PROMISC) ? RX_MODE_F_PROMISC : 0;
+ rx_mode |= (netdev->flags & IFF_ALLMULTI) ? RX_MODE_F_ALLMULTI : 0;
+
+ /* sync unicast addresses
+ * next check to see if we're in an overflow state
+ * if so, we track that we overflowed and enable NIC PROMISC
+ * else if the overflow is set and not needed
+ * we remove our overflow flag and check the netdev flags
+ * to see if we can disable NIC PROMISC
+ */
+ __dev_uc_sync(netdev, ionic_addr_add, ionic_addr_del);
+ nfilters = le32_to_cpu(ident->lif.eth.max_ucast_filters);
+ if (netdev_uc_count(netdev) + 1 > nfilters) {
+ rx_mode |= RX_MODE_F_PROMISC;
+ lif->uc_overflow = true;
+ } else if (lif->uc_overflow) {
+ lif->uc_overflow = false;
+ if (!(netdev->flags & IFF_PROMISC))
+ rx_mode &= ~RX_MODE_F_PROMISC;
+ }
+
+ /* same for multicast */
+ __dev_mc_sync(netdev, ionic_addr_add, ionic_addr_del);
+ nfilters = le32_to_cpu(ident->lif.eth.max_mcast_filters);
+ if (netdev_mc_count(netdev) > nfilters) {
+ rx_mode |= RX_MODE_F_ALLMULTI;
+ lif->mc_overflow = true;
+ } else if (lif->mc_overflow) {
+ lif->mc_overflow = false;
+ if (!(netdev->flags & IFF_ALLMULTI))
+ rx_mode &= ~RX_MODE_F_ALLMULTI;
+ }
+
+ if (lif->rx_mode != rx_mode)
+ _ionic_lif_rx_mode(lif, rx_mode);
+}
+
static int ionic_set_features(struct net_device *netdev,
netdev_features_t features)
{
@@ -196,8 +471,26 @@ static int ionic_set_features(struct net_device *netdev,
static int ionic_set_mac_address(struct net_device *netdev, void *sa)
{
- netdev_info(netdev, "%s: stubbed\n", __func__);
- return 0;
+ struct sockaddr *addr = sa;
+ u8 *mac;
+
+ mac = (u8 *)addr->sa_data;
+ if (ether_addr_equal(netdev->dev_addr, mac))
+ return 0;
+
+ if (!is_valid_ether_addr(mac))
+ return -EADDRNOTAVAIL;
+
+ if (!is_zero_ether_addr(netdev->dev_addr)) {
+ netdev_info(netdev, "deleting mac addr %pM\n",
+ netdev->dev_addr);
+ ionic_addr_del(netdev, netdev->dev_addr);
+ }
+
+ memcpy(netdev->dev_addr, mac, netdev->addr_len);
+ netdev_info(netdev, "updating mac addr %pM\n", mac);
+
+ return ionic_addr_add(netdev, mac);
}
static int ionic_change_mtu(struct net_device *netdev, int new_mtu)
@@ -232,20 +525,63 @@ static void ionic_tx_timeout(struct net_device *netdev)
static int ionic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
u16 vid)
{
- netdev_info(netdev, "%s: stubbed\n", __func__);
- return 0;
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_filter_add = {
+ .opcode = CMD_OPCODE_RX_FILTER_ADD,
+ .lif_index = cpu_to_le16(lif->index),
+ .match = cpu_to_le16(RX_FILTER_MATCH_VLAN),
+ .vlan.vlan = cpu_to_le16(vid),
+ },
+ };
+ int err;
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ netdev_dbg(netdev, "rx_filter add VLAN %d (id %d)\n", vid,
+ ctx.comp.rx_filter_add.filter_id);
+
+ return ionic_rx_filter_save(lif, 0, RXQ_INDEX_ANY, 0, &ctx);
}
static int ionic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto,
u16 vid)
{
- netdev_info(netdev, "%s: stubbed\n", __func__);
- return 0;
+ struct lif *lif = netdev_priv(netdev);
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.rx_filter_del = {
+ .opcode = CMD_OPCODE_RX_FILTER_DEL,
+ .lif_index = cpu_to_le16(lif->index),
+ },
+ };
+ struct rx_filter *f;
+
+ spin_lock_bh(&lif->rx_filters.lock);
+
+ f = ionic_rx_filter_by_vlan(lif, vid);
+ if (!f) {
+ spin_unlock_bh(&lif->rx_filters.lock);
+ return -ENOENT;
+ }
+
+ netdev_dbg(netdev, "rx_filter del VLAN %d (id %d)\n", vid,
+ le32_to_cpu(ctx.cmd.rx_filter_del.filter_id));
+
+ ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
+ ionic_rx_filter_free(lif, f);
+ spin_unlock_bh(&lif->rx_filters.lock);
+
+ return ionic_adminq_post_wait(lif, &ctx);
}
static const struct net_device_ops ionic_netdev_ops = {
.ndo_open = ionic_open,
.ndo_stop = ionic_stop,
+ .ndo_set_rx_mode = ionic_set_rx_mode,
.ndo_set_features = ionic_set_features,
.ndo_set_mac_address = ionic_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
@@ -546,6 +882,10 @@ static struct lif *ionic_lif_alloc(struct ionic *ionic, unsigned int index)
spin_lock_init(&lif->adminq_lock);
+ spin_lock_init(&lif->deferred.lock);
+ INIT_LIST_HEAD(&lif->deferred.list);
+ INIT_WORK(&lif->deferred.work, ionic_lif_deferred_work);
+
/* allocate lif info */
lif->info_sz = ALIGN(sizeof(*lif->info), PAGE_SIZE);
lif->info = dma_alloc_coherent(dev, lif->info_sz,
@@ -607,6 +947,8 @@ static void ionic_lif_free(struct lif *lif)
ionic_qcqs_free(lif);
ionic_lif_reset(lif);
+ cancel_work_sync(&lif->deferred.work);
+
/* free lif info */
if (lif->info) {
dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa);
@@ -977,6 +1319,37 @@ static int ionic_init_nic_features(struct lif *lif)
return 0;
}
+static int ionic_station_set(struct lif *lif)
+{
+ struct net_device *netdev = lif->netdev;
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.lif_getattr = {
+ .opcode = CMD_OPCODE_LIF_GETATTR,
+ .index = cpu_to_le16(lif->index),
+ .attr = IONIC_LIF_ATTR_MAC,
+ },
+ };
+ int err;
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ if (!is_zero_ether_addr(netdev->dev_addr)) {
+ netdev_dbg(lif->netdev, "deleting station MAC addr %pM\n",
+ netdev->dev_addr);
+ ionic_lif_addr(lif, netdev->dev_addr, false);
+ }
+ memcpy(netdev->dev_addr, ctx.comp.lif_getattr.mac,
+ netdev->addr_len);
+ netdev_dbg(lif->netdev, "adding station MAC addr %pM\n",
+ netdev->dev_addr);
+ ionic_lif_addr(lif, netdev->dev_addr, true);
+
+ return 0;
+}
+
static int ionic_lif_init(struct lif *lif)
{
struct ionic_dev *idev = &lif->ionic->idev;
@@ -1041,6 +1414,10 @@ static int ionic_lif_init(struct lif *lif)
if (err)
goto err_out_notifyq_deinit;
+ err = ionic_station_set(lif);
+ if (err)
+ goto err_out_notifyq_deinit;
+
set_bit(LIF_INITED, lif->state);
return 0;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 9f112aa69033..20b4fa573f77 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -60,6 +60,29 @@ struct qcq {
#define napi_to_qcq(napi) container_of(napi, struct qcq, napi)
#define napi_to_cq(napi) (&napi_to_qcq(napi)->cq)
+enum ionic_deferred_work_type {
+ DW_TYPE_RX_MODE,
+ DW_TYPE_RX_ADDR_ADD,
+ DW_TYPE_RX_ADDR_DEL,
+ DW_TYPE_LINK_STATUS,
+ DW_TYPE_LIF_RESET,
+};
+
+struct ionic_deferred_work {
+ struct list_head list;
+ enum ionic_deferred_work_type type;
+ union {
+ unsigned int rx_mode;
+ u8 addr[ETH_ALEN];
+ };
+};
+
+struct ionic_deferred {
+ spinlock_t lock; /* lock for deferred work list */
+ struct list_head list;
+ struct work_struct work;
+};
+
enum lif_state_flags {
LIF_INITED,
LIF_UP,
@@ -87,13 +110,19 @@ struct lif {
u64 last_eid;
unsigned int neqs;
unsigned int nxqs;
+ unsigned int rx_mode;
u64 hw_features;
+ bool mc_overflow;
+ unsigned int nmcast;
+ bool uc_overflow;
+ unsigned int nucast;
struct lif_info *info;
dma_addr_t info_pa;
u32 info_sz;
struct rx_filters rx_filters;
+ struct ionic_deferred deferred;
unsigned long *dbid_inuse;
unsigned int dbid_count;
struct dentry *dentry;
--
2.17.1
^ permalink raw reply related
* [PATCH v2 net-next 08/19] ionic: Add notifyq support
From: Shannon Nelson @ 2019-06-28 21:39 UTC (permalink / raw)
To: snelson, netdev
In-Reply-To: <20190628213934.8810-1-snelson@pensando.io>
The AdminQ is fine for sending messages and requests to the NIC,
but we also need to have events published from the NIC to the
driver. The NotifyQ handles this for us, using the same interrupt
as AdminQ.
Signed-off-by: Shannon Nelson <snelson@pensando.io>
---
.../ethernet/pensando/ionic/ionic_debugfs.c | 16 ++
.../net/ethernet/pensando/ionic/ionic_lif.c | 181 +++++++++++++++++-
.../net/ethernet/pensando/ionic/ionic_lif.h | 4 +
3 files changed, 200 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
index 9af15c69b2a6..1d05b23de303 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
@@ -126,6 +126,7 @@ int ionic_debugfs_add_qcq(struct lif *lif, struct qcq *qcq)
struct debugfs_blob_wrapper *desc_blob;
struct device *dev = lif->ionic->dev;
struct intr *intr = &qcq->intr;
+ struct dentry *stats_dentry;
struct queue *q = &qcq->q;
struct cq *cq = &qcq->cq;
@@ -219,6 +220,21 @@ int ionic_debugfs_add_qcq(struct lif *lif, struct qcq *qcq)
intr_ctrl_regset);
}
+ if (qcq->flags & QCQ_F_NOTIFYQ) {
+ stats_dentry = debugfs_create_dir("notifyblock", qcq_dentry);
+ if (IS_ERR_OR_NULL(stats_dentry))
+ return PTR_ERR(stats_dentry);
+
+ debugfs_create_u64("eid", 0400, stats_dentry,
+ (u64 *)&lif->info->status.eid);
+ debugfs_create_u16("link_status", 0400, stats_dentry,
+ (u16 *)&lif->info->status.link_status);
+ debugfs_create_u32("link_speed", 0400, stats_dentry,
+ (u32 *)&lif->info->status.link_speed);
+ debugfs_create_u16("link_down_count", 0400, stats_dentry,
+ (u16 *)&lif->info->status.link_down_count);
+ }
+
return 0;
}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index e1596cfcc491..b59cf5e739fc 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -12,6 +12,8 @@
#include "ionic_lif.h"
#include "ionic_debugfs.h"
+static int ionic_notifyq_clean(struct lif *lif, int budget);
+
static bool ionic_adminq_service(struct cq *cq, struct cq_info *cq_info)
{
struct admin_comp *comp = cq_info->cq_desc;
@@ -26,7 +28,90 @@ static bool ionic_adminq_service(struct cq *cq, struct cq_info *cq_info)
static int ionic_adminq_napi(struct napi_struct *napi, int budget)
{
- return ionic_napi(napi, budget, ionic_adminq_service, NULL, NULL);
+ struct lif *lif = napi_to_cq(napi)->lif;
+ int n_work = 0;
+ int a_work = 0;
+
+ if (likely(lif->notifyqcq && lif->notifyqcq->flags & QCQ_F_INITED))
+ n_work = ionic_notifyq_clean(lif, budget);
+ a_work = ionic_napi(napi, budget, ionic_adminq_service, NULL, NULL);
+
+ return max(n_work, a_work);
+}
+
+static bool ionic_notifyq_service(struct cq *cq, struct cq_info *cq_info)
+{
+ union notifyq_comp *comp = cq_info->cq_desc;
+ struct net_device *netdev;
+ struct queue *q;
+ struct lif *lif;
+ u64 eid;
+
+ q = cq->bound_q;
+ lif = q->info[0].cb_arg;
+ netdev = lif->netdev;
+ eid = le64_to_cpu(comp->event.eid);
+
+ /* Have we run out of new completions to process? */
+ if (eid <= lif->last_eid)
+ return false;
+
+ lif->last_eid = eid;
+
+ dev_dbg(lif->ionic->dev, "notifyq event:\n");
+ dynamic_hex_dump("event ", DUMP_PREFIX_OFFSET, 16, 1,
+ comp, sizeof(*comp), true);
+
+ switch (le16_to_cpu(comp->event.ecode)) {
+ case EVENT_OPCODE_LINK_CHANGE:
+ netdev_info(netdev, "Notifyq EVENT_OPCODE_LINK_CHANGE eid=%lld\n",
+ eid);
+ netdev_info(netdev,
+ " link_status=%d link_speed=%d\n",
+ le16_to_cpu(comp->link_change.link_status),
+ le32_to_cpu(comp->link_change.link_speed));
+ break;
+ case EVENT_OPCODE_RESET:
+ netdev_info(netdev, "Notifyq EVENT_OPCODE_RESET eid=%lld\n",
+ eid);
+ netdev_info(netdev, " reset_code=%d state=%d\n",
+ comp->reset.reset_code,
+ comp->reset.state);
+ break;
+ case EVENT_OPCODE_LOG:
+ netdev_info(netdev, "Notifyq EVENT_OPCODE_LOG eid=%lld\n", eid);
+ print_hex_dump(KERN_INFO, "notifyq ", DUMP_PREFIX_OFFSET, 16, 1,
+ comp->log.data, sizeof(comp->log.data), true);
+ break;
+ default:
+ netdev_warn(netdev, "Notifyq unknown event ecode=%d eid=%lld\n",
+ comp->event.ecode, eid);
+ break;
+ }
+
+ return true;
+}
+
+static int ionic_notifyq_clean(struct lif *lif, int budget)
+{
+ struct ionic_dev *idev = &lif->ionic->idev;
+ struct cq *cq = &lif->notifyqcq->cq;
+ u32 work_done;
+
+ work_done = ionic_cq_service(cq, budget, ionic_notifyq_service,
+ NULL, NULL);
+ if (work_done)
+ ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index,
+ work_done, IONIC_INTR_CRED_RESET_COALESCE);
+
+ /* If we ran out of budget, there are more events
+ * to process and napi will reschedule us soon
+ */
+ if (work_done == budget)
+ goto return_to_napi;
+
+return_to_napi:
+ return work_done;
}
static irqreturn_t ionic_isr(int irq, void *data)
@@ -62,6 +147,17 @@ static void ionic_intr_free(struct lif *lif, int index)
clear_bit(index, lif->ionic->intrs);
}
+static void ionic_link_qcq_interrupts(struct qcq *src_qcq, struct qcq *n_qcq)
+{
+ if (WARN_ON(n_qcq->flags & QCQ_F_INTR)) {
+ ionic_intr_free(n_qcq->cq.lif, n_qcq->intr.index);
+ n_qcq->flags &= ~QCQ_F_INTR;
+ }
+
+ n_qcq->intr.vector = src_qcq->intr.vector;
+ n_qcq->intr.index = src_qcq->intr.index;
+}
+
static int ionic_qcq_alloc(struct lif *lif, unsigned int type,
unsigned int index,
const char *name, unsigned int flags,
@@ -236,11 +332,36 @@ static int ionic_qcqs_alloc(struct lif *lif)
if (err)
return err;
+ if (lif->ionic->nnqs_per_lif) {
+ flags = QCQ_F_NOTIFYQ;
+ err = ionic_qcq_alloc(lif, IONIC_QTYPE_NOTIFYQ, 0, "notifyq",
+ flags, IONIC_NOTIFYQ_LENGTH,
+ sizeof(struct notifyq_cmd),
+ sizeof(union notifyq_comp),
+ 0, lif->kern_pid, &lif->notifyqcq);
+ if (err)
+ goto err_out_free_adminqcq;
+
+ /* Let the notifyq ride on the adminq interrupt */
+ ionic_link_qcq_interrupts(lif->adminqcq, lif->notifyqcq);
+ }
+
return 0;
+
+err_out_free_adminqcq:
+ ionic_qcq_free(lif, lif->adminqcq);
+ lif->adminqcq = NULL;
+
+ return err;
}
static void ionic_qcqs_free(struct lif *lif)
{
+ if (lif->notifyqcq) {
+ ionic_qcq_free(lif, lif->notifyqcq);
+ lif->notifyqcq = NULL;
+ }
+
if (lif->adminqcq) {
ionic_qcq_free(lif, lif->adminqcq);
lif->adminqcq = NULL;
@@ -402,6 +523,7 @@ static void ionic_lif_deinit(struct lif *lif)
clear_bit(LIF_INITED, lif->state);
napi_disable(&lif->adminqcq->napi);
+ ionic_lif_qcq_deinit(lif, lif->notifyqcq);
ionic_lif_qcq_deinit(lif, lif->adminqcq);
ionic_lif_reset(lif);
@@ -486,6 +608,55 @@ static int ionic_lif_adminq_init(struct lif *lif)
return 0;
}
+static int ionic_lif_notifyq_init(struct lif *lif)
+{
+ struct device *dev = lif->ionic->dev;
+ struct qcq *qcq = lif->notifyqcq;
+ struct queue *q = &qcq->q;
+ int err;
+
+ struct ionic_admin_ctx ctx = {
+ .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+ .cmd.q_init = {
+ .opcode = CMD_OPCODE_Q_INIT,
+ .lif_index = cpu_to_le16(lif->index),
+ .type = q->type,
+ .index = cpu_to_le32(q->index),
+ .flags = cpu_to_le16(IONIC_QINIT_F_IRQ |
+ IONIC_QINIT_F_ENA),
+ .intr_index = cpu_to_le16(lif->adminqcq->intr.index),
+ .pid = cpu_to_le16(q->pid),
+ .ring_size = ilog2(q->num_descs),
+ .ring_base = cpu_to_le64(q->base_pa),
+ }
+ };
+
+ dev_dbg(dev, "notifyq_init.pid %d\n", ctx.cmd.q_init.pid);
+ dev_dbg(dev, "notifyq_init.index %d\n", ctx.cmd.q_init.index);
+ dev_dbg(dev, "notifyq_init.ring_base 0x%llx\n", ctx.cmd.q_init.ring_base);
+ dev_dbg(dev, "notifyq_init.ring_size %d\n", ctx.cmd.q_init.ring_size);
+
+ err = ionic_adminq_post_wait(lif, &ctx);
+ if (err)
+ return err;
+
+ q->hw_type = ctx.comp.q_init.hw_type;
+ q->hw_index = le32_to_cpu(ctx.comp.q_init.hw_index);
+ q->dbval = IONIC_DBELL_QID(q->hw_index);
+
+ dev_dbg(dev, "notifyq->hw_type %d\n", q->hw_type);
+ dev_dbg(dev, "notifyq->hw_index %d\n", q->hw_index);
+
+ /* preset the callback info */
+ q->info[0].cb_arg = lif;
+
+ qcq->flags |= QCQ_F_INITED;
+
+ ionic_debugfs_add_qcq(lif, qcq);
+
+ return 0;
+}
+
static int ionic_lif_init(struct lif *lif)
{
struct ionic_dev *idev = &lif->ionic->idev;
@@ -536,10 +707,18 @@ static int ionic_lif_init(struct lif *lif)
if (err)
goto err_out_adminq_deinit;
+ if (lif->ionic->nnqs_per_lif) {
+ err = ionic_lif_notifyq_init(lif);
+ if (err)
+ goto err_out_notifyq_deinit;
+ }
+
set_bit(LIF_INITED, lif->state);
return 0;
+err_out_notifyq_deinit:
+ ionic_lif_qcq_deinit(lif, lif->notifyqcq);
err_out_adminq_deinit:
ionic_lif_qcq_deinit(lif, lif->adminqcq);
ionic_lif_reset(lif);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 28ab92b43a64..80eec0778f40 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -7,6 +7,7 @@
#include <linux/pci.h>
#define IONIC_ADMINQ_LENGTH 16 /* must be a power of two */
+#define IONIC_NOTIFYQ_LENGTH 64 /* must be a power of two */
#define GET_NAPI_CNTR_IDX(work_done) (work_done)
#define MAX_NUM_NAPI_CNTR (NAPI_POLL_WEIGHT + 1)
@@ -26,6 +27,7 @@ struct rx_stats {
#define QCQ_F_INITED BIT(0)
#define QCQ_F_SG BIT(1)
#define QCQ_F_INTR BIT(2)
+#define QCQ_F_NOTIFYQ BIT(5)
struct napi_stats {
u64 poll_count;
@@ -78,6 +80,8 @@ struct lif {
u64 __iomem *kern_dbpage;
spinlock_t adminq_lock; /* lock for AdminQ operations */
struct qcq *adminqcq;
+ struct qcq *notifyqcq;
+ u64 last_eid;
unsigned int neqs;
unsigned int nxqs;
--
2.17.1
^ permalink raw reply related
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