* [net-next 5/5] net/mlx5: Add Fast teardown support
From: Saeed Mahameed @ 2018-10-04 14:31 UTC (permalink / raw)
To: David S. Miller; +Cc: Jiri Pirko, netdev, Feras Daoud, Saeed Mahameed
In-Reply-To: <20181004143124.11607-1-saeedm@mellanox.com>
From: Feras Daoud <ferasda@mellanox.com>
Today mlx5 devices support two teardown modes:
1- Regular teardown
2- Force teardown
This change introduces the enhanced version of the "Force teardown" that
allows SW to perform teardown in a faster way without the need to reclaim
all the pages.
Fast teardown provides the following advantages:
1- Fix a FW race condition that could cause command timeout
2- Avoid moving to polling mode
3- Close the vport to prevent PCI ACK to be sent without been scatter
to memory
Signed-off-by: Feras Daoud <ferasda@mellanox.com>
Reviewed-by: Majd Dibbiny <majd@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
---
drivers/net/ethernet/mellanox/mlx5/core/fw.c | 50 ++++++++++++++++++-
.../net/ethernet/mellanox/mlx5/core/health.c | 25 ++++++----
.../net/ethernet/mellanox/mlx5/core/main.c | 29 +++++++----
.../ethernet/mellanox/mlx5/core/mlx5_core.h | 12 +++++
include/linux/mlx5/device.h | 4 ++
include/linux/mlx5/mlx5_ifc.h | 6 ++-
6 files changed, 103 insertions(+), 23 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
index 41ad24f0de2c..1ab6f7e3bec6 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
@@ -250,7 +250,7 @@ int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev)
if (ret)
return ret;
- force_state = MLX5_GET(teardown_hca_out, out, force_state);
+ force_state = MLX5_GET(teardown_hca_out, out, state);
if (force_state == MLX5_TEARDOWN_HCA_OUT_FORCE_STATE_FAIL) {
mlx5_core_warn(dev, "teardown with force mode failed, doing normal teardown\n");
return -EIO;
@@ -259,6 +259,54 @@ int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev)
return 0;
}
+#define MLX5_FAST_TEARDOWN_WAIT_MS 3000
+int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev)
+{
+ unsigned long end, delay_ms = MLX5_FAST_TEARDOWN_WAIT_MS;
+ u32 out[MLX5_ST_SZ_DW(teardown_hca_out)] = {0};
+ u32 in[MLX5_ST_SZ_DW(teardown_hca_in)] = {0};
+ int state;
+ int ret;
+
+ if (!MLX5_CAP_GEN(dev, fast_teardown)) {
+ mlx5_core_dbg(dev, "fast teardown is not supported in the firmware\n");
+ return -EOPNOTSUPP;
+ }
+
+ MLX5_SET(teardown_hca_in, in, opcode, MLX5_CMD_OP_TEARDOWN_HCA);
+ MLX5_SET(teardown_hca_in, in, profile,
+ MLX5_TEARDOWN_HCA_IN_PROFILE_PREPARE_FAST_TEARDOWN);
+
+ ret = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+ if (ret)
+ return ret;
+
+ state = MLX5_GET(teardown_hca_out, out, state);
+ if (state == MLX5_TEARDOWN_HCA_OUT_FORCE_STATE_FAIL) {
+ mlx5_core_warn(dev, "teardown with fast mode failed\n");
+ return -EIO;
+ }
+
+ mlx5_set_nic_state(dev, MLX5_NIC_IFC_DISABLED);
+
+ /* Loop until device state turns to disable */
+ end = jiffies + msecs_to_jiffies(delay_ms);
+ do {
+ if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
+ break;
+
+ cond_resched();
+ } while (!time_after(jiffies, end));
+
+ if (mlx5_get_nic_state(dev) != MLX5_NIC_IFC_DISABLED) {
+ dev_err(&dev->pdev->dev, "NIC IFC still %d after %lums.\n",
+ mlx5_get_nic_state(dev), delay_ms);
+ return -EIO;
+ }
+
+ return 0;
+}
+
enum mlxsw_reg_mcc_instruction {
MLX5_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE = 0x01,
MLX5_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE = 0x02,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c
index 9f39aeca863f..43118de8ee99 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/health.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c
@@ -58,23 +58,26 @@ enum {
MLX5_HEALTH_SYNDR_HIGH_TEMP = 0x10
};
-enum {
- MLX5_NIC_IFC_FULL = 0,
- MLX5_NIC_IFC_DISABLED = 1,
- MLX5_NIC_IFC_NO_DRAM_NIC = 2,
- MLX5_NIC_IFC_INVALID = 3
-};
-
enum {
MLX5_DROP_NEW_HEALTH_WORK,
MLX5_DROP_NEW_RECOVERY_WORK,
};
-static u8 get_nic_state(struct mlx5_core_dev *dev)
+u8 mlx5_get_nic_state(struct mlx5_core_dev *dev)
{
return (ioread32be(&dev->iseg->cmdq_addr_l_sz) >> 8) & 3;
}
+void mlx5_set_nic_state(struct mlx5_core_dev *dev, u8 state)
+{
+ u32 cur_cmdq_addr_l_sz;
+
+ cur_cmdq_addr_l_sz = ioread32be(&dev->iseg->cmdq_addr_l_sz);
+ iowrite32be((cur_cmdq_addr_l_sz & 0xFFFFF000) |
+ state << MLX5_NIC_IFC_OFFSET,
+ &dev->iseg->cmdq_addr_l_sz);
+}
+
static void trigger_cmd_completions(struct mlx5_core_dev *dev)
{
unsigned long flags;
@@ -103,7 +106,7 @@ static int in_fatal(struct mlx5_core_dev *dev)
struct mlx5_core_health *health = &dev->priv.health;
struct health_buffer __iomem *h = health->health;
- if (get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
+ if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
return 1;
if (ioread32be(&h->fw_ver) == 0xffffffff)
@@ -133,7 +136,7 @@ void mlx5_enter_error_state(struct mlx5_core_dev *dev, bool force)
static void mlx5_handle_bad_state(struct mlx5_core_dev *dev)
{
- u8 nic_interface = get_nic_state(dev);
+ u8 nic_interface = mlx5_get_nic_state(dev);
switch (nic_interface) {
case MLX5_NIC_IFC_FULL:
@@ -168,7 +171,7 @@ static void health_recover(struct work_struct *work)
priv = container_of(health, struct mlx5_priv, health);
dev = container_of(priv, struct mlx5_core_dev, priv);
- nic_state = get_nic_state(dev);
+ nic_state = mlx5_get_nic_state(dev);
if (nic_state == MLX5_NIC_IFC_INVALID) {
dev_err(&dev->pdev->dev, "health recovery flow aborted since the nic state is invalid\n");
return;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index b5e9f664fc66..28132c7dc05f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -1594,12 +1594,17 @@ static const struct pci_error_handlers mlx5_err_handler = {
static int mlx5_try_fast_unload(struct mlx5_core_dev *dev)
{
- int ret;
+ bool fast_teardown = false, force_teardown = false;
+ int ret = 1;
+
+ fast_teardown = MLX5_CAP_GEN(dev, fast_teardown);
+ force_teardown = MLX5_CAP_GEN(dev, force_teardown);
+
+ mlx5_core_dbg(dev, "force teardown firmware support=%d\n", force_teardown);
+ mlx5_core_dbg(dev, "fast teardown firmware support=%d\n", fast_teardown);
- if (!MLX5_CAP_GEN(dev, force_teardown)) {
- mlx5_core_dbg(dev, "force teardown is not supported in the firmware\n");
+ if (!fast_teardown && !force_teardown)
return -EOPNOTSUPP;
- }
if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
mlx5_core_dbg(dev, "Device in internal error state, giving up\n");
@@ -1612,13 +1617,19 @@ static int mlx5_try_fast_unload(struct mlx5_core_dev *dev)
mlx5_drain_health_wq(dev);
mlx5_stop_health_poll(dev, false);
+ ret = mlx5_cmd_fast_teardown_hca(dev);
+ if (!ret)
+ goto succeed;
+
ret = mlx5_cmd_force_teardown_hca(dev);
- if (ret) {
- mlx5_core_dbg(dev, "Firmware couldn't do fast unload error: %d\n", ret);
- mlx5_start_health_poll(dev);
- return ret;
- }
+ if (!ret)
+ goto succeed;
+
+ mlx5_core_dbg(dev, "Firmware couldn't do fast unload error: %d\n", ret);
+ mlx5_start_health_poll(dev);
+ return ret;
+succeed:
mlx5_enter_error_state(dev, true);
/* Some platforms requiring freeing the IRQ's in the shutdown
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
index b4134fa0bba3..cc298527baf1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
@@ -95,6 +95,8 @@ int mlx5_query_board_id(struct mlx5_core_dev *dev);
int mlx5_cmd_init_hca(struct mlx5_core_dev *dev, uint32_t *sw_owner_id);
int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev);
int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev);
+int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev);
+
void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
unsigned long param);
void mlx5_core_page_fault(struct mlx5_core_dev *dev,
@@ -214,4 +216,14 @@ int mlx5_lag_allow(struct mlx5_core_dev *dev);
int mlx5_lag_forbid(struct mlx5_core_dev *dev);
void mlx5_reload_interface(struct mlx5_core_dev *mdev, int protocol);
+
+enum {
+ MLX5_NIC_IFC_FULL = 0,
+ MLX5_NIC_IFC_DISABLED = 1,
+ MLX5_NIC_IFC_NO_DRAM_NIC = 2,
+ MLX5_NIC_IFC_INVALID = 3
+};
+
+u8 mlx5_get_nic_state(struct mlx5_core_dev *dev);
+void mlx5_set_nic_state(struct mlx5_core_dev *dev, u8 state);
#endif /* __MLX5_CORE_H__ */
diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h
index 11fa4e66afc5..e9b502d5bcc1 100644
--- a/include/linux/mlx5/device.h
+++ b/include/linux/mlx5/device.h
@@ -504,6 +504,10 @@ struct health_buffer {
__be16 ext_synd;
};
+enum mlx5_cmd_addr_l_sz_offset {
+ MLX5_NIC_IFC_OFFSET = 8,
+};
+
struct mlx5_init_seg {
__be32 fw_rev;
__be32 cmdif_rev_fw_sub;
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index f043d65b9bac..6e8a882052b1 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -896,7 +896,8 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 log_max_mkey[0x6];
u8 reserved_at_f0[0x8];
u8 dump_fill_mkey[0x1];
- u8 reserved_at_f9[0x3];
+ u8 reserved_at_f9[0x2];
+ u8 fast_teardown[0x1];
u8 log_max_eq[0x4];
u8 max_indirection[0x8];
@@ -3352,12 +3353,13 @@ struct mlx5_ifc_teardown_hca_out_bits {
u8 reserved_at_40[0x3f];
- u8 force_state[0x1];
+ u8 state[0x1];
};
enum {
MLX5_TEARDOWN_HCA_IN_PROFILE_GRACEFUL_CLOSE = 0x0,
MLX5_TEARDOWN_HCA_IN_PROFILE_FORCE_CLOSE = 0x1,
+ MLX5_TEARDOWN_HCA_IN_PROFILE_PREPARE_FAST_TEARDOWN = 0x2,
};
struct mlx5_ifc_teardown_hca_in_bits {
--
2.17.1
^ permalink raw reply related
* [net-next 3/5] net/mlx5e: Add extack messages for TC offload failures
From: Saeed Mahameed @ 2018-10-04 14:31 UTC (permalink / raw)
To: David S. Miller; +Cc: Jiri Pirko, netdev, Eli Britstein, Saeed Mahameed
In-Reply-To: <20181004143124.11607-1-saeedm@mellanox.com>
From: Eli Britstein <elibr@mellanox.com>
Return tc extack messages for failures to user space.
Messages provide reasons for not being able to offload rules to HW.
Signed-off-by: Eli Britstein <elibr@mellanox.com>
Reviewed-by: Or Gerlitz <ogerlitz@mellanox.com>
Reviewed-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
---
.../net/ethernet/mellanox/mlx5/core/en_tc.c | 156 +++++++++++++-----
1 file changed, 118 insertions(+), 38 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index 82723a0e509a..eeb2b215f5a4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -532,7 +532,8 @@ static struct mlx5e_hairpin_entry *mlx5e_hairpin_get(struct mlx5e_priv *priv,
#define UNKNOWN_MATCH_PRIO 8
static int mlx5e_hairpin_get_prio(struct mlx5e_priv *priv,
- struct mlx5_flow_spec *spec, u8 *match_prio)
+ struct mlx5_flow_spec *spec, u8 *match_prio,
+ struct netlink_ext_ack *extack)
{
void *headers_c, *headers_v;
u8 prio_val, prio_mask = 0;
@@ -540,8 +541,8 @@ static int mlx5e_hairpin_get_prio(struct mlx5e_priv *priv,
#ifdef CONFIG_MLX5_CORE_EN_DCB
if (priv->dcbx_dp.trust_state != MLX5_QPTS_TRUST_PCP) {
- netdev_warn(priv->netdev,
- "only PCP trust state supported for hairpin\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "only PCP trust state supported for hairpin");
return -EOPNOTSUPP;
}
#endif
@@ -557,8 +558,8 @@ static int mlx5e_hairpin_get_prio(struct mlx5e_priv *priv,
if (!vlan_present || !prio_mask) {
prio_val = UNKNOWN_MATCH_PRIO;
} else if (prio_mask != 0x7) {
- netdev_warn(priv->netdev,
- "masked priority match not supported for hairpin\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "masked priority match not supported for hairpin");
return -EOPNOTSUPP;
}
@@ -568,7 +569,8 @@ static int mlx5e_hairpin_get_prio(struct mlx5e_priv *priv,
static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv,
struct mlx5e_tc_flow *flow,
- struct mlx5e_tc_flow_parse_attr *parse_attr)
+ struct mlx5e_tc_flow_parse_attr *parse_attr,
+ struct netlink_ext_ack *extack)
{
int peer_ifindex = parse_attr->mirred_ifindex;
struct mlx5_hairpin_params params;
@@ -583,12 +585,13 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv,
peer_mdev = mlx5e_hairpin_get_mdev(dev_net(priv->netdev), peer_ifindex);
if (!MLX5_CAP_GEN(priv->mdev, hairpin) || !MLX5_CAP_GEN(peer_mdev, hairpin)) {
- netdev_warn(priv->netdev, "hairpin is not supported\n");
+ NL_SET_ERR_MSG_MOD(extack, "hairpin is not supported");
return -EOPNOTSUPP;
}
peer_id = MLX5_CAP_GEN(peer_mdev, vhca_id);
- err = mlx5e_hairpin_get_prio(priv, &parse_attr->spec, &match_prio);
+ err = mlx5e_hairpin_get_prio(priv, &parse_attr->spec, &match_prio,
+ extack);
if (err)
return err;
hpe = mlx5e_hairpin_get(priv, peer_id, match_prio);
@@ -677,7 +680,8 @@ static void mlx5e_hairpin_flow_del(struct mlx5e_priv *priv,
static struct mlx5_flow_handle *
mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
struct mlx5e_tc_flow_parse_attr *parse_attr,
- struct mlx5e_tc_flow *flow)
+ struct mlx5e_tc_flow *flow,
+ struct netlink_ext_ack *extack)
{
struct mlx5_nic_flow_attr *attr = flow->nic_attr;
struct mlx5_core_dev *dev = priv->mdev;
@@ -694,7 +698,7 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
int err, dest_ix = 0;
if (flow->flags & MLX5E_TC_FLOW_HAIRPIN) {
- err = mlx5e_hairpin_flow_add(priv, flow, parse_attr);
+ err = mlx5e_hairpin_flow_add(priv, flow, parse_attr, extack);
if (err) {
rule = ERR_PTR(err);
goto err_add_hairpin_flow;
@@ -753,6 +757,8 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
MLX5E_TC_TABLE_NUM_GROUPS,
MLX5E_TC_FT_LEVEL, 0);
if (IS_ERR(priv->fs.tc.t)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Failed to create tc offload table\n");
netdev_err(priv->netdev,
"Failed to create tc offload table\n");
rule = ERR_CAST(priv->fs.tc.t);
@@ -819,12 +825,14 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv,
struct ip_tunnel_info *tun_info,
struct net_device *mirred_dev,
struct net_device **encap_dev,
- struct mlx5e_tc_flow *flow);
+ struct mlx5e_tc_flow *flow,
+ struct netlink_ext_ack *extack);
static struct mlx5_flow_handle *
mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
struct mlx5e_tc_flow_parse_attr *parse_attr,
- struct mlx5e_tc_flow *flow)
+ struct mlx5e_tc_flow *flow,
+ struct netlink_ext_ack *extack)
{
struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
struct mlx5_esw_flow_attr *attr = flow->esw_attr;
@@ -838,7 +846,7 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
out_dev = __dev_get_by_index(dev_net(priv->netdev),
attr->parse_attr->mirred_ifindex);
err = mlx5e_attach_encap(priv, &parse_attr->tun_info,
- out_dev, &encap_dev, flow);
+ out_dev, &encap_dev, flow, extack);
if (err) {
rule = ERR_PTR(err);
if (err != -EAGAIN)
@@ -1105,6 +1113,7 @@ static int parse_tunnel_attr(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
struct tc_cls_flower_offload *f)
{
+ struct netlink_ext_ack *extack = f->common.extack;
void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
outer_headers);
void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
@@ -1133,6 +1142,8 @@ static int parse_tunnel_attr(struct mlx5e_priv *priv,
MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap))
parse_vxlan_attr(spec, f);
else {
+ NL_SET_ERR_MSG_MOD(extack,
+ "port isn't an offloaded vxlan udp dport");
netdev_warn(priv->netdev,
"%d isn't an offloaded vxlan udp dport\n", be16_to_cpu(key->dst));
return -EOPNOTSUPP;
@@ -1149,6 +1160,8 @@ static int parse_tunnel_attr(struct mlx5e_priv *priv,
udp_sport, ntohs(key->src));
} else { /* udp dst port must be given */
vxlan_match_offload_err:
+ NL_SET_ERR_MSG_MOD(extack,
+ "IP tunnel decap offload supported only for vxlan, must set UDP dport");
netdev_warn(priv->netdev,
"IP tunnel decap offload supported only for vxlan, must set UDP dport\n");
return -EOPNOTSUPP;
@@ -1225,6 +1238,16 @@ static int parse_tunnel_attr(struct mlx5e_priv *priv,
MLX5_SET(fte_match_set_lyr_2_4, headers_c, ttl_hoplimit, mask->ttl);
MLX5_SET(fte_match_set_lyr_2_4, headers_v, ttl_hoplimit, key->ttl);
+
+ if (mask->ttl &&
+ !MLX5_CAP_ESW_FLOWTABLE_FDB
+ (priv->mdev,
+ ft_field_support.outer_ipv4_ttl)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Matching on TTL is not supported");
+ return -EOPNOTSUPP;
+ }
+
}
/* Enforce DMAC when offloading incoming tunneled flows.
@@ -1247,6 +1270,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
struct tc_cls_flower_offload *f,
u8 *match_level)
{
+ struct netlink_ext_ack *extack = f->common.extack;
void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
outer_headers);
void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
@@ -1277,6 +1301,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
BIT(FLOW_DISSECTOR_KEY_TCP) |
BIT(FLOW_DISSECTOR_KEY_IP) |
BIT(FLOW_DISSECTOR_KEY_ENC_IP))) {
+ NL_SET_ERR_MSG_MOD(extack, "Unsupported key");
netdev_warn(priv->netdev, "Unsupported key used: 0x%x\n",
f->dissector->used_keys);
return -EOPNOTSUPP;
@@ -1550,8 +1575,11 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
if (mask->ttl &&
!MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev,
- ft_field_support.outer_ipv4_ttl))
+ ft_field_support.outer_ipv4_ttl)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Matching on TTL is not supported");
return -EOPNOTSUPP;
+ }
if (mask->tos || mask->ttl)
*match_level = MLX5_MATCH_L3;
@@ -1593,6 +1621,8 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
udp_dport, ntohs(key->dst));
break;
default:
+ NL_SET_ERR_MSG_MOD(extack,
+ "Only UDP and TCP transports are supported for L4 matching");
netdev_err(priv->netdev,
"Only UDP and TCP transport are supported\n");
return -EINVAL;
@@ -1629,6 +1659,7 @@ static int parse_cls_flower(struct mlx5e_priv *priv,
struct mlx5_flow_spec *spec,
struct tc_cls_flower_offload *f)
{
+ struct netlink_ext_ack *extack = f->common.extack;
struct mlx5_core_dev *dev = priv->mdev;
struct mlx5_eswitch *esw = dev->priv.eswitch;
struct mlx5e_rep_priv *rpriv = priv->ppriv;
@@ -1643,6 +1674,8 @@ static int parse_cls_flower(struct mlx5e_priv *priv,
if (rep->vport != FDB_UPLINK_VPORT &&
(esw->offloads.inline_mode != MLX5_INLINE_MODE_NONE &&
esw->offloads.inline_mode < match_level)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Flow is not offloaded due to min inline setting");
netdev_warn(priv->netdev,
"Flow is not offloaded due to min inline setting, required %d actual %d\n",
match_level, esw->offloads.inline_mode);
@@ -1744,7 +1777,8 @@ static struct mlx5_fields fields[] = {
*/
static int offload_pedit_fields(struct pedit_headers *masks,
struct pedit_headers *vals,
- struct mlx5e_tc_flow_parse_attr *parse_attr)
+ struct mlx5e_tc_flow_parse_attr *parse_attr,
+ struct netlink_ext_ack *extack)
{
struct pedit_headers *set_masks, *add_masks, *set_vals, *add_vals;
int i, action_size, nactions, max_actions, first, last, next_z;
@@ -1783,11 +1817,15 @@ static int offload_pedit_fields(struct pedit_headers *masks,
continue;
if (s_mask && a_mask) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "can't set and add to the same HW field");
printk(KERN_WARNING "mlx5: can't set and add to the same HW field (%x)\n", f->field);
return -EOPNOTSUPP;
}
if (nactions == max_actions) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "too many pedit actions, can't offload");
printk(KERN_WARNING "mlx5: parsed %d pedit actions, can't do more\n", nactions);
return -EOPNOTSUPP;
}
@@ -1820,6 +1858,8 @@ static int offload_pedit_fields(struct pedit_headers *masks,
next_z = find_next_zero_bit(&mask, field_bsize, first);
last = find_last_bit(&mask, field_bsize);
if (first < next_z && next_z < last) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "rewrite of few sub-fields isn't supported");
printk(KERN_WARNING "mlx5: rewrite of few sub-fields (mask %lx) isn't offloaded\n",
mask);
return -EOPNOTSUPP;
@@ -1878,7 +1918,8 @@ static const struct pedit_headers zero_masks = {};
static int parse_tc_pedit_action(struct mlx5e_priv *priv,
const struct tc_action *a, int namespace,
- struct mlx5e_tc_flow_parse_attr *parse_attr)
+ struct mlx5e_tc_flow_parse_attr *parse_attr,
+ struct netlink_ext_ack *extack)
{
struct pedit_headers masks[__PEDIT_CMD_MAX], vals[__PEDIT_CMD_MAX], *cmd_masks;
int nkeys, i, err = -EOPNOTSUPP;
@@ -1896,12 +1937,13 @@ static int parse_tc_pedit_action(struct mlx5e_priv *priv,
err = -EOPNOTSUPP; /* can't be all optimistic */
if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
- netdev_warn(priv->netdev, "legacy pedit isn't offloaded\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "legacy pedit isn't offloaded");
goto out_err;
}
if (cmd != TCA_PEDIT_KEY_EX_CMD_SET && cmd != TCA_PEDIT_KEY_EX_CMD_ADD) {
- netdev_warn(priv->netdev, "pedit cmd %d isn't offloaded\n", cmd);
+ NL_SET_ERR_MSG_MOD(extack, "pedit cmd isn't offloaded");
goto out_err;
}
@@ -1918,13 +1960,15 @@ static int parse_tc_pedit_action(struct mlx5e_priv *priv,
if (err)
goto out_err;
- err = offload_pedit_fields(masks, vals, parse_attr);
+ err = offload_pedit_fields(masks, vals, parse_attr, extack);
if (err < 0)
goto out_dealloc_parsed_actions;
for (cmd = 0; cmd < __PEDIT_CMD_MAX; cmd++) {
cmd_masks = &masks[cmd];
if (memcmp(cmd_masks, &zero_masks, sizeof(zero_masks))) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "attempt to offload an unsupported field");
netdev_warn(priv->netdev, "attempt to offload an unsupported field (cmd %d)\n", cmd);
print_hex_dump(KERN_WARNING, "mask: ", DUMP_PREFIX_ADDRESS,
16, 1, cmd_masks, sizeof(zero_masks), true);
@@ -1941,19 +1985,26 @@ static int parse_tc_pedit_action(struct mlx5e_priv *priv,
return err;
}
-static bool csum_offload_supported(struct mlx5e_priv *priv, u32 action, u32 update_flags)
+static bool csum_offload_supported(struct mlx5e_priv *priv,
+ u32 action,
+ u32 update_flags,
+ struct netlink_ext_ack *extack)
{
u32 prot_flags = TCA_CSUM_UPDATE_FLAG_IPV4HDR | TCA_CSUM_UPDATE_FLAG_TCP |
TCA_CSUM_UPDATE_FLAG_UDP;
/* The HW recalcs checksums only if re-writing headers */
if (!(action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "TC csum action is only offloaded with pedit");
netdev_warn(priv->netdev,
"TC csum action is only offloaded with pedit\n");
return false;
}
if (update_flags & ~prot_flags) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "can't offload TC csum action for some header/s");
netdev_warn(priv->netdev,
"can't offload TC csum action for some header/s - flags %#x\n",
update_flags);
@@ -1964,7 +2015,8 @@ static bool csum_offload_supported(struct mlx5e_priv *priv, u32 action, u32 upda
}
static bool modify_header_match_supported(struct mlx5_flow_spec *spec,
- struct tcf_exts *exts)
+ struct tcf_exts *exts,
+ struct netlink_ext_ack *extack)
{
const struct tc_action *a;
bool modify_ip_header;
@@ -2002,6 +2054,8 @@ static bool modify_header_match_supported(struct mlx5_flow_spec *spec,
ip_proto = MLX5_GET(fte_match_set_lyr_2_4, headers_v, ip_protocol);
if (modify_ip_header && ip_proto != IPPROTO_TCP &&
ip_proto != IPPROTO_UDP && ip_proto != IPPROTO_ICMP) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "can't offload re-write of non TCP/UDP");
pr_info("can't offload re-write of ip proto %d\n", ip_proto);
return false;
}
@@ -2013,7 +2067,8 @@ static bool modify_header_match_supported(struct mlx5_flow_spec *spec,
static bool actions_match_supported(struct mlx5e_priv *priv,
struct tcf_exts *exts,
struct mlx5e_tc_flow_parse_attr *parse_attr,
- struct mlx5e_tc_flow *flow)
+ struct mlx5e_tc_flow *flow,
+ struct netlink_ext_ack *extack)
{
u32 actions;
@@ -2027,7 +2082,8 @@ static bool actions_match_supported(struct mlx5e_priv *priv,
return false;
if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
- return modify_header_match_supported(&parse_attr->spec, exts);
+ return modify_header_match_supported(&parse_attr->spec, exts,
+ extack);
return true;
}
@@ -2048,7 +2104,8 @@ static bool same_hw_devs(struct mlx5e_priv *priv, struct mlx5e_priv *peer_priv)
static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
struct mlx5e_tc_flow_parse_attr *parse_attr,
- struct mlx5e_tc_flow *flow)
+ struct mlx5e_tc_flow *flow,
+ struct netlink_ext_ack *extack)
{
struct mlx5_nic_flow_attr *attr = flow->nic_attr;
const struct tc_action *a;
@@ -2072,7 +2129,7 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
if (is_tcf_pedit(a)) {
err = parse_tc_pedit_action(priv, a, MLX5_FLOW_NAMESPACE_KERNEL,
- parse_attr);
+ parse_attr, extack);
if (err)
return err;
@@ -2083,7 +2140,8 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
if (is_tcf_csum(a)) {
if (csum_offload_supported(priv, action,
- tcf_csum_update_flags(a)))
+ tcf_csum_update_flags(a),
+ extack))
continue;
return -EOPNOTSUPP;
@@ -2099,6 +2157,8 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
MLX5_FLOW_CONTEXT_ACTION_COUNT;
} else {
+ NL_SET_ERR_MSG_MOD(extack,
+ "device is not on same HW, can't offload");
netdev_warn(priv->netdev, "device %s not on same HW, can't offload\n",
peer_dev->name);
return -EINVAL;
@@ -2110,8 +2170,8 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
u32 mark = tcf_skbedit_mark(a);
if (mark & ~MLX5E_TC_FLOW_ID_MASK) {
- netdev_warn(priv->netdev, "Bad flow mark - only 16 bit is supported: 0x%x\n",
- mark);
+ NL_SET_ERR_MSG_MOD(extack,
+ "Bad flow mark - only 16 bit is supported");
return -EINVAL;
}
@@ -2124,7 +2184,7 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
}
attr->action = action;
- if (!actions_match_supported(priv, exts, parse_attr, flow))
+ if (!actions_match_supported(priv, exts, parse_attr, flow, extack))
return -EOPNOTSUPP;
return 0;
@@ -2526,7 +2586,8 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv,
struct ip_tunnel_info *tun_info,
struct net_device *mirred_dev,
struct net_device **encap_dev,
- struct mlx5e_tc_flow *flow)
+ struct mlx5e_tc_flow *flow,
+ struct netlink_ext_ack *extack)
{
struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
unsigned short family = ip_tunnel_info_af(tun_info);
@@ -2544,6 +2605,8 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv,
/* setting udp src port isn't supported */
if (memchr_inv(&key->tp_src, 0, sizeof(key->tp_src))) {
vxlan_encap_offload_err:
+ NL_SET_ERR_MSG_MOD(extack,
+ "must set udp dst port and not set udp src port");
netdev_warn(priv->netdev,
"must set udp dst port and not set udp src port\n");
return -EOPNOTSUPP;
@@ -2553,6 +2616,8 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv,
MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap)) {
tunnel_type = MLX5_HEADER_TYPE_VXLAN;
} else {
+ NL_SET_ERR_MSG_MOD(extack,
+ "port isn't an offloaded vxlan udp dport");
netdev_warn(priv->netdev,
"%d isn't an offloaded vxlan udp dport\n", be16_to_cpu(key->tp_dst));
return -EOPNOTSUPP;
@@ -2657,7 +2722,8 @@ static int parse_tc_vlan_action(struct mlx5e_priv *priv,
static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
struct mlx5e_tc_flow_parse_attr *parse_attr,
- struct mlx5e_tc_flow *flow)
+ struct mlx5e_tc_flow *flow,
+ struct netlink_ext_ack *extack)
{
struct mlx5_esw_flow_attr *attr = flow->esw_attr;
struct mlx5e_rep_priv *rpriv = priv->ppriv;
@@ -2683,7 +2749,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
if (is_tcf_pedit(a)) {
err = parse_tc_pedit_action(priv, a, MLX5_FLOW_NAMESPACE_FDB,
- parse_attr);
+ parse_attr, extack);
if (err)
return err;
@@ -2694,7 +2760,8 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
if (is_tcf_csum(a)) {
if (csum_offload_supported(priv, action,
- tcf_csum_update_flags(a)))
+ tcf_csum_update_flags(a),
+ extack))
continue;
return -EOPNOTSUPP;
@@ -2707,6 +2774,8 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
out_dev = tcf_mirred_dev(a);
if (attr->out_count >= MLX5_MAX_FLOW_FWD_VPORTS) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "can't support more output ports, can't offload forwarding");
pr_err("can't support more than %d output ports, can't offload forwarding\n",
attr->out_count);
return -EOPNOTSUPP;
@@ -2730,6 +2799,8 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
MLX5_FLOW_CONTEXT_ACTION_COUNT;
/* attr->out_rep is resolved when we handle encap */
} else {
+ NL_SET_ERR_MSG_MOD(extack,
+ "devices are not on same switch HW, can't offload forwarding");
pr_err("devices %s %s not on same switch HW, can't offload forwarding\n",
priv->netdev->name, out_dev->name);
return -EINVAL;
@@ -2766,10 +2837,12 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
}
attr->action = action;
- if (!actions_match_supported(priv, exts, parse_attr, flow))
+ if (!actions_match_supported(priv, exts, parse_attr, flow, extack))
return -EOPNOTSUPP;
if (attr->out_count > 1 && !mlx5_esw_has_fwd_fdb(priv->mdev)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "current firmware doesn't support split rule for port mirroring");
netdev_warn_once(priv->netdev, "current firmware doesn't support split rule for port mirroring\n");
return -EOPNOTSUPP;
}
@@ -2811,6 +2884,7 @@ static struct rhashtable *get_tc_ht(struct mlx5e_priv *priv)
int mlx5e_configure_flower(struct mlx5e_priv *priv,
struct tc_cls_flower_offload *f, int flags)
{
+ struct netlink_ext_ack *extack = f->common.extack;
struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
struct mlx5e_tc_flow_parse_attr *parse_attr;
struct rhashtable *tc_ht = get_tc_ht(priv);
@@ -2822,6 +2896,8 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv,
flow = rhashtable_lookup_fast(tc_ht, &f->cookie, tc_ht_params);
if (flow) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "flow cookie already exists, ignoring");
netdev_warn_once(priv->netdev, "flow cookie %lx already exists, ignoring\n", f->cookie);
return 0;
}
@@ -2850,15 +2926,19 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv,
goto err_free;
if (flow->flags & MLX5E_TC_FLOW_ESWITCH) {
- err = parse_tc_fdb_actions(priv, f->exts, parse_attr, flow);
+ err = parse_tc_fdb_actions(priv, f->exts, parse_attr, flow,
+ extack);
if (err < 0)
goto err_free;
- flow->rule[0] = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow);
+ flow->rule[0] = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow,
+ extack);
} else {
- err = parse_tc_nic_actions(priv, f->exts, parse_attr, flow);
+ err = parse_tc_nic_actions(priv, f->exts, parse_attr, flow,
+ extack);
if (err < 0)
goto err_free;
- flow->rule[0] = mlx5e_tc_add_nic_flow(priv, parse_attr, flow);
+ flow->rule[0] = mlx5e_tc_add_nic_flow(priv, parse_attr, flow,
+ extack);
}
if (IS_ERR(flow->rule[0])) {
--
2.17.1
^ permalink raw reply related
* [net-next 4/5] net/mlx5e: Add new counter for aRFS rule insertion failures
From: Saeed Mahameed @ 2018-10-04 14:31 UTC (permalink / raw)
To: David S. Miller; +Cc: Jiri Pirko, netdev, Eran Ben Elisha, Saeed Mahameed
In-Reply-To: <20181004143124.11607-1-saeedm@mellanox.com>
From: Eran Ben Elisha <eranbe@mellanox.com>
Count aRFS rules insertion failure for ethtool output. In addition, move
the error print into debug prints mechanism, as it could flood the dmesg
and reduce system BW dramatically.
Signed-off-by: Eran Ben Elisha <eranbe@mellanox.com>
Reviewed-by: Tariq Toukan <tariqt@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
---
drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c | 7 +++++--
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c | 3 +++
drivers/net/ethernet/mellanox/mlx5/core/en_stats.h | 2 ++
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c
index 45cdde694d20..8657e0f26995 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c
@@ -543,8 +543,11 @@ static struct mlx5_flow_handle *arfs_add_rule(struct mlx5e_priv *priv,
rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1);
if (IS_ERR(rule)) {
err = PTR_ERR(rule);
- netdev_err(priv->netdev, "%s: add rule(filter id=%d, rq idx=%d) failed, err=%d\n",
- __func__, arfs_rule->filter_id, arfs_rule->rxq, err);
+ priv->channel_stats[arfs_rule->rxq].rq.arfs_err++;
+ mlx5e_dbg(HW, priv,
+ "%s: add rule(filter id=%d, rq idx=%d, ip proto=0x%x) failed,err=%d\n",
+ __func__, arfs_rule->filter_id, arfs_rule->rxq,
+ tuple->ip_proto, err);
}
out:
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
index 90c7607b1f44..b7d4896c7c7b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
@@ -93,6 +93,7 @@ static const struct counter_desc sw_stats_desc[] = {
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cache_busy) },
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cache_waive) },
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_congst_umr) },
+ { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_arfs_err) },
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, ch_events) },
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, ch_poll) },
{ MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, ch_arm) },
@@ -170,6 +171,7 @@ void mlx5e_grp_sw_update_stats(struct mlx5e_priv *priv)
s->rx_cache_busy += rq_stats->cache_busy;
s->rx_cache_waive += rq_stats->cache_waive;
s->rx_congst_umr += rq_stats->congst_umr;
+ s->rx_arfs_err += rq_stats->arfs_err;
s->ch_events += ch_stats->events;
s->ch_poll += ch_stats->poll;
s->ch_arm += ch_stats->arm;
@@ -1161,6 +1163,7 @@ static const struct counter_desc rq_stats_desc[] = {
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cache_busy) },
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cache_waive) },
{ MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, congst_umr) },
+ { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, arfs_err) },
};
static const struct counter_desc sq_stats_desc[] = {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
index a5fb3dc27f50..77f74ce11280 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
@@ -106,6 +106,7 @@ struct mlx5e_sw_stats {
u64 rx_cache_busy;
u64 rx_cache_waive;
u64 rx_congst_umr;
+ u64 rx_arfs_err;
u64 ch_events;
u64 ch_poll;
u64 ch_arm;
@@ -202,6 +203,7 @@ struct mlx5e_rq_stats {
u64 cache_busy;
u64 cache_waive;
u64 congst_umr;
+ u64 arfs_err;
};
struct mlx5e_sq_stats {
--
2.17.1
^ permalink raw reply related
* [net-next 2/5] net/mlx5e: E-Switch, Add extack messages to devlink callbacks
From: Saeed Mahameed @ 2018-10-04 14:31 UTC (permalink / raw)
To: David S. Miller; +Cc: Jiri Pirko, netdev, Eli Britstein, Saeed Mahameed
In-Reply-To: <20181004143124.11607-1-saeedm@mellanox.com>
From: Eli Britstein <elibr@mellanox.com>
Return extack messages for failures in the e-switch devlink callbacks.
Messages provide reasons for not being able to issue the operation.
Signed-off-by: Eli Britstein <elibr@mellanox.com>
Reviewed-by: Or Gerlitz <ogerlitz@mellanox.com>
Reviewed-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
---
.../mellanox/mlx5/core/eswitch_offloads.c | 38 ++++++++++++-------
1 file changed, 24 insertions(+), 14 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index eee34ffcf242..a35a2310f871 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -816,24 +816,29 @@ static int esw_offloads_start(struct mlx5_eswitch *esw,
int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs;
if (esw->mode != SRIOV_LEGACY) {
- esw_warn(esw->dev, "Can't set offloads mode, SRIOV legacy not enabled\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "Can't set offloads mode, SRIOV legacy not enabled");
return -EINVAL;
}
mlx5_eswitch_disable_sriov(esw);
err = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_OFFLOADS);
if (err) {
- esw_warn(esw->dev, "Failed setting eswitch to offloads, err %d\n", err);
+ NL_SET_ERR_MSG_MOD(extack,
+ "Failed setting eswitch to offloads");
err1 = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_LEGACY);
- if (err1)
- esw_warn(esw->dev, "Failed setting eswitch back to legacy, err %d\n", err1);
+ if (err1) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Failed setting eswitch back to legacy");
+ }
}
if (esw->offloads.inline_mode == MLX5_INLINE_MODE_NONE) {
if (mlx5_eswitch_inline_mode_get(esw,
num_vfs,
&esw->offloads.inline_mode)) {
esw->offloads.inline_mode = MLX5_INLINE_MODE_L2;
- esw_warn(esw->dev, "Inline mode is different between vports\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "Inline mode is different between vports");
}
}
return err;
@@ -982,10 +987,12 @@ static int esw_offloads_stop(struct mlx5_eswitch *esw,
mlx5_eswitch_disable_sriov(esw);
err = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_LEGACY);
if (err) {
- esw_warn(esw->dev, "Failed setting eswitch to legacy, err %d\n", err);
+ NL_SET_ERR_MSG_MOD(extack, "Failed setting eswitch to legacy");
err1 = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_OFFLOADS);
- if (err1)
- esw_warn(esw->dev, "Failed setting eswitch back to offloads, err %d\n", err);
+ if (err1) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Failed setting eswitch back to offloads");
+ }
}
/* enable back PF RoCE */
@@ -1151,14 +1158,15 @@ int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode,
return 0;
/* fall through */
case MLX5_CAP_INLINE_MODE_L2:
- esw_warn(dev, "Inline mode can't be set\n");
+ NL_SET_ERR_MSG_MOD(extack, "Inline mode can't be set");
return -EOPNOTSUPP;
case MLX5_CAP_INLINE_MODE_VPORT_CONTEXT:
break;
}
if (esw->offloads.num_flows > 0) {
- esw_warn(dev, "Can't set inline mode when flows are configured\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "Can't set inline mode when flows are configured");
return -EOPNOTSUPP;
}
@@ -1169,8 +1177,8 @@ int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode,
for (vport = 1; vport < esw->enabled_vports; vport++) {
err = mlx5_modify_nic_vport_min_inline(dev, vport, mlx5_mode);
if (err) {
- esw_warn(dev, "Failed to set min inline on vport %d\n",
- vport);
+ NL_SET_ERR_MSG_MOD(extack,
+ "Failed to set min inline on vport");
goto revert_inline_mode;
}
}
@@ -1264,7 +1272,8 @@ int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, u8 encap,
return 0;
if (esw->offloads.num_flows > 0) {
- esw_warn(dev, "Can't set encapsulation when flows are configured\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "Can't set encapsulation when flows are configured");
return -EOPNOTSUPP;
}
@@ -1273,7 +1282,8 @@ int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, u8 encap,
esw->offloads.encap = encap;
err = esw_create_offloads_fast_fdb_table(esw);
if (err) {
- esw_warn(esw->dev, "Failed re-creating fast FDB table, err %d\n", err);
+ NL_SET_ERR_MSG_MOD(extack,
+ "Failed re-creating fast FDB table");
esw->offloads.encap = !encap;
(void)esw_create_offloads_fast_fdb_table(esw);
}
--
2.17.1
^ permalink raw reply related
* [net-next 1/5] devlink: Add extack for eswitch operations
From: Saeed Mahameed @ 2018-10-04 14:31 UTC (permalink / raw)
To: David S. Miller; +Cc: Jiri Pirko, netdev, Eli Britstein, Saeed Mahameed
In-Reply-To: <20181004143124.11607-1-saeedm@mellanox.com>
From: Eli Britstein <elibr@mellanox.com>
Add extack argument to the eswitch related operations.
Signed-off-by: Eli Britstein <elibr@mellanox.com>
Reviewed-by: Or Gerlitz <ogerlitz@mellanox.com>
Reviewed-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
---
drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c | 3 ++-
drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h | 3 ++-
.../net/ethernet/cavium/liquidio/lio_main.c | 3 ++-
.../net/ethernet/mellanox/mlx5/core/eswitch.h | 9 ++++++---
.../mellanox/mlx5/core/eswitch_offloads.c | 19 ++++++++++++-------
.../net/ethernet/netronome/nfp/nfp_devlink.c | 3 ++-
include/net/devlink.h | 9 ++++++---
net/core/devlink.c | 8 +++++---
8 files changed, 37 insertions(+), 20 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c
index b574fe8e974e..9a25c05aa571 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c
@@ -521,7 +521,8 @@ int bnxt_dl_eswitch_mode_get(struct devlink *devlink, u16 *mode)
return 0;
}
-int bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode)
+int bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode,
+ struct netlink_ext_ack *extack)
{
struct bnxt *bp = bnxt_get_bp_from_dl(devlink);
int rc = 0;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h
index 38b9a75ad724..d7287651422f 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h
@@ -30,7 +30,8 @@ static inline u16 bnxt_vf_rep_get_fid(struct net_device *dev)
bool bnxt_dev_is_vf_rep(struct net_device *dev);
int bnxt_dl_eswitch_mode_get(struct devlink *devlink, u16 *mode);
-int bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode);
+int bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode,
+ struct netlink_ext_ack *extack);
#else
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c
index 9d70e5c6157f..3d24133e5e49 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c
@@ -3144,7 +3144,8 @@ liquidio_eswitch_mode_get(struct devlink *devlink, u16 *mode)
}
static int
-liquidio_eswitch_mode_set(struct devlink *devlink, u16 mode)
+liquidio_eswitch_mode_set(struct devlink *devlink, u16 mode,
+ struct netlink_ext_ack *extack)
{
struct lio_devlink_priv *priv;
struct octeon_device *oct;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
index 0b05bf2b91f6..dfc642de4e6d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -269,12 +269,15 @@ struct mlx5_esw_flow_attr {
struct mlx5e_tc_flow_parse_attr *parse_attr;
};
-int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode);
+int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
+ struct netlink_ext_ack *extack);
int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode);
-int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode);
+int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode,
+ struct netlink_ext_ack *extack);
int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode);
int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode);
-int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, u8 encap);
+int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, u8 encap,
+ struct netlink_ext_ack *extack);
int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink, u8 *encap);
void *mlx5_eswitch_get_uplink_priv(struct mlx5_eswitch *esw, u8 rep_type);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index 21e957083f65..eee34ffcf242 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -810,7 +810,8 @@ mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, int vport,
return flow_rule;
}
-static int esw_offloads_start(struct mlx5_eswitch *esw)
+static int esw_offloads_start(struct mlx5_eswitch *esw,
+ struct netlink_ext_ack *extack)
{
int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs;
@@ -973,7 +974,8 @@ int esw_offloads_init(struct mlx5_eswitch *esw, int nvports)
return err;
}
-static int esw_offloads_stop(struct mlx5_eswitch *esw)
+static int esw_offloads_stop(struct mlx5_eswitch *esw,
+ struct netlink_ext_ack *extack)
{
int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs;
@@ -1092,7 +1094,8 @@ static int mlx5_devlink_eswitch_check(struct devlink *devlink)
return 0;
}
-int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode)
+int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
+ struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
u16 cur_mlx5_mode, mlx5_mode = 0;
@@ -1111,9 +1114,9 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode)
return 0;
if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV)
- return esw_offloads_start(dev->priv.eswitch);
+ return esw_offloads_start(dev->priv.eswitch, extack);
else if (mode == DEVLINK_ESWITCH_MODE_LEGACY)
- return esw_offloads_stop(dev->priv.eswitch);
+ return esw_offloads_stop(dev->priv.eswitch, extack);
else
return -EINVAL;
}
@@ -1130,7 +1133,8 @@ int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
return esw_mode_to_devlink(dev->priv.eswitch->mode, mode);
}
-int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode)
+int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode,
+ struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
struct mlx5_eswitch *esw = dev->priv.eswitch;
@@ -1232,7 +1236,8 @@ int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode)
return 0;
}
-int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, u8 encap)
+int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, u8 encap,
+ struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
struct mlx5_eswitch *esw = dev->priv.eswitch;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c
index db463e20a876..4213fe42ac4d 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c
@@ -177,7 +177,8 @@ static int nfp_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
return nfp_app_eswitch_mode_get(pf->app, mode);
}
-static int nfp_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode)
+static int nfp_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
+ struct netlink_ext_ack *extack)
{
struct nfp_pf *pf = devlink_priv(devlink);
int ret;
diff --git a/include/net/devlink.h b/include/net/devlink.h
index b9b89d6604d4..70671f0d4c30 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -451,11 +451,14 @@ struct devlink_ops {
u32 *p_cur, u32 *p_max);
int (*eswitch_mode_get)(struct devlink *devlink, u16 *p_mode);
- int (*eswitch_mode_set)(struct devlink *devlink, u16 mode);
+ int (*eswitch_mode_set)(struct devlink *devlink, u16 mode,
+ struct netlink_ext_ack *extack);
int (*eswitch_inline_mode_get)(struct devlink *devlink, u8 *p_inline_mode);
- int (*eswitch_inline_mode_set)(struct devlink *devlink, u8 inline_mode);
+ int (*eswitch_inline_mode_set)(struct devlink *devlink, u8 inline_mode,
+ struct netlink_ext_ack *extack);
int (*eswitch_encap_mode_get)(struct devlink *devlink, u8 *p_encap_mode);
- int (*eswitch_encap_mode_set)(struct devlink *devlink, u8 encap_mode);
+ int (*eswitch_encap_mode_set)(struct devlink *devlink, u8 encap_mode,
+ struct netlink_ext_ack *extack);
};
static inline void *devlink_priv(struct devlink *devlink)
diff --git a/net/core/devlink.c b/net/core/devlink.c
index 8c0ed225e280..de6adad7ccbe 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -1626,7 +1626,7 @@ static int devlink_nl_cmd_eswitch_set_doit(struct sk_buff *skb,
if (!ops->eswitch_mode_set)
return -EOPNOTSUPP;
mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]);
- err = ops->eswitch_mode_set(devlink, mode);
+ err = ops->eswitch_mode_set(devlink, mode, info->extack);
if (err)
return err;
}
@@ -1636,7 +1636,8 @@ static int devlink_nl_cmd_eswitch_set_doit(struct sk_buff *skb,
return -EOPNOTSUPP;
inline_mode = nla_get_u8(
info->attrs[DEVLINK_ATTR_ESWITCH_INLINE_MODE]);
- err = ops->eswitch_inline_mode_set(devlink, inline_mode);
+ err = ops->eswitch_inline_mode_set(devlink, inline_mode,
+ info->extack);
if (err)
return err;
}
@@ -1645,7 +1646,8 @@ static int devlink_nl_cmd_eswitch_set_doit(struct sk_buff *skb,
if (!ops->eswitch_encap_mode_set)
return -EOPNOTSUPP;
encap_mode = nla_get_u8(info->attrs[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]);
- err = ops->eswitch_encap_mode_set(devlink, encap_mode);
+ err = ops->eswitch_encap_mode_set(devlink, encap_mode,
+ info->extack);
if (err)
return err;
}
--
2.17.1
^ permalink raw reply related
* [pull request][net-next 0/5] Mellanox, mlx5 updates 2018-10-03
From: Saeed Mahameed @ 2018-10-04 14:31 UTC (permalink / raw)
To: David S. Miller; +Cc: Jiri Pirko, netdev, Saeed Mahameed
Hi Dave,
The following series includes mlx5 core driver and ethernet netdev updates,
please note there is a small devlink related update to allow extack
argument to eswitch operations, for more information please see tag log
below.
Please pull and let me know if there's any problem.
Thanks,
Saeed.
---
The following changes since commit 024926def6ca95819442699fbecc1fe376253fb9:
net: phy: Convert to using %pOFn instead of device_node.name (2018-10-01 23:29:37 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/saeed/linux.git tags/mlx5-updates-2018-10-03
for you to fetch changes up to fcd29ad17c6ff885dfae58f557e9323941e63ba2:
net/mlx5: Add Fast teardown support (2018-10-03 16:18:00 -0700)
----------------------------------------------------------------
mlx5-updates-2018-10-03
mlx5 core driver and ethernet netdev updates, please note there is a small
devlink releated update to allow extack argument to eswitch operations.
>From Eli Britstein,
1) devlink: Add extack argument to the eswitch related operations
2) net/mlx5e: E-Switch, return extack messages for failures in the e-switch devlink callbacks
3) net/mlx5e: Add extack messages for TC offload failures
>From Eran Ben Elisha,
4) mlx5e: Add counter for aRFS rule insertion failures
>From Feras Daoud
5) Fast teardown support for mlx5 device
This change introduces the enhanced version of the "Force teardown" that
allows SW to perform teardown in a faster way without the need to reclaim
all the FW pages.
Fast teardown provides the following advantages:
1- Fix a FW race condition that could cause command timeout
2- Avoid moving to polling mode
3- Close the vport to prevent PCI ACK to be sent without been scatter
to memory
----------------------------------------------------------------
Eli Britstein (3):
devlink: Add extack for eswitch operations
net/mlx5e: E-Switch, Add extack messages to devlink callbacks
net/mlx5e: Add extack messages for TC offload failures
Eran Ben Elisha (1):
net/mlx5e: Add new counter for aRFS rule insertion failures
Feras Daoud (1):
net/mlx5: Add Fast teardown support
drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c | 3 +-
drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h | 3 +-
drivers/net/ethernet/cavium/liquidio/lio_main.c | 3 +-
drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c | 7 +-
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c | 3 +
drivers/net/ethernet/mellanox/mlx5/core/en_stats.h | 2 +
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 156 ++++++++++++++++-----
drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 9 +-
.../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 57 +++++---
drivers/net/ethernet/mellanox/mlx5/core/fw.c | 50 ++++++-
drivers/net/ethernet/mellanox/mlx5/core/health.c | 25 ++--
drivers/net/ethernet/mellanox/mlx5/core/main.c | 29 ++--
.../net/ethernet/mellanox/mlx5/core/mlx5_core.h | 12 ++
drivers/net/ethernet/netronome/nfp/nfp_devlink.c | 3 +-
include/linux/mlx5/device.h | 4 +
include/linux/mlx5/mlx5_ifc.h | 6 +-
include/net/devlink.h | 9 +-
net/core/devlink.c | 8 +-
18 files changed, 292 insertions(+), 97 deletions(-)
^ permalink raw reply
* Re: [RFC 0/2] net: sched: indirect/remote setup tc block cb registering
From: Or Gerlitz @ 2018-10-04 14:28 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Linux Netdev List, Jiri Pirko, oss-drivers, John Hurley,
Oz Shlomo, Aviv Heller
In-Reply-To: <20181004045511.27733-1-jakub.kicinski@netronome.com>
On Thu, Oct 4, 2018 at 7:55 AM Jakub Kicinski
<jakub.kicinski@netronome.com> wrote:
>
> Hi!
>
> This set contains a rough RFC implementation of a proposed [1] replacement
> for egdev cls_flower offloads. I did some last minute restructuring
> and removal of parts I felt were unnecessary, so if there are glaring bugs
> they are probably mine, not John's :) but hopefully this will give an idea
> of the general direction. We need to beef up the driver part to see how
> it fully comes together.
>
> [1] http://vger.kernel.org/netconf2018_files/JakubKicinski_netconf2018.pdf
> slides 10-13
>
> John's says:
>
> This patchset introduces as an alternative to egdev offload by allowing a
> driver to register for block updates when an external device (e.g. tunnel
> netdev) is bound to a TC block.
In a slightly different but hopefully somehow related context, regarding
the case of flow offloading in the presence of upper devices (specifically LAG),
your ovs user patch [1] applied TC block sharing on the slave of lag
(bond/team)
device which serves as ovs port. This way, flows that are installed on
the bond are
propagated to both uplink devices - good!
However, when tunneling comes into play, the bond device is not part of
the virtual switch but rather the tunnel device, so the SW DP is
wire --> hw driver --> bond --> stack --> tunnel driver --> virtual switch
So now, if the HW driver uses your new facility to register for rules
installed on the
tunnel device, we are again properly sharing (duplicating) the rules
to both uplinks, right?!
[1] d22f892 netdev-linux: monitor and offload LAG slaves to TC
> Drivers can track new netdevs or register
> to existing ones to receive information on such events. Based on this,
> they may register for block offload rules using already existing functions.
Just to make it clear, (part of) the claim to fame here is that once
we have this
code in, we can just go and remove all the egdev related code from the
kernel (both
core and drivers), right? only nfp and mlx5 use egdev, so the removal
should be simple
exercise.
> Included with this RFC is a patch to the NFP driver. This is only supposed
> to provide an example of how the remote block setup can be used.
We will look and play with the patches next week and provide feedback, cool
that you took the lead to improve the facilities here!
^ permalink raw reply
* Re: [PATCH] docs: net: typo fix in Documentation/networking/af_xdp.rst
From: Daniel Borkmann @ 2018-10-04 14:25 UTC (permalink / raw)
To: Konrad Djimeli, netdev
In-Reply-To: <20181004135643.3224-1-kdjimeli@igalia.com>
On 10/04/2018 03:56 PM, Konrad Djimeli wrote:
> Fix a simple typo: Completetion -> Completion
>
> Signed-off-by: Konrad Djimeli <kdjimeli@igalia.com>
> ---
> Documentation/networking/af_xdp.rst | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/Documentation/networking/af_xdp.rst b/Documentation/networking/af_xdp.rst
> index ff929cfab4f4..e81162609ce3 100644
> --- a/Documentation/networking/af_xdp.rst
> +++ b/Documentation/networking/af_xdp.rst
> @@ -159,7 +159,7 @@ log2(2048) LSB of the addr will be masked off, meaning that 2048, 2050
> and 3000 refers to the same chunk.
>
>
> -UMEM Completetion Ring
> +UMEM Completion Ring
> ~~~~~~~~~~~~~~~~~~~~~~
Please keep the line also as same length to the text above.
> The Completion Ring is used transfer ownership of UMEM frames from
>
^ permalink raw reply
* Re: [PATCH bpf-next 0/6] Consistent prefixes for libbpf interfaces
From: Daniel Borkmann @ 2018-10-04 14:22 UTC (permalink / raw)
To: Andrey Ignatov, netdev; +Cc: ast, kernel-team, yhs
In-Reply-To: <cover.1538605140.git.rdna@fb.com>
[ +Yonghong ]
On 10/04/2018 12:26 AM, Andrey Ignatov wrote:
> This patch set renames a few interfaces in libbpf, mostly netlink related,
> so that all symbols provided by the library have only three possible
> prefixes:
>
> % nm -D tools/lib/bpf/libbpf.so | \
> awk '$2 == "T" {sub(/[_\(].*/, "", $3); if ($3) print $3}' | \
> sort | \
> uniq -c
> 91 bpf
> 8 btf
> 14 libbpf
>
> libbpf is used more and more outside kernel tree. That means the library
> should follow good practices in library design and implementation to
> play well with third party code that uses it.
>
> One of such practices is to have a common prefix (or a few) for every
> interface, function or data structure, library provides. It helps to
> avoid name conflicts with other libraries and keeps API/ABI consistent.
>
> Inconsistent names in libbpf already cause problems in real life. E.g.
> an application can't use both libbpf and libnl due to conflicting
> symbols (specifically nla_parse, nla_parse_nested and a few others).
>
> Some of problematic global symbols are not part of ABI and can be
> restricted from export with either visibility attribute/pragma or export
> map (what is useful by itself and can be done in addition). That won't
> solve the problem for those that are part of ABI though. Also export
> restrictions would help only in DSO case. If third party application links
> libbpf statically it won't help, and people do it (e.g. Facebook links
> most of libraries statically, including libbpf).
>
> libbpf already uses the following prefixes for its interfaces:
> * bpf_ for bpf system call wrappers, program/map/elf-object
> abstractions and a few other things;
> * btf_ for BTF related API;
> * libbpf_ for everything else.
>
> The patch adds libbpf_ prefix to interfaces that use none of mentioned
> above prefixes and don't fit well into the first two categories.
>
> Long term benefits of having common prefix should outweigh possible
> inconvenience of changing API for those functions now.
>
> Patches 2-4 add libbpf_ prefix to libbpf interfaces: separate patch per
> header. Other patches are simple improvements in API.
>
>
> Andrey Ignatov (6):
> libbpf: Move __dump_nlmsg_t from API to implementation
> libbpf: Consistent prefixes for interfaces in libbpf.h.
> libbpf: Consistent prefixes for interfaces in nlattr.h.
> libbpf: Consistent prefixes for interfaces in str_error.h.
> libbpf: Make include guards consistent
> libbpf: Use __u32 instead of u32 in bpf_program__load
>
> tools/bpf/bpftool/net.c | 41 ++++++++++---------
> tools/bpf/bpftool/netlink_dumper.c | 32 ++++++++-------
> tools/lib/bpf/bpf.h | 6 +--
> tools/lib/bpf/btf.h | 6 +--
> tools/lib/bpf/libbpf.c | 22 +++++-----
> tools/lib/bpf/libbpf.h | 31 +++++++-------
> tools/lib/bpf/netlink.c | 48 ++++++++++++----------
> tools/lib/bpf/nlattr.c | 64 +++++++++++++++--------------
> tools/lib/bpf/nlattr.h | 65 +++++++++++++++---------------
> tools/lib/bpf/str_error.c | 2 +-
> tools/lib/bpf/str_error.h | 8 ++--
> 11 files changed, 171 insertions(+), 154 deletions(-)
Overall agree that this is needed, and I've therefore applied the
set, thanks for cleaning up, Andrey!
But, I would actually like to see this going one step further, in
particular, we should keep all the netlink related stuff inside
libbpf-/only/. Meaning, the goal of libbpf is not to provide yet
another set of netlink primitives but instead e.g. for the bpftool
dumper this should be abstracted away such that we pass in a callback
from bpftool side and have an iterator object which will then be
populated from inside the libbpf logic, meaning, bpftool shouldn't
even be aware of anything netlink there.
Thanks,
Daniel
^ permalink raw reply
* Re: [RFC PATCH ethtool] ethtool: better syntax for combinations of FEC modes
From: John W. Linville @ 2018-10-04 14:08 UTC (permalink / raw)
To: Edward Cree; +Cc: Michal Kubecek, netdev
In-Reply-To: <20181001185909.GD3281@tuxdriver.com>
Ping?
On Mon, Oct 01, 2018 at 02:59:10PM -0400, John W. Linville wrote:
> Is this patch still RFC?
>
> On Wed, Sep 19, 2018 at 05:06:25PM +0100, Edward Cree wrote:
> > Instead of commas, just have them as separate argvs.
> >
> > The parsing state machine might look heavyweight, but it makes it easy to add
> > more parameters later and distinguish parameter names from encoding names.
> >
> > Suggested-by: Michal Kubecek <mkubecek@suse.cz>
> > Signed-off-by: Edward Cree <ecree@solarflare.com>
> > ---
> > ethtool.8.in | 6 +++---
> > ethtool.c | 63 ++++++++++++++++------------------------------------------
> > test-cmdline.c | 10 +++++-----
> > 3 files changed, 25 insertions(+), 54 deletions(-)
> >
> > diff --git a/ethtool.8.in b/ethtool.8.in
> > index 414eaa1..7ea2cc0 100644
> > --- a/ethtool.8.in
> > +++ b/ethtool.8.in
> > @@ -390,7 +390,7 @@ ethtool \- query or control network driver and hardware settings
> > .B ethtool \-\-set\-fec
> > .I devname
> > .B encoding
> > -.BR auto | off | rs | baser [ , ...]
> > +.BR auto | off | rs | baser \ [...]
> > .
> > .\" Adjust lines (i.e. full justification) and hyphenate.
> > .ad
> > @@ -1120,11 +1120,11 @@ current FEC mode, the driver or firmware must take the link down
> > administratively and report the problem in the system logs for users to correct.
> > .RS 4
> > .TP
> > -.BR encoding\ auto | off | rs | baser [ , ...]
> > +.BR encoding\ auto | off | rs | baser \ [...]
> >
> > Sets the FEC encoding for the device. Combinations of options are specified as
> > e.g.
> > -.B auto,rs
> > +.B encoding auto rs
> > ; the semantics of such combinations vary between drivers.
> > .TS
> > nokeep;
> > diff --git a/ethtool.c b/ethtool.c
> > index 9997930..2f7e96b 100644
> > --- a/ethtool.c
> > +++ b/ethtool.c
> > @@ -4979,39 +4979,6 @@ static int fecmode_str_to_type(const char *str)
> > return 0;
> > }
> >
> > -/* Takes a comma-separated list of FEC modes, returns the bitwise OR of their
> > - * corresponding ETHTOOL_FEC_* constants.
> > - * Accepts repetitions (e.g. 'auto,auto') and trailing comma (e.g. 'off,').
> > - */
> > -static int parse_fecmode(const char *str)
> > -{
> > - int fecmode = 0;
> > - char buf[6];
> > -
> > - if (!str)
> > - return 0;
> > - while (*str) {
> > - size_t next;
> > - int mode;
> > -
> > - next = strcspn(str, ",");
> > - if (next >= 6) /* Bad mode, longest name is 5 chars */
> > - return 0;
> > - /* Copy into temp buffer and nul-terminate */
> > - memcpy(buf, str, next);
> > - buf[next] = 0;
> > - mode = fecmode_str_to_type(buf);
> > - if (!mode) /* Bad mode encountered */
> > - return 0;
> > - fecmode |= mode;
> > - str += next;
> > - /* Skip over ',' (but not nul) */
> > - if (*str)
> > - str++;
> > - }
> > - return fecmode;
> > -}
> > -
> > static int do_gfec(struct cmd_context *ctx)
> > {
> > struct ethtool_fecparam feccmd = { 0 };
> > @@ -5041,22 +5008,26 @@ static int do_gfec(struct cmd_context *ctx)
> >
> > static int do_sfec(struct cmd_context *ctx)
> > {
> > - char *fecmode_str = NULL;
> > + enum { ARG_NONE, ARG_ENCODING } state = ARG_NONE;
> > struct ethtool_fecparam feccmd;
> > - struct cmdline_info cmdline_fec[] = {
> > - { "encoding", CMDL_STR, &fecmode_str, &feccmd.fec},
> > - };
> > - int changed;
> > - int fecmode;
> > - int rv;
> > + int fecmode = 0, newmode;
> > + int rv, i;
> >
> > - parse_generic_cmdline(ctx, &changed, cmdline_fec,
> > - ARRAY_SIZE(cmdline_fec));
> > -
> > - if (!fecmode_str)
> > + for (i = 0; i < ctx->argc; i++) {
> > + if (!strcmp(ctx->argp[i], "encoding")) {
> > + state = ARG_ENCODING;
> > + continue;
> > + }
> > + if (state == ARG_ENCODING) {
> > + newmode = fecmode_str_to_type(ctx->argp[i]);
> > + if (!newmode)
> > + exit_bad_args();
> > + fecmode |= newmode;
> > + continue;
> > + }
> > exit_bad_args();
> > + }
> >
> > - fecmode = parse_fecmode(fecmode_str);
> > if (!fecmode)
> > exit_bad_args();
> >
> > @@ -5265,7 +5236,7 @@ static const struct option {
> > " [ all ]\n"},
> > { "--show-fec", 1, do_gfec, "Show FEC settings"},
> > { "--set-fec", 1, do_sfec, "Set FEC settings",
> > - " [ encoding auto|off|rs|baser ]\n"},
> > + " [ encoding auto|off|rs|baser [...]]\n"},
> > { "-h|--help", 0, show_usage, "Show this help" },
> > { "--version", 0, do_version, "Show version number" },
> > {}
> > diff --git a/test-cmdline.c b/test-cmdline.c
> > index 9c51cca..84630a5 100644
> > --- a/test-cmdline.c
> > +++ b/test-cmdline.c
> > @@ -268,12 +268,12 @@ static struct test_case {
> > { 1, "--set-eee devname advertise foo" },
> > { 1, "--set-fec devname" },
> > { 0, "--set-fec devname encoding auto" },
> > - { 0, "--set-fec devname encoding off," },
> > - { 0, "--set-fec devname encoding baser,rs" },
> > - { 0, "--set-fec devname encoding auto,auto," },
> > + { 0, "--set-fec devname encoding off" },
> > + { 0, "--set-fec devname encoding baser rs" },
> > + { 0, "--set-fec devname encoding auto auto" },
> > { 1, "--set-fec devname encoding foo" },
> > - { 1, "--set-fec devname encoding auto,foo" },
> > - { 1, "--set-fec devname encoding auto,," },
> > + { 1, "--set-fec devname encoding auto foo" },
> > + { 1, "--set-fec devname encoding none" },
> > { 1, "--set-fec devname auto" },
> > /* can't test --set-priv-flags yet */
> > { 0, "-h" },
> >
>
> --
> John W. Linville Someday the world will need a hero, and you
> linville@tuxdriver.com might be all we have. Be ready.
>
--
John W. Linville Someday the world will need a hero, and you
linville@tuxdriver.com might be all we have. Be ready.
^ permalink raw reply
* Re: [PATCH v2 00/29] at24: remove at24_platform_data
From: Arnd Bergmann @ 2018-10-04 13:58 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Florian Fainelli, Brian Norris, David Miller, Jonathan Corbet,
Sekhar Nori, Kevin Hilman, Russell King - ARM Linux, gregkh,
David Woodhouse, Boris Brezillon, Marek Vasut, Richard Weinberger,
Grygorii Strashko, Srinivas Kandagatla, naren.kernel,
Mauro Carvalho Chehab, Andrew Morton, Lukas Wunner, Dan Carpenter
In-Reply-To: <CAMRc=MfwM7vODsnuV9eKS4Uf7M5ac59u7ao7KvyaxKzPv5FRvw@mail.gmail.com>
On Thu, Oct 4, 2018 at 1:06 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> śr., 3 paź 2018 o 23:04 Florian Fainelli <f.fainelli@gmail.com> napisał(a):
> > On 10/3/2018 1:15 PM, Bartosz Golaszewski wrote:
> > > pt., 31 sie 2018 o 21:46 Brian Norris <computersforpeace@gmail.com> napisał(a):
> > >>
> > >> Hi,
> > >>
> > >> On Fri, Aug 10, 2018 at 10:04:57AM +0200, Bartosz Golaszewski wrote:
> > >>> Most boards use the EEPROM to store the MAC address. This series adds
> > >>> support for cell lookups to the nvmem framework, registers relevant
> > >>> cells for all users, adds nvmem support to eth_platform_get_mac_address(),
> > >>> converts davinci_emac driver to using it and replaces at24_platform_data
> > >>> with device properties.
> > >>
> > >> We already have:
> > >>
> > >> of_get_nvmem_mac_address() (which does exactly what you're adding,
> > >> except it's DT specific)
> > >> of_get_mac_address()
> > >> fwnode_get_mac_address()
> > >> device_get_mac_address()
> > >>
> > >> and now you've taught me that this exists too:
> > >>
> > >> eth_platform_get_mac_address()
> > >>
> > >> These mostly don't share code, and with your series, they'll start to
> > >> diverge even more as to what they support. Can you please help rectify
> > >> that, instead of widening the gap?
> > >>
> > >> For instance, you can delete most of eth_platform_get_mac_address() and
> > >> replace it with device_get_mac_address() [1]. And you could add your new
> > >> stuff to fwnode_get_mac_address().
> > >>
> > >> And important part to note here is that you code isn't just useful for
> > >> ethernet -- it could be useful for Wifi devices too. So IMO, sticking it
> > >> only in an "eth" function is the wrong move.
> > >>
> > >> Brian
> > >>
> > >> [1] arch_get_platform_mac_address() is the only part I wouldn't want to
> > >> replicate into a truly generic helper. The following should be a no-op
> > >> refactor, AIUI:
> > >>
> > >
> > > The only user of arch_get_platform_mac_address() is sparc. It returns
> > > an address that seems to be read from some kind of EEPROM. I'm not
> > > familiar with this arch though. I'm wondering if we could somehow
> > > seamlessly remove this call and then convert all users of
> > > eth_platform_get_mac_address() to using device_get_mac_address()?
> > >
> > > David: I couldn't find a place in sparc code where any ethernet device
> > > would be registered, so is there a chance that nobody is using it?
> >
> > SPARC uses a true Open Firmware implementation, so it would register
> > drivers through the CONFIG_OF infrastructure.
> > --
>
> I'm seeing that there are only six callers of
> eth_platform_get_mac_address() (the only function which calls
> arch_get_platform_mac_address()).
>
> Of these six callers four are intel ethernet drivers and two are usb
> ethernet adapter drivers.
>
> Is it even possible that sparc wants to get the mac address for a usb
> adapter from some memory chip? Maybe we *can* safely remove that
> function completely? That would allow us to simplify a lot of code.
The calls are not even that old, and clearly added intentionally for sparc,
see commit ba94272d08a7 ("i40e: use eth_platform_get_mac_address()")
which added the first one.
Before that commit, the driver did the same as a couple of sun
specific ones that access the idprom directly:
drivers/net/ethernet/aeroflex/greth.c:
macaddr[i] = (unsigned int) idprom->id_ethaddr[i];
drivers/net/ethernet/amd/sun3lance.c: dev->dev_addr[i] =
idprom->id_ethaddr[i];
drivers/net/ethernet/amd/sunlance.c: dev->dev_addr[i] =
idprom->id_ethaddr[i];
drivers/net/ethernet/broadcom/tg3.c: memcpy(dev->dev_addr,
idprom->id_ethaddr, ETH_ALEN);
drivers/net/ethernet/i825xx/sun3_82586.c: dev->dev_addr[i]
= idprom->id_ethaddr[i];
drivers/net/ethernet/sun/sunbmac.c: dev->dev_addr[i] =
idprom->id_ethaddr[i];
drivers/net/ethernet/sun/sungem.c: addr = idprom->id_ethaddr;
drivers/net/ethernet/sun/sunhme.c:
memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN);
drivers/net/ethernet/sun/sunhme.c:
memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN);
drivers/net/ethernet/sun/sunqe.c: memcpy(dev->dev_addr,
idprom->id_ethaddr, ETH_ALEN);
Arnd
^ permalink raw reply
* Re: [PATCH] net/usb: cancel pending work when unbinding smsc75xx
From: David Miller @ 2018-10-04 20:52 UTC (permalink / raw)
To: yuzhao; +Cc: steve.glendinning, netdev, linux-usb, linux-kernel
In-Reply-To: <20180928230430.125003-1-yuzhao@google.com>
From: Yu Zhao <yuzhao@google.com>
Date: Fri, 28 Sep 2018 17:04:30 -0600
> Cancel pending work before freeing smsc75xx private data structure
> during binding. This fixes the following crash in the driver:
>
> BUG: unable to handle kernel NULL pointer dereference at 0000000000000050
> IP: mutex_lock+0x2b/0x3f
> <snipped>
> Workqueue: events smsc75xx_deferred_multicast_write [smsc75xx]
> task: ffff8caa83e85700 task.stack: ffff948b80518000
> RIP: 0010:mutex_lock+0x2b/0x3f
> <snipped>
> Call Trace:
> smsc75xx_deferred_multicast_write+0x40/0x1af [smsc75xx]
> process_one_work+0x18d/0x2fc
> worker_thread+0x1a2/0x269
> ? pr_cont_work+0x58/0x58
> kthread+0xfa/0x10a
> ? pr_cont_work+0x58/0x58
> ? rcu_read_unlock_sched_notrace+0x48/0x48
> ret_from_fork+0x22/0x40
>
> Signed-off-by: Yu Zhao <yuzhao@google.com>
Applied and queued up for -stable.
^ permalink raw reply
* [PATCH] docs: net: typo fix in Documentation/networking/af_xdp.rst
From: Konrad Djimeli @ 2018-10-04 13:56 UTC (permalink / raw)
To: netdev; +Cc: Konrad Djimeli
Fix a simple typo: Completetion -> Completion
Signed-off-by: Konrad Djimeli <kdjimeli@igalia.com>
---
Documentation/networking/af_xdp.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/networking/af_xdp.rst b/Documentation/networking/af_xdp.rst
index ff929cfab4f4..e81162609ce3 100644
--- a/Documentation/networking/af_xdp.rst
+++ b/Documentation/networking/af_xdp.rst
@@ -159,7 +159,7 @@ log2(2048) LSB of the addr will be masked off, meaning that 2048, 2050
and 3000 refers to the same chunk.
-UMEM Completetion Ring
+UMEM Completion Ring
~~~~~~~~~~~~~~~~~~~~~~
The Completion Ring is used transfer ownership of UMEM frames from
--
2.17.1
^ permalink raw reply related
* Re: pull-request: mac80211 2018-10-04
From: David Miller @ 2018-10-04 20:48 UTC (permalink / raw)
To: johannes-cdvu00un1VgdHxzADdlk8Q
Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20181004194355.1364-1-johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
From: Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
Date: Thu, 4 Oct 2018 21:43:54 +0200
> I got three more fixes submitted, and am sending those to you
> now in the hope of still getting them in, although all of the
> bugs are actually older than the current cycle.
>
> If there's a release on Sunday then I guess I won't care, but
> if not then I'd appreciate if you could pull net into net-next
> as I have at least one patch there that otherwise doesn't apply
> (at least the result wouldn't compile). Not sure if you've seen
> anything from Greg indicating either way.
>
> Anyway, for now these fixes look good to me, so please pull or
> let me know if there's any problem.
Pulled, thanks!
^ permalink raw reply
* Re: [PATCH 1/9] PCI: sysfs: Export available PCIe bandwidth
From: Bjorn Helgaas @ 2018-10-04 20:45 UTC (permalink / raw)
To: Alex_Gagniuc
Cc: mr.nuke.me, linux-pci, bhelgaas, keith.busch, Austin.Bolen,
Shyam.Iyer, ariel.elior, everest-linux-l2, davem, michael.chan,
ganeshgr, jeffrey.t.kirsher, tariqt, saeedm, leon, jakub.kicinski,
dirk.vandermerwe, netdev, linux-kernel, intel-wired-lan,
linux-rdma, oss-drivers, stephen, mj, Alex Williamson
In-Reply-To: <f9fbc8babbb344eca593a3f8b0d096d9@ausx13mps321.AMER.DELL.COM>
[+cc Alex (VC mentioned below)]
On Wed, Oct 03, 2018 at 10:00:19PM +0000, Alex_Gagniuc@Dellteam.com wrote:
> On 10/03/2018 04:31 PM, Bjorn Helgaas wrote:
> > On Mon, Sep 03, 2018 at 01:02:28PM -0500, Alexandru Gagniuc wrote:
> >> For certain bandwidth-critical devices (e.g. multi-port network cards)
> >> it is useful to know the available bandwidth to the root complex. This
> >> information is only available via the system log, which doesn't
> >> account for link degradation after probing.
> >>
> >> With a sysfs attribute, we can computes the bandwidth on-demand, and
> >> will detect degraded links.
> >>
> >> Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
> >> ---
> >> drivers/pci/pci-sysfs.c | 13 +++++++++++++
> >> 1 file changed, 13 insertions(+)
> >>
> >> diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
> >> index 9ecfe13157c0..6658e927b1f5 100644
> >> --- a/drivers/pci/pci-sysfs.c
> >> +++ b/drivers/pci/pci-sysfs.c
> >> @@ -218,6 +218,18 @@ static ssize_t current_link_width_show(struct device *dev,
> >> }
> >> static DEVICE_ATTR_RO(current_link_width);
> >>
> >> +static ssize_t available_bandwidth_show(struct device *dev,
> >> + struct device_attribute *attr, char *buf)
> >> +{
> >> + struct pci_dev *pci_dev = to_pci_dev(dev);
> >> + u32 bw_avail;
> >> +
> >> + bw_avail = pcie_bandwidth_available(pci_dev, NULL, NULL, NULL);
> >> +
> >> + return sprintf(buf, "%u.%03u Gb/s\n", bw_avail / 1000, bw_avail % 1000);
> >> +}
> >> +static DEVICE_ATTR_RO(available_bandwidth);
> >
> > Help me understand this. We already have these sysfs attributes:
> >
> > max_link_speed # eg, 16 GT/s
> > max_link_width # eg, 8
> > current_link_speed # eg, 16 GT/s
> > current_link_width # eg, 8
> >
> > so I think the raw materials are already exposed.
> >
> > The benefits I see for this new file are that
> >
> > - pcie_bandwidth_available() does the work of traversing up the
> > tree, doing the computations (link width * speed, reduced by
> > encoding overhead), and finding the minimum, and
> >
> > - it re-traverses the path every time we look at it, while the
> > boot-time check is a one-time event.
> >
> > In principle this could all be done in user space with the attributes
> > that are already exported. There's some precedent for things like
> > this in lspci, e.g., "NUMA node" [1], and lspci might even be a more
> > user-friendly place for users to look for this, as opposed to
> > searching through sysfs.
>
> Parsing the endpoint to root port bandwidth is, in principle, possible
> from userspace. It's just that in practice it's very clumsy to do, and,
> as you pointed out, not that reliable.
I don't remember the reliability issue. Was that in a previous
conversation? AFAICT, using current_link_speed and current_link_width
should be as reliable as pcie_bandwidth_available() because they're
both based on reading PCI_EXP_LNKSTA.
This patch exposes only the available bandwidth, not the limiting
device, link speed, or link width. Maybe that extra information isn't
important in this context. Of course, it's easy to derive using
current_link_speed and current_link_width, but if we do that, there's
really no benefit to adding a new file.
Linux doesn't really support Virtual Channels (VC) yet, and I don't
know much about it, but it seems like Quality-of-Service features like
VC could affect this idea of available bandwidth.
Since we already expose the necessary information, and we might throw
additional things like VC into the mix, I'm a little hesitant about
adding things to sysfs because they're very difficult to change later.
> I understand it's not information that all users would jump in the air
> to know. However, it was important enough for certain use cases, that
> the kernel already has a very reliable way to calculate it.
>
> It seems to me that the most elegant way is to let the kernel tell us,
> since the kernel already has this facility. To quote one of the texts
> under Documentation/, it is an elegant way to "avoid reinventing kernel
> wheels in userspace".
>
> Alex
>
> > [1] https://git.kernel.org/pub/scm/utils/pciutils/pciutils.git/commit/?id=90ec4a6d0ae8
> >
> >> static ssize_t secondary_bus_number_show(struct device *dev,
> >> struct device_attribute *attr,
> >> char *buf)
> >> @@ -786,6 +798,7 @@ static struct attribute *pcie_dev_attrs[] = {
> >> &dev_attr_current_link_width.attr,
> >> &dev_attr_max_link_width.attr,
> >> &dev_attr_max_link_speed.attr,
> >> + &dev_attr_available_bandwidth.attr,
> >> NULL,
> >> };
> >>
> >> --
> >> 2.17.1
> >>
> >
>
^ permalink raw reply
* [PATCH net-next 9/9] rxrpc: Allow the reply time to be obtained on a client call
From: David Howells @ 2018-10-04 13:51 UTC (permalink / raw)
To: netdev; +Cc: dhowells, linux-afs, linux-kernel
In-Reply-To: <153866103101.27255.4710558896251591679.stgit@warthog.procyon.org.uk>
Allow the epoch value to be queried on a server connection. This is in the
rxrpc header of every packet for use in routing and is derived from the
client's state. It's also not supposed to change unless the client gets
restarted.
AFS can make use of this information to deduce whether a fileserver has
been restarted because the fileserver makes client calls to the filesystem
driver's cache manager to send notifications (ie. callback breaks) about
conflicting changes from other clients. These convey the fileserver's own
epoch value back to the filesystem.
Signed-off-by: David Howells <dhowells@redhat.com>
---
Documentation/networking/rxrpc.txt | 14 ++++++++++++++
include/net/af_rxrpc.h | 1 +
net/rxrpc/af_rxrpc.c | 14 ++++++++++++++
3 files changed, 29 insertions(+)
diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt
index 67879992b4c2..605e00cdd6be 100644
--- a/Documentation/networking/rxrpc.txt
+++ b/Documentation/networking/rxrpc.txt
@@ -1080,6 +1080,20 @@ The kernel interface functions are as follows:
successful, the timestamp will be stored into *_ts and true will be
returned; false will be returned otherwise.
+ (*) Get remote client epoch.
+
+ u32 rxrpc_kernel_get_epoch(struct socket *sock,
+ struct rxrpc_call *call)
+
+ This allows the epoch that's contained in packets of an incoming client
+ call to be queried. This value is returned. The function always
+ successful if the call is still in progress. It shouldn't be called once
+ the call has expired. Note that calling this on a local client call only
+ returns the local epoch.
+
+ This value can be used to determine if the remote client has been
+ restarted as it shouldn't change otherwise.
+
=======================
CONFIGURABLE PARAMETERS
diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h
index c4c912554dee..de587948042a 100644
--- a/include/net/af_rxrpc.h
+++ b/include/net/af_rxrpc.h
@@ -78,6 +78,7 @@ int rxrpc_kernel_retry_call(struct socket *, struct rxrpc_call *,
int rxrpc_kernel_check_call(struct socket *, struct rxrpc_call *,
enum rxrpc_call_completion *, u32 *);
u32 rxrpc_kernel_check_life(struct socket *, struct rxrpc_call *);
+u32 rxrpc_kernel_get_epoch(struct socket *, struct rxrpc_call *);
bool rxrpc_kernel_get_reply_time(struct socket *, struct rxrpc_call *,
ktime_t *);
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index 2fdd276f6842..013dbcb052e5 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -385,6 +385,20 @@ u32 rxrpc_kernel_check_life(struct socket *sock, struct rxrpc_call *call)
}
EXPORT_SYMBOL(rxrpc_kernel_check_life);
+/**
+ * rxrpc_kernel_get_epoch - Retrieve the epoch value from a call.
+ * @sock: The socket the call is on
+ * @call: The call to query
+ *
+ * Allow a kernel service to retrieve the epoch value from a service call to
+ * see if the client at the other end rebooted.
+ */
+u32 rxrpc_kernel_get_epoch(struct socket *sock, struct rxrpc_call *call)
+{
+ return call->conn->proto.epoch;
+}
+EXPORT_SYMBOL(rxrpc_kernel_get_epoch);
+
/**
* rxrpc_kernel_check_call - Check a call's state
* @sock: The socket the call is on
^ permalink raw reply related
* [PATCH net-next 6/9] rxrpc: Use IPv4 addresses throught the IPv6
From: David Howells @ 2018-10-04 13:51 UTC (permalink / raw)
To: netdev; +Cc: dhowells, linux-afs, linux-kernel
In-Reply-To: <153866103101.27255.4710558896251591679.stgit@warthog.procyon.org.uk>
AF_RXRPC opens an IPv6 socket through which to send and receive network
packets, both IPv6 and IPv4. It currently turns AF_INET addresses into
AF_INET-as-AF_INET6 addresses based on an assumption that this was
necessary; on further inspection of the code, however, it turns out that
the IPv6 code just farms packets aimed at AF_INET addresses out to the IPv4
code.
Fix AF_RXRPC to use AF_INET addresses directly when given them.
Fixes: 7b674e390e51 ("rxrpc: Fix IPv6 support")
Signed-off-by: David Howells <dhowells@redhat.com>
---
fs/afs/addr_list.c | 29 +++++++++++++++--------------
net/rxrpc/af_rxrpc.c | 3 ++-
net/rxrpc/conn_object.c | 5 +++--
net/rxrpc/peer_event.c | 12 +++++++-----
net/rxrpc/utils.c | 19 +++++--------------
5 files changed, 32 insertions(+), 36 deletions(-)
diff --git a/fs/afs/addr_list.c b/fs/afs/addr_list.c
index 89374de4785c..55a756c60746 100644
--- a/fs/afs/addr_list.c
+++ b/fs/afs/addr_list.c
@@ -232,7 +232,7 @@ struct afs_addr_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry)
*/
void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
{
- struct sockaddr_in6 *p;
+ struct sockaddr_rxrpc *srx;
u32 addr = ntohl(xdr);
int i;
@@ -240,9 +240,9 @@ void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
return;
for (i = 0; i < alist->nr_ipv4; i++) {
- struct sockaddr_in6 *a = &alist->addrs[i].transport.sin6;
- u32 a_addr = ntohl(a->sin6_addr.s6_addr32[3]);
- u16 a_port = ntohs(a->sin6_port);
+ struct sockaddr_in *a = &alist->addrs[i].transport.sin;
+ u32 a_addr = ntohl(a->sin_addr.s_addr);
+ u16 a_port = ntohs(a->sin_port);
if (addr == a_addr && port == a_port)
return;
@@ -257,12 +257,11 @@ void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
alist->addrs + i,
sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
- p = &alist->addrs[i].transport.sin6;
- p->sin6_port = htons(port);
- p->sin6_addr.s6_addr32[0] = 0;
- p->sin6_addr.s6_addr32[1] = 0;
- p->sin6_addr.s6_addr32[2] = htonl(0xffff);
- p->sin6_addr.s6_addr32[3] = xdr;
+ srx = &alist->addrs[i];
+ srx->transport_len = sizeof(srx->transport.sin);
+ srx->transport.sin.sin_family = AF_INET;
+ srx->transport.sin.sin_port = htons(port);
+ srx->transport.sin.sin_addr.s_addr = xdr;
alist->nr_ipv4++;
alist->nr_addrs++;
}
@@ -272,7 +271,7 @@ void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
*/
void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port)
{
- struct sockaddr_in6 *p;
+ struct sockaddr_rxrpc *srx;
int i, diff;
if (alist->nr_addrs >= alist->max_addrs)
@@ -296,9 +295,11 @@ void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port)
alist->addrs + i,
sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
- p = &alist->addrs[i].transport.sin6;
- p->sin6_port = htons(port);
- memcpy(&p->sin6_addr, xdr, 16);
+ srx = &alist->addrs[i];
+ srx->transport_len = sizeof(srx->transport.sin6);
+ srx->transport.sin6.sin6_family = AF_INET6;
+ srx->transport.sin6.sin6_port = htons(port);
+ memcpy(&srx->transport.sin6.sin6_addr, xdr, 16);
alist->nr_addrs++;
}
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index ac44d8afffb1..2fdd276f6842 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -97,7 +97,8 @@ static int rxrpc_validate_address(struct rxrpc_sock *rx,
srx->transport_len > len)
return -EINVAL;
- if (srx->transport.family != rx->family)
+ if (srx->transport.family != rx->family &&
+ srx->transport.family == AF_INET && rx->family != AF_INET6)
return -EAFNOSUPPORT;
switch (srx->transport.family) {
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c
index 885dae829f4a..fb856a1ccda8 100644
--- a/net/rxrpc/conn_object.c
+++ b/net/rxrpc/conn_object.c
@@ -89,8 +89,9 @@ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local,
if (rxrpc_extract_addr_from_skb(local, &srx, skb) < 0)
goto not_found;
- /* We may have to handle mixing IPv4 and IPv6 */
- if (srx.transport.family != local->srx.transport.family) {
+ if (srx.transport.family != local->srx.transport.family &&
+ (srx.transport.family == AF_INET &&
+ local->srx.transport.family != AF_INET6)) {
pr_warn_ratelimited("AF_RXRPC: Protocol mismatch %u not %u\n",
srx.transport.family,
local->srx.transport.family);
diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c
index f3e6fc670da2..81a7869325a6 100644
--- a/net/rxrpc/peer_event.c
+++ b/net/rxrpc/peer_event.c
@@ -47,6 +47,8 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local,
*/
switch (srx->transport.family) {
case AF_INET:
+ srx->transport_len = sizeof(srx->transport.sin);
+ srx->transport.family = AF_INET;
srx->transport.sin.sin_port = serr->port;
switch (serr->ee.ee_origin) {
case SO_EE_ORIGIN_ICMP:
@@ -70,20 +72,20 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local,
#ifdef CONFIG_AF_RXRPC_IPV6
case AF_INET6:
- srx->transport.sin6.sin6_port = serr->port;
switch (serr->ee.ee_origin) {
case SO_EE_ORIGIN_ICMP6:
_net("Rx ICMP6");
+ srx->transport.sin6.sin6_port = serr->port;
memcpy(&srx->transport.sin6.sin6_addr,
skb_network_header(skb) + serr->addr_offset,
sizeof(struct in6_addr));
break;
case SO_EE_ORIGIN_ICMP:
_net("Rx ICMP on v6 sock");
- srx->transport.sin6.sin6_addr.s6_addr32[0] = 0;
- srx->transport.sin6.sin6_addr.s6_addr32[1] = 0;
- srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
- memcpy(srx->transport.sin6.sin6_addr.s6_addr + 12,
+ srx->transport_len = sizeof(srx->transport.sin);
+ srx->transport.family = AF_INET;
+ srx->transport.sin.sin_port = serr->port;
+ memcpy(&srx->transport.sin.sin_addr,
skb_network_header(skb) + serr->addr_offset,
sizeof(struct in_addr));
break;
diff --git a/net/rxrpc/utils.c b/net/rxrpc/utils.c
index e801171fa351..017adaa54e90 100644
--- a/net/rxrpc/utils.c
+++ b/net/rxrpc/utils.c
@@ -25,20 +25,11 @@ int rxrpc_extract_addr_from_skb(struct rxrpc_local *local,
switch (ntohs(skb->protocol)) {
case ETH_P_IP:
- if (local->srx.transport.family == AF_INET6) {
- srx->transport_type = SOCK_DGRAM;
- srx->transport_len = sizeof(srx->transport.sin6);
- srx->transport.sin6.sin6_family = AF_INET6;
- srx->transport.sin6.sin6_port = udp_hdr(skb)->source;
- srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
- srx->transport.sin6.sin6_addr.s6_addr32[3] = ip_hdr(skb)->saddr;
- } else {
- srx->transport_type = SOCK_DGRAM;
- srx->transport_len = sizeof(srx->transport.sin);
- srx->transport.sin.sin_family = AF_INET;
- srx->transport.sin.sin_port = udp_hdr(skb)->source;
- srx->transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
- }
+ srx->transport_type = SOCK_DGRAM;
+ srx->transport_len = sizeof(srx->transport.sin);
+ srx->transport.sin.sin_family = AF_INET;
+ srx->transport.sin.sin_port = udp_hdr(skb)->source;
+ srx->transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
return 0;
#ifdef CONFIG_AF_RXRPC_IPV6
^ permalink raw reply related
* [PATCH bpf-next] bpf: emit audit messages upon successful prog load and unload
From: Daniel Borkmann @ 2018-10-04 13:50 UTC (permalink / raw)
To: ast; +Cc: netdev, brouer, Daniel Borkmann, Jiri Olsa
Allow for audit messages to be emitted upon BPF program load and
unload for having a timeline of events. The load itself is in
syscall context, so additional info about the process initiating
the BPF prog creation can be logged and later directly correlated
to the unload event.
The only info really needed from BPF side is the globally unique
prog ID where then audit user space tooling can query / dump all
info needed about the specific BPF program right upon load event
and enrich the record, thus these changes needed here can be kept
small and non-intrusive to the core.
Raw example output:
# auditctl -D
# auditctl -a always,exit -F arch=x86_64 -S bpf
# ausearch --start recent -m 1332
[...]
----
time->Thu Oct 4 15:29:11 2018
type=PROCTITLE msg=audit(1538659751.047:174): proctitle=2E2F746573745F76657269666965720030
type=SYSCALL msg=audit(1538659751.047:174): arch=c000003e syscall=321 success=yes exit=3
a0=5 a1=7ffe6f46dec0 a2=48 a3=40bd40 items=0 ppid=1892 pid=2020 auid=1000 uid=0 gid=0
euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=3 comm="test_verifier"
exe="/home/foo/test_verifier" key=(null)
type=UNKNOWN[1332] msg=audit(1538659751.047:174): auid=1000 uid=0 gid=0 ses=3 pid=2020
comm="test_verifier" exe="/home/foo/test_verifier" prog-id=2 event=LOAD
----
time->Thu Oct 4 15:29:11 2018
type=UNKNOWN[1332] msg=audit(1538659751.047:176): prog-id=2 event=UNLOAD
----
[...]
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Cc: Jiri Olsa <jolsa@kernel.org>
---
include/linux/audit.h | 3 +++
include/uapi/linux/audit.h | 1 +
kernel/auditsc.c | 2 +-
kernel/bpf/syscall.c | 31 +++++++++++++++++++++++++++++++
4 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 9334fbe..9495c84 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -152,6 +152,7 @@ extern void audit_log_key(struct audit_buffer *ab,
extern void audit_log_link_denied(const char *operation);
extern void audit_log_lost(const char *message);
+extern void audit_log_task(struct audit_buffer *ab);
extern int audit_log_task_context(struct audit_buffer *ab);
extern void audit_log_task_info(struct audit_buffer *ab,
struct task_struct *tsk);
@@ -198,6 +199,8 @@ static inline void audit_log_key(struct audit_buffer *ab, char *key)
{ }
static inline void audit_log_link_denied(const char *string)
{ }
+static inline void audit_log_task(struct audit_buffer *ab)
+{ }
static inline int audit_log_task_context(struct audit_buffer *ab)
{
return 0;
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 818ae69..a749881 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -114,6 +114,7 @@
#define AUDIT_REPLACE 1329 /* Replace auditd if this packet unanswerd */
#define AUDIT_KERN_MODULE 1330 /* Kernel Module events */
#define AUDIT_FANOTIFY 1331 /* Fanotify access decision */
+#define AUDIT_BPF 1332 /* BPF subsystem */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index b2d1f04..6c498d9 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2427,7 +2427,7 @@ void __audit_fanotify(unsigned int response)
AUDIT_FANOTIFY, "resp=%u", response);
}
-static void audit_log_task(struct audit_buffer *ab)
+void audit_log_task(struct audit_buffer *ab)
{
kuid_t auid, uid;
kgid_t gid;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 5742df2..83afbd1 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -31,6 +31,7 @@
#include <linux/timekeeping.h>
#include <linux/ctype.h>
#include <linux/nospec.h>
+#include <linux/audit.h>
#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \
(map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
@@ -1011,6 +1012,34 @@ static void free_used_maps(struct bpf_prog_aux *aux)
kfree(aux->used_maps);
}
+enum bpf_event {
+ BPF_EVENT_LOAD,
+ BPF_EVENT_UNLOAD,
+};
+
+static const char * const bpf_event_audit_str[] = {
+ [BPF_EVENT_LOAD] = "LOAD",
+ [BPF_EVENT_UNLOAD] = "UNLOAD",
+};
+
+static void bpf_audit_prog(const struct bpf_prog *prog, enum bpf_event event)
+{
+ bool has_task_context = event == BPF_EVENT_LOAD;
+ struct audit_buffer *ab;
+
+ if (audit_enabled == AUDIT_OFF)
+ return;
+ ab = audit_log_start(audit_context(), GFP_ATOMIC, AUDIT_BPF);
+ if (unlikely(!ab))
+ return;
+ if (has_task_context)
+ audit_log_task(ab);
+ audit_log_format(ab, "%sprog-id=%u event=%s",
+ has_task_context ? " " : "",
+ prog->aux->id, bpf_event_audit_str[event]);
+ audit_log_end(ab);
+}
+
int __bpf_prog_charge(struct user_struct *user, u32 pages)
{
unsigned long memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
@@ -1112,6 +1141,7 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)
static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
{
if (atomic_dec_and_test(&prog->aux->refcnt)) {
+ bpf_audit_prog(prog, BPF_EVENT_UNLOAD);
/* bpf_prog_free_id() must be called first */
bpf_prog_free_id(prog, do_idr_lock);
bpf_prog_kallsyms_del_all(prog);
@@ -1452,6 +1482,7 @@ static int bpf_prog_load(union bpf_attr *attr)
}
bpf_prog_kallsyms_add(prog);
+ bpf_audit_prog(prog, BPF_EVENT_LOAD);
return err;
free_used_maps:
--
2.9.5
^ permalink raw reply related
* Re: [PATCH 2/2] can: tcan4x5x: Add tcan4x5x driver to the kernel
From: Dan Murphy @ 2018-10-04 20:26 UTC (permalink / raw)
To: Wolfgang Grandegger, mkl, davem; +Cc: linux-can, netdev, linux-kernel
In-Reply-To: <45621ef7-bc87-3d1e-efca-e6387fdebf9a@grandegger.com>
Wolfgang
On 09/26/2018 12:54 PM, Wolfgang Grandegger wrote:
> Hello,
>
> I wonder why you do not extend the existing MCAN driver by implementing
> an interface to access the hardware. Would that be feasible?
>
I have created a m_can_core code base that can be used by other hardware that
have special needs.
So I have created the m_can_core, m_can and the tcan4x5x drivers.
I can RFC the code to see if this is what is expected.
It is not 100% working but it is close enough for a directional call.
Dan
> Wolfgang.
>
> Am 26.09.2018 um 19:40 schrieb Dan Murphy:
>> bump
>>
>> On 09/10/2018 03:12 PM, Dan Murphy wrote:
>>> Add the TCAN4x5x SPI CAN driver. This device
>>> uses the Bosch MCAN IP core along with a SPI
>>> interface map. The register and data are
>>> 32 bits wide.
>>>
>>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
>>> ---
>>> drivers/net/can/spi/Kconfig | 5 +
>>> drivers/net/can/spi/Makefile | 1 +
>>> drivers/net/can/spi/tcan4x5x.c | 1206 ++++++++++++++++++++++++++++++++
>>> drivers/net/can/spi/tcan4x5x.h | 109 +++
>>> 4 files changed, 1321 insertions(+)
>>> create mode 100644 drivers/net/can/spi/tcan4x5x.c
>>> create mode 100644 drivers/net/can/spi/tcan4x5x.h
>>>
>>> diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
>>> index 8f2e0dd7b756..8cac6ce37506 100644
>>> --- a/drivers/net/can/spi/Kconfig
>>> +++ b/drivers/net/can/spi/Kconfig
>>> @@ -13,4 +13,9 @@ config CAN_MCP251X
>>> ---help---
>>> Driver for the Microchip MCP251x SPI CAN controllers.
>>>
>>> +config CAN_TCAN4X5X
>>> + tristate "Texas Instruments TCAN4X5X SPI CAN controllers"
>>> + depends on HAS_DMA
>>> + ---help---
>>> + Driver for the Texas Instruments TCAN4X5X SPI CAN controllers.
>>> endmenu
>>> diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
>>> index f59fa3731073..8ecaace7a920 100644
>>> --- a/drivers/net/can/spi/Makefile
>>> +++ b/drivers/net/can/spi/Makefile
>>> @@ -5,3 +5,4 @@
>>>
>>> obj-$(CONFIG_CAN_HI311X) += hi311x.o
>>> obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
>>> +obj-$(CONFIG_CAN_TCAN4X5X) += tcan4x5x.o
>>> diff --git a/drivers/net/can/spi/tcan4x5x.c b/drivers/net/can/spi/tcan4x5x.c
>>> new file mode 100644
>>> index 000000000000..ca3753efe35a
>>> --- /dev/null
>>> +++ b/drivers/net/can/spi/tcan4x5x.c
>>> @@ -0,0 +1,1206 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +// SPI to CAN driver for the Texas Instruments TCAN4x5x
>>> +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
>>> +
>>> +#include <linux/can/core.h>
>>> +#include <linux/can/dev.h>
>>> +#include <linux/can/led.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/completion.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/device.h>
>>> +#include <linux/dma-mapping.h>
>>> +#include <linux/freezer.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/io.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/netdevice.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/spi/spi.h>
>>> +#include <linux/uaccess.h>
>>> +
>>> +#include <linux/regulator/consumer.h>
>>> +#include <linux/gpio/consumer.h>
>>> +
>>> +#include "tcan4x5x.h"
>>> +
>>> +#define DEVICE_NAME "tcan4x5x"
>>> +#define TCAN4X5X_EXT_CLK_DEF 40000000
>>> +
>>> +#define TCAN4X5X_CLEAR_ALL_INT 0xffffffff
>>> +#define TCAN4X5X_SET_ALL_INT 0xffffffff
>>> +
>>> +#define TCAN4X5X_TX_ECHO_SKB_MAX 1
>>> +#define TCAN4X5X_DATA_PKT_OFF 2
>>> +#define TCAN4X5X_WRITE_CMD (0x61 << 24)
>>> +#define TCAN4X5X_READ_CMD (0x41 << 24)
>>> +
>>> +#define TCAN4X5X_SID_SHIFT 18
>>> +#define TCAN4X5X_DLC_SHIFT 16
>>> +
>>> +#define TCAN4X5X_ESI_SHIFT 31
>>> +#define TCAN4X5X_XTD_SHIFT 30
>>> +#define TCAN4X5X_RTR_SHIFT 29
>>> +#define TCAN4X5X_FDF_SHIFT 21
>>> +#define TCAN4X5X_BRS_SHIFT 20
>>> +#define TCAN4X5X_DLC_SHIFT 16
>>> +
>>> +#define TCAN4X5X_ESI_MASK BIT(31)
>>> +#define TCAN4X5X_XTD_MASK BIT(30)
>>> +#define TCAN4X5X_RTR_MASK BIT(29)
>>> +
>>> +#define TCAN4X5X_DLC_MASK 0xf0000
>>> +#define TCAN4X5X_SW_RESET BIT(2)
>>> +
>>> +#define TCAN4X5X_MODE_SEL_MASK (BIT(7) | BIT(6))
>>> +#define TCAN4X5X_MODE_SLEEP 0x00
>>> +#define TCAN4X5X_MODE_STANDBY BIT(6)
>>> +#define TCAN4X5X_MODE_NORMAL BIT(7)
>>> +#define TCAN4X5X_MCAN_CONFIGURED BIT(5)
>>> +#define TCAN4X5X_WATCHDOG_EN BIT(3)
>>> +#define TCAN4X5X_WD_60_MS_TIMER 0
>>> +#define TCAN4X5X_WD_600_MS_TIMER BIT(28)
>>> +#define TCAN4X5X_WD_3_S_TIMER BIT(29)
>>> +#define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29))
>>> +
>>> +/* Nominal Bit Timing & Prescaler Register */
>>> +#define TCAN4X5X_NSJW_SHIFT 25
>>> +#define TCAN4X5X_NBRP_SHIFT 16
>>> +#define TCAN4X5X_NTSEG1_SHIFT 8
>>> +
>>> +#define TCAN4X5X_TDCR_TDCO_SHIFT 8
>>> +
>>> +/* Data Bit Timing & Prescaler Register (DBTP) */
>>> +#define DBTP_TDC BIT(23)
>>> +#define DBTP_DBRP_SHIFT 16
>>> +#define DBTP_DBRP_MASK (0x1f << DBTP_DBRP_SHIFT)
>>> +#define DBTP_DTSEG1_SHIFT 8
>>> +#define DBTP_DTSEG1_MASK (0x1f << DBTP_DTSEG1_SHIFT)
>>> +#define DBTP_DTSEG2_SHIFT 4
>>> +#define DBTP_DTSEG2_MASK (0xf << DBTP_DTSEG2_SHIFT)
>>> +#define DBTP_DSJW_SHIFT 0
>>> +#define DBTP_DSJW_MASK (0xf << DBTP_DSJW_SHIFT)
>>> +
>>> +#define TCAN4x5x_QUEUE_LVL_MASK 0x1f
>>> +#define TCAN4x5x_QUEUE_IDX_SHIFT 16
>>> +#define TCAN4x5x_QUEUE_IDX_MASK 0x1f00
>>> +
>>> +#define TCAN4X5X_CANBUSNOM_INT_EN BIT(14)
>>> +
>>> +#define TCAN4X5X_NUM_TX_BUF 5
>>> +#define TCAN4X5X_TX_QUEUE_SHIFT 24
>>> +#define TCAN4X5X_TX_NDTB_SHIFT 16
>>> +#define TCAN4X5X_TX_BUF_START 0x324
>>> +
>>> +#define TCAN4X5X_NUM_RX_BUF 3
>>> +#define TCAN4X5X_RX_WATER_MARK 2
>>> +#define TCAN4X5X_RX_WATER_MARK_SHIFT 24
>>> +#define TCAN4X5X_RX_FIFO_SZ_SHIFT 16
>>> +#define TCAN4X5X_RX_BUF_START 0x4
>>> +
>>> +#define TCAN4X5X_RX_F1DS_SHIFT 4
>>> +#define TCAN4X5X_RX_RBDS_SHIFT 8
>>> +
>>> +#define TCAN4X5X_RX_FIFO0_MESSAGE BIT(0)
>>> +#define TCAN4X5X_RX_FIFO1_MESSAGE BIT(4)
>>> +#define TCAN4X5X_RX_BUFFER_MESSAGE BIT(19)
>>> +#define TCAN4X5X_RX_INDEX_MASK 0x3f00
>>> +#define TCAN4X5X_RX_INDEX_SHIFT 8
>>> +
>>> +#define TCAN4X5X_RX_ADDR_OFFSET 0x8000
>>> +#define TCAN4X5X_RX_BUF_ADDR_OFFSET 0x8100
>>> +#define TCAN4X5X_RX_ADDR_MASK 0xffff
>>> +
>>> +#define TCAN4X5X_ERR_PROTOCOL_MASK 0x7
>>> +#define TCAN4X5X_ERR_STUFERR 0x1
>>> +#define TCAN4X5X_ERR_FRMERR 0x2
>>> +#define TCAN4X5X_ERR_ACKERR 0x3
>>> +#define TCAN4X5X_ERR_BIT1ERR 0x4
>>> +#define TCAN4X5X_ERR_BIT0ERR 0x5
>>> +#define TCAN4X5X_ERR_CRCERR 0x6
>>> +
>>> +/* Interrupt bits */
>>> +#define TCAN4X5X_CANBUSTERMOPEN_INT_EN BIT(30)
>>> +#define TCAN4X5X_CANHCANL_INT_EN BIT(29)
>>> +#define TCAN4X5X_CANHBAT_INT_EN BIT(28)
>>> +#define TCAN4X5X_CANLGND_INT_EN BIT(27)
>>> +#define TCAN4X5X_CANBUSOPEN_INT_EN BIT(26)
>>> +#define TCAN4X5X_CANBUSGND_INT_EN BIT(25)
>>> +#define TCAN4X5X_CANBUSBAT_INT_EN BIT(24)
>>> +#define TCAN4X5X_UVSUP_INT_EN BIT(22)
>>> +#define TCAN4X5X_UVIO_INT_EN BIT(21)
>>> +#define TCAN4X5X_TSD_INT_EN BIT(19)
>>> +#define TCAN4X5X_ECCERR_INT_EN BIT(16)
>>> +#define TCAN4X5X_CANINT_INT_EN BIT(15)
>>> +#define TCAN4X5X_LWU_INT_EN BIT(14)
>>> +#define TCAN4X5X_CANSLNT_INT_EN BIT(10)
>>> +#define TCAN4X5X_CANDOM_INT_EN BIT(8)
>>> +#define TCAN4X5X_CANBUS_ERR_INT_EN BIT(5)
>>> +#define TCAN4X5X_BUS_FAULT BIT(4)
>>> +#define TCAN4X5X_MCAN_INT BIT(1)
>>> +#define TCAN4X5X_ENABLE_ALL_INT (TCAN4X5X_MCAN_INT | \
>>> + TCAN4X5X_BUS_FAULT | \
>>> + TCAN4X5X_CANBUS_ERR_INT_EN | \
>>> + TCAN4X5X_CANINT_INT_EN)
>>> +
>>> +/* MCAN Interrupt bits */
>>> +#define TCAN4X5X_MCAN_IR_ARA BIT(29)
>>> +#define TCAN4X5X_MCAN_IR_PED BIT(28)
>>> +#define TCAN4X5X_MCAN_IR_PEA BIT(27)
>>> +#define TCAN4X5X_MCAN_IR_WD BIT(26)
>>> +#define TCAN4X5X_MCAN_IR_BO BIT(25)
>>> +#define TCAN4X5X_MCAN_IR_EW BIT(24)
>>> +#define TCAN4X5X_MCAN_IR_EP BIT(23)
>>> +#define TCAN4X5X_MCAN_IR_ELO BIT(22)
>>> +#define TCAN4X5X_MCAN_IR_BEU BIT(21)
>>> +#define TCAN4X5X_MCAN_IR_BEC BIT(20)
>>> +#define TCAN4X5X_MCAN_IR_DRX BIT(19)
>>> +#define TCAN4X5X_MCAN_IR_TOO BIT(18)
>>> +#define TCAN4X5X_MCAN_IR_MRAF BIT(17)
>>> +#define TCAN4X5X_MCAN_IR_TSW BIT(16)
>>> +#define TCAN4X5X_MCAN_IR_TEFL BIT(15)
>>> +#define TCAN4X5X_MCAN_IR_TEFF BIT(14)
>>> +#define TCAN4X5X_MCAN_IR_TEFW BIT(13)
>>> +#define TCAN4X5X_MCAN_IR_TEFN BIT(12)
>>> +#define TCAN4X5X_MCAN_IR_TFE BIT(11)
>>> +#define TCAN4X5X_MCAN_IR_TCF BIT(10)
>>> +#define TCAN4X5X_MCAN_IR_TC BIT(9)
>>> +#define TCAN4X5X_MCAN_IR_HPM BIT(8)
>>> +#define TCAN4X5X_MCAN_IR_RF1L BIT(7)
>>> +#define TCAN4X5X_MCAN_IR_RF1F BIT(6)
>>> +#define TCAN4X5X_MCAN_IR_RF1W BIT(5)
>>> +#define TCAN4X5X_MCAN_IR_RF1N BIT(4)
>>> +#define TCAN4X5X_MCAN_IR_RF0L BIT(3)
>>> +#define TCAN4X5X_MCAN_IR_RF0F BIT(2)
>>> +#define TCAN4X5X_MCAN_IR_RF0W BIT(1)
>>> +#define TCAN4X5X_MCAN_IR_RF0N BIT(0)
>>> +#define TCAN4X5X_ENABLE_MCAN_INT (TCAN4X5X_MCAN_IR_TC | \
>>> + TCAN4X5X_MCAN_IR_RF0N | \
>>> + TCAN4X5X_MCAN_IR_RF1N | \
>>> + TCAN4X5X_MCAN_IR_RF0F | \
>>> + TCAN4X5X_MCAN_IR_RF1F)
>>> +
>>> +/* CCR bits */
>>> +#define TCAN4X5X_CCCR_NISO_BOSCH BIT(15)
>>> +#define TCAN4X5X_CCCR_TXP BIT(15)
>>> +#define TCAN4X5X_CCCR_EFBI BIT(13)
>>> +#define TCAN4X5X_CCCR_PXHD_DIS BIT(12)
>>> +#define TCAN4X5X_CCCR_BRSE BIT(9)
>>> +#define TCAN4X5X_CCCR_FDOE BIT(8)
>>> +#define TCAN4X5X_CCCR_TEST BIT(7)
>>> +#define TCAN4X5X_CCCR_DAR_DIS BIT(6)
>>> +#define TCAN4X5X_CCCR_MON BIT(5)
>>> +#define TCAN4X5X_CCCR_CSR BIT(4)
>>> +#define TCAN4X5X_CCCR_CSA BIT(3)
>>> +#define TCAN4X5X_CCCR_ASM BIT(2)
>>> +#define TCAN4X5X_CCCR_CCE BIT(1)
>>> +#define TCAN4X5X_CCCR_INIT BIT(0)
>>> +
>>> +#define TCAN4X5X_EINT0 BIT(0)
>>> +#define TCAN4X5X_EINT1 BIT(1)
>>> +
>>> +struct tcan4x5x_rx_regs {
>>> + u32 fifo_status_reg;
>>> + u32 fifo_config_reg;
>>> + u32 fifo_ack_reg;
>>> + u32 rx_buf_shift;
>>> +};
>>> +
>>> +struct tcan4x5x_rx_regs tcan4x5x_fifo_regs[] = {
>>> + { TCAN4X5X_MCAN_RXF0S, TCAN4X5X_MCAN_RXF0C, TCAN4X5X_MCAN_RXF0A, 0},
>>> + { TCAN4X5X_MCAN_RXF1S, TCAN4X5X_MCAN_RXF1C, TCAN4X5X_MCAN_RXF1A, 4},
>>> + { TCAN4X5X_MCAN_NDAT1, TCAN4X5X_MCAN_RXBC, TCAN4X5X_MCAN_NDAT1, 8},
>>> +};
>>> +
>>> +enum tcan4x5x_data_size {
>>> + TCAN4X5X_8_BYTE = 0,
>>> + TCAN4X5X_12_BYTE,
>>> + TCAN4X5X_16_BYTE,
>>> + TCAN4X5X_20_BYTE,
>>> + TCAN4X5X_24_BYTE,
>>> + TCAN4X5X_32_BYTE,
>>> + TCAN4X5X_48_BYTE,
>>> + TCAN4X5X_64_BYTE,
>>> +};
>>> +
>>> +static const struct can_bittiming_const tcan4x5x_bittiming_const = {
>>> + .name = DEVICE_NAME,
>>> + .tseg1_min = 2,
>>> + .tseg1_max = 31,
>>> + .tseg2_min = 2,
>>> + .tseg2_max = 16,
>>> + .sjw_max = 16,
>>> + .brp_min = 1,
>>> + .brp_max = 32,
>>> + .brp_inc = 1,
>>> +};
>>> +
>>> +static const struct can_bittiming_const tcan4x5x_data_bittiming_const = {
>>> + .name = DEVICE_NAME,
>>> + .tseg1_min = 1,
>>> + .tseg1_max = 32,
>>> + .tseg2_min = 1,
>>> + .tseg2_max = 16,
>>> + .sjw_max = 16,
>>> + .brp_min = 1,
>>> + .brp_max = 32,
>>> + .brp_inc = 1,
>>> +};
>>> +
>>> +static void tcan4x5x_clean(struct net_device *net)
>>> +{
>>> + struct tcan4x5x_priv *priv = netdev_priv(net);
>>> +
>>> + if (priv->tx_skb || priv->tx_len)
>>> + net->stats.tx_errors++;
>>> + if (priv->tx_skb)
>>> + dev_kfree_skb(priv->tx_skb);
>>> + if (priv->tx_len)
>>> + can_free_echo_skb(priv->net, 0);
>>> +
>>> + priv->tx_skb = NULL;
>>> + priv->tx_len = 0;
>>> +}
>>> +
>>> +static int regmap_spi_gather_write(void *context, const void *reg,
>>> + size_t reg_len, const void *val,
>>> + size_t val_len)
>>> +{
>>> + struct device *dev = context;
>>> + struct spi_device *spi = to_spi_device(dev);
>>> + u32 addr;
>>> + struct spi_message m;
>>> + struct spi_transfer t[2] = {{ .tx_buf = &addr, .len = 4, .cs_change = 0,},
>>> + { .tx_buf = val, .len = val_len, },};
>>> +
>>> + addr = TCAN4X5X_WRITE_CMD | (*((u16 *)reg) << 8) | val_len >> 2;
>>> +
>>> + spi_message_init(&m);
>>> + spi_message_add_tail(&t[0], &m);
>>> + spi_message_add_tail(&t[1], &m);
>>> +
>>> + return spi_sync(spi, &m);
>>> +}
>>> +
>>> +static int tcan4x5x_regmap_write(void *context, const void *data, size_t count)
>>> +{
>>> + u16 *reg = (u16 *)(data);
>>> + const u32 *val = data + 2;
>>> +
>>> + return regmap_spi_gather_write(context, reg, 2, val, count - 2);
>>> +}
>>> +
>>> +static int regmap_spi_async_write(void *context,
>>> + const void *reg, size_t reg_len,
>>> + const void *val, size_t val_len,
>>> + struct regmap_async *a)
>>> +{
>>> + return -ENOTSUPP;
>>> +}
>>> +
>>> +static struct regmap_async *regmap_spi_async_alloc(void)
>>> +{
>>> + return NULL;
>>> +}
>>> +
>>> +static int tcan4x5x_regmap_read(void *context,
>>> + const void *reg, size_t reg_size,
>>> + void *val, size_t val_size)
>>> +{
>>> + struct device *dev = context;
>>> + struct spi_device *spi = to_spi_device(dev);
>>> +
>>> + u32 addr = TCAN4X5X_READ_CMD | (*((u16 *)reg) << 8) | val_size >> 2;
>>> +
>>> + return spi_write_then_read(spi, &addr, 4, val, val_size);
>>> +}
>>> +
>>> +static struct regmap_bus tcan4x5x_bus = {
>>> + .write = tcan4x5x_regmap_write,
>>> + .gather_write = regmap_spi_gather_write,
>>> + .async_write = regmap_spi_async_write,
>>> + .async_alloc = regmap_spi_async_alloc,
>>> + .read = tcan4x5x_regmap_read,
>>> + .read_flag_mask = 0x00,
>>> + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
>>> + .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
>>> +};
>>> +
>>> +static uint8_t tcan4x5x_dlc_conv(uint8_t input)
>>> +{
>>> + const static u8 lookup[7] = {12, 16, 20, 24, 32, 48, 64};
>>> +
>>> + if (input < 9)
>>> + return input;
>>> +
>>> + if (input < 16)
>>> + return lookup[(unsigned int)(input - 9)];
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static uint8_t tcan4x5x_txrxesc_value(uint8_t input)
>>> +{
>>> + const u8 lookup[8] = {8, 12, 16, 20, 24, 32, 48, 64};
>>> + return lookup[(unsigned int)(input & 0x07)];
>>> +}
>>> +
>>> +static void tcan4x5x_hw_tx(struct tcan4x5x_priv *tcan4x5x)
>>> +{
>>> + u32 sid, eid, exide, rtr, brs, esi, fdf, xtd, data_len;
>>> + u32 mcan_address, mcan_tx_element_sz;
>>> + int queue_stat, queue_lvl, queue_idx;
>>> + struct canfd_frame *fd_frame;
>>> + struct can_frame *frame;
>>> + int tx_element_sz, i, temp;
>>> + canid_t frame_id;
>>> + u8 dlc_len;
>>> +
>>> + regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXFQS, &queue_stat);
>>> + queue_lvl = queue_stat & TCAN4x5x_QUEUE_LVL_MASK;
>>> + queue_idx = (queue_stat & TCAN4x5x_QUEUE_IDX_MASK) >> TCAN4x5x_QUEUE_IDX_SHIFT;
>>> +
>>> + if (tcan4x5x->tx_skb->len == CAN_MTU) {
>>> + fd_frame = NULL;
>>> + frame = (struct can_frame *)tcan4x5x->tx_skb->data;
>>> + frame_id = frame->can_id;
>>> + dlc_len = frame->can_dlc;
>>> + data_len = ((dlc_len % 4) + dlc_len) / 4;
>>> + brs = 0;
>>> + } else if (tcan4x5x->tx_skb->len == CANFD_MTU) {
>>> + frame = NULL;
>>> + fd_frame = (struct canfd_frame *)tcan4x5x->tx_skb->data;
>>> + frame_id = fd_frame->can_id;
>>> + dlc_len = fd_frame->len;
>>> + data_len = ((dlc_len % 4) + dlc_len) / 4;
>>> + brs = fd_frame->flags & CANFD_BRS;
>>> + esi = fd_frame->flags & CANFD_ESI;
>>> + fdf = 1;
>>> + } else {
>>> + return;
>>> + }
>>> +
>>> + eid = frame_id & CAN_EFF_MASK;
>>> + rtr = (frame_id & CAN_RTR_FLAG) ? 1 : 0;
>>> +
>>> + exide = (frame_id & CAN_EFF_FLAG) ? 1 : 0;
>>> + if (exide) {
>>> + sid = frame_id & CAN_EFF_MASK;
>>> + xtd = 1;
>>> + } else {
>>> + sid = (frame_id & CAN_SFF_MASK) << TCAN4X5X_SID_SHIFT;
>>> + xtd = 0;
>>> + }
>>> +
>>> + regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBC, &mcan_address);
>>> +
>>> + mcan_address = (mcan_address & 0xffff) + TCAN4X5X_MRAM_START;
>>> + temp = (uint8_t)((mcan_address >> 24) & 0x3F);
>>> +
>>> + tx_element_sz = temp > 32 ? 32 : temp;
>>> + temp = (uint8_t)((mcan_address >> 16) & 0x3F);
>>> +
>>> + tx_element_sz += temp > 32 ? 32 : temp;
>>> + mcan_address += ((uint32_t)tx_element_sz * queue_idx);
>>> + regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXESC, &mcan_tx_element_sz);
>>> + tx_element_sz = tcan4x5x_txrxesc_value(mcan_tx_element_sz & 0x07) + 8;
>>> + mcan_address += ((uint32_t)tx_element_sz * 0);
>>> +
>>> + tx_element_sz = (tcan4x5x_dlc_conv(dlc_len & 0x0F) + 8) >> 2;
>>> + if (tcan4x5x_dlc_conv(dlc_len & 0x0F) % 4)
>>> + tx_element_sz += 1;
>>> +
>>> + tcan4x5x->spi_tx_buf[0] = esi << TCAN4X5X_ESI_SHIFT |
>>> + xtd << TCAN4X5X_XTD_SHIFT |
>>> + rtr << TCAN4X5X_RTR_SHIFT | sid;
>>> +
>>> + tcan4x5x->spi_tx_buf[1] = fdf << TCAN4X5X_FDF_SHIFT |
>>> + brs << TCAN4X5X_BRS_SHIFT | dlc_len << TCAN4X5X_DLC_SHIFT;
>>> +
>>> + if (tcan4x5x->tx_skb->len == CAN_MTU)
>>> + memcpy(tcan4x5x->spi_tx_buf + TCAN4X5X_DATA_PKT_OFF,
>>> + frame->data, dlc_len);
>>> + else
>>> + memcpy(tcan4x5x->spi_tx_buf + TCAN4X5X_DATA_PKT_OFF,
>>> + fd_frame->data, dlc_len);
>>> +
>>> + for (i = dlc_len + 1; i < TCAN4X5X_BUF_LEN / 4; i++)
>>> + tcan4x5x->spi_tx_buf[i] = 0;
>>> +
>>> + regmap_bulk_write(tcan4x5x->regmap, mcan_address, tcan4x5x->spi_tx_buf,
>>> + TCAN4X5X_BUF_LEN);
>>> +
>>> + regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBAR, (1 << (queue_idx)));
>>> +}
>>> +
>>> +int tcan4x5x_hw_rx(struct tcan4x5x_priv *tcan4x5x)
>>> +{
>>> + u32 queue_idx, fifo_idx, fifo_start_addr, rx_buf_size, msg_type;
>>> + u32 data_buffer[TCAN4X5X_BUF_LEN] = {0x0};
>>> + u32 rx_header[2] = {0x0};
>>> + struct tcan4x5x_rx_regs *buffer_regs;
>>> + struct canfd_frame *fd_frame;
>>> + int dlc_len, data_len;
>>> + struct sk_buff *skb;
>>> +
>>> + skb = alloc_canfd_skb(tcan4x5x->net, &fd_frame);
>>> + if (!skb) {
>>> + dev_err(&tcan4x5x->spi->dev, "cannot allocate RX skb\n");
>>> + tcan4x5x->net->stats.rx_dropped++;
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG, &msg_type);
>>> + if (msg_type & TCAN4X5X_RX_FIFO0_MESSAGE) {
>>> + buffer_regs = &tcan4x5x_fifo_regs[0];
>>> + } else if (msg_type & TCAN4X5X_RX_FIFO1_MESSAGE) {
>>> + buffer_regs = &tcan4x5x_fifo_regs[1];
>>> + } else if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE) {
>>> + buffer_regs = &tcan4x5x_fifo_regs[2];
>>> + } else {
>>> + buffer_regs = NULL;
>>> + return -EINVAL;
>>> + }
>>> +
>>> + rx_buf_size = TCAN4X5X_BUF_LEN;
>>> +
>>> + /* Determine which FIFO needs service */
>>> + regmap_read(tcan4x5x->regmap, buffer_regs->fifo_status_reg, &fifo_idx);
>>> + if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE)
>>> + queue_idx = fifo_idx - 1;
>>> + else
>>> + queue_idx = (TCAN4X5X_RX_INDEX_MASK & fifo_idx) >> TCAN4X5X_RX_INDEX_SHIFT;
>>> +
>>> + /* Calculate the FIFO start address to service */
>>> + regmap_read(tcan4x5x->regmap, buffer_regs->fifo_config_reg, &fifo_start_addr);
>>> + fifo_start_addr = (TCAN4X5X_RX_ADDR_MASK & fifo_start_addr);
>>> + if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE)
>>> + fifo_start_addr = fifo_start_addr + TCAN4X5X_RX_BUF_ADDR_OFFSET +
>>> + (rx_buf_size * queue_idx);
>>> + else
>>> + fifo_start_addr = fifo_start_addr + TCAN4X5X_RX_ADDR_OFFSET +
>>> + (rx_buf_size * queue_idx);
>>> +
>>> + regmap_bulk_read(tcan4x5x->regmap, fifo_start_addr, rx_header, 2);
>>> +
>>> + dlc_len = (rx_header[1] & TCAN4X5X_DLC_MASK) >> TCAN4X5X_DLC_SHIFT;
>>> + if (dlc_len <= 8)
>>> + data_len = dlc_len;
>>> + else
>>> + data_len = tcan4x5x_txrxesc_value(dlc_len);
>>> +
>>> + regmap_bulk_read(tcan4x5x->regmap, fifo_start_addr + 8,
>>> + data_buffer, data_len / 4);
>>> +
>>> + /* Acknowledge receipt of the data */
>>> + regmap_write(tcan4x5x->regmap, buffer_regs->fifo_ack_reg, queue_idx);
>>> +
>>> + if (rx_header[0] & TCAN4X5X_XTD_MASK) {
>>> + fd_frame->can_id = CAN_EFF_FLAG;
>>> + fd_frame->can_id |= (rx_header[0] & CAN_EFF_MASK);
>>> + } else {
>>> + fd_frame->can_id |= ((rx_header[0] >> TCAN4X5X_SID_SHIFT) &
>>> + CAN_SFF_MASK);
>>> + }
>>> +
>>> + if (rx_header[0] & TCAN4X5X_RTR_MASK)
>>> + fd_frame->can_id |= CAN_RTR_FLAG;
>>> +
>>> + if (rx_header[0] & TCAN4X5X_ESI_MASK) {
>>> + fd_frame->can_id |= CAN_ERR_FLAG;
>>> + fd_frame->flags |= CANFD_ESI;
>>> + netdev_dbg(tcan4x5x->net, "ESI Error\n");
>>> + }
>>> +
>>> + fd_frame->len = data_len;
>>> + memcpy(fd_frame->data, data_buffer, fd_frame->len);
>>> +
>>> + tcan4x5x->net->stats.rx_packets++;
>>> + tcan4x5x->net->stats.rx_bytes += fd_frame->len;
>>> +
>>> + can_led_event(tcan4x5x->net, CAN_LED_EVENT_RX);
>>> + netif_rx_ni(skb);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void tcan4x5x_sleep(struct spi_device *spi)
>>> +{
>>> + struct tcan4x5x_priv *tcan4x5x = spi_get_drvdata(spi);
>>> +
>>> + regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
>>> + TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_STANDBY);
>>> +}
>>> +
>>> +static int tcan4x5x_reset(struct net_device *net)
>>> +{
>>> + struct tcan4x5x_priv *tcan4x5x = netdev_priv(net);
>>> +
>>> + if (tcan4x5x->reset_gpio) {
>>> + gpiod_set_value_cansleep(tcan4x5x->reset_gpio, 1);
>>> + udelay(10);
>>> + gpiod_set_value_cansleep(tcan4x5x->reset_gpio, 0);
>>> + } else {
>>> + regmap_write(tcan4x5x->regmap, TCAN4X5X_CONFIG,
>>> + TCAN4X5X_SW_RESET);
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int tcan4x5x_power_enable(struct regulator *reg, int enable)
>>> +{
>>> + if (IS_ERR_OR_NULL(reg))
>>> + return 0;
>>> +
>>> + if (enable)
>>> + return regulator_enable(reg);
>>> + else
>>> + return regulator_disable(reg);
>>> +}
>>> +
>>> +static irqreturn_t tcan4x5x_can_ist(int irq, void *dev_id)
>>> +{
>>> + struct tcan4x5x_priv *tcan4x5x = dev_id;
>>> + struct spi_device *spi = tcan4x5x->spi;
>>> + struct net_device *net = tcan4x5x->net;
>>> + enum can_state new_state;
>>> + int intf, eflag, mcan_intf;
>>> +
>>> + mutex_lock(&tcan4x5x->tcan4x5x_lock);
>>> +
>>> + regmap_read(tcan4x5x->regmap, TCAN4X5X_INT_FLAGS, &intf);
>>> + if (intf & TCAN4X5X_MCAN_INT)
>>> + tcan4x5x_hw_rx(tcan4x5x);
>>> +
>>> + regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG, &mcan_intf);
>>> +
>>> + regmap_read(tcan4x5x->regmap, TCAN4X5X_STATUS, &eflag);
>>> + /* Update can state */
>>> + if (eflag & TCAN4X5X_MCAN_IR_BO)
>>> + new_state = CAN_STATE_BUS_OFF;
>>> + else if (eflag & TCAN4X5X_MCAN_IR_EP)
>>> + new_state = CAN_STATE_ERROR_PASSIVE;
>>> + else if (eflag & TCAN4X5X_MCAN_IR_EW)
>>> + new_state = CAN_STATE_ERROR_WARNING;
>>> + else
>>> + new_state = CAN_STATE_ERROR_ACTIVE;
>>> +
>>> + if (new_state != tcan4x5x->can.state) {
>>> + struct can_frame *cf;
>>> + struct sk_buff *skb;
>>> + enum can_state rx_state, tx_state;
>>> + u32 error_count;
>>> +
>>> + skb = alloc_can_err_skb(net, &cf);
>>> + if (!skb)
>>> + goto ist_out;
>>> +
>>> + regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_ECR, &error_count);
>>> + cf->data[6] = error_count & 0xff;
>>> + cf->data[7] = error_count & 0x7f00 >> 8;
>>> + tx_state = cf->data[6] >= cf->data[7] ? new_state : 0;
>>> + rx_state = cf->data[6] <= cf->data[7] ? new_state : 0;
>>> + can_change_state(net, cf, tx_state, rx_state);
>>> + netif_rx_ni(skb);
>>> +
>>> + if (new_state == CAN_STATE_BUS_OFF) {
>>> + can_bus_off(net);
>>> + if (tcan4x5x->can.restart_ms == 0) {
>>> + tcan4x5x->force_quit = 1;
>>> + tcan4x5x_sleep(spi);
>>> + goto ist_out;
>>> + }
>>> + }
>>> + }
>>> +
>>> + /* Update bus errors */
>>> + if ((intf & TCAN4X5X_BUS_FAULT) &&
>>> + (tcan4x5x->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) {
>>> + struct can_frame *cf;
>>> + struct sk_buff *skb;
>>> + u32 psr_err, error_count;
>>> +
>>> + /* Check for protocol errors */
>>> + regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_PSR, &psr_err);
>>> + if (psr_err & TCAN4X5X_ERR_PROTOCOL_MASK) {
>>> + skb = alloc_can_err_skb(net, &cf);
>>> + if (!skb)
>>> + goto ist_out;
>>> +
>>> + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
>>> + tcan4x5x->can.can_stats.bus_error++;
>>> + tcan4x5x->net->stats.rx_errors++;
>>> + if (psr_err & TCAN4X5X_ERR_BIT0ERR)
>>> + cf->data[2] |= CAN_ERR_PROT_BIT0;
>>> + else if (psr_err & TCAN4X5X_ERR_BIT1ERR)
>>> + cf->data[2] |= CAN_ERR_PROT_BIT1;
>>> + else if (psr_err & TCAN4X5X_ERR_FRMERR)
>>> + cf->data[2] |= CAN_ERR_PROT_FORM;
>>> + else if (psr_err & TCAN4X5X_ERR_STUFERR)
>>> + cf->data[2] |= CAN_ERR_PROT_STUFF;
>>> + else if (psr_err & TCAN4X5X_ERR_CRCERR)
>>> + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
>>> + else if (psr_err & TCAN4X5X_ERR_ACKERR)
>>> + cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
>>> +
>>> + regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_ECR,
>>> + &error_count);
>>> + cf->data[6] = error_count & 0xff;
>>> + cf->data[7] = error_count & 0x7f00 >> 8;
>>> + netdev_dbg(tcan4x5x->net, "Bus Error\n");
>>> + netif_rx_ni(skb);
>>> + }
>>> + }
>>> +
>>> + if (mcan_intf & TCAN4X5X_MCAN_IR_TC) {
>>> + net->stats.tx_packets++;
>>> + net->stats.tx_bytes += tcan4x5x->tx_len - 1;
>>> + can_led_event(net, CAN_LED_EVENT_TX);
>>> + if (tcan4x5x->tx_len) {
>>> + can_get_echo_skb(net, 0);
>>> + tcan4x5x->tx_len = 0;
>>> + }
>>> + netif_wake_queue(net);
>>> + }
>>> +
>>> +ist_out:
>>> + regmap_write(tcan4x5x->regmap, TCAN4X5X_INT_FLAGS, TCAN4X5X_CLEAR_ALL_INT);
>>> + regmap_write(tcan4x5x->regmap, TCAN4X5X_STATUS, TCAN4X5X_CLEAR_ALL_INT);
>>> + regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG,
>>> + TCAN4X5X_CLEAR_ALL_INT);
>>> +
>>> + mutex_unlock(&tcan4x5x->tcan4x5x_lock);
>>> + return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int tcan4x5x_do_set_bittiming(struct net_device *net)
>>> +{
>>> + struct tcan4x5x_priv *priv = netdev_priv(net);
>>> + struct can_bittiming *bt = &priv->can.bittiming;
>>> + struct can_bittiming *dbt = &priv->can.data_bittiming;
>>> + u16 brp, sjw, tseg1, tseg2;
>>> + int ret;
>>> + u32 val;
>>> +
>>> + brp = bt->brp - 1;
>>> + sjw = bt->sjw - 1;
>>> + tseg1 = bt->prop_seg + bt->phase_seg1 - 1;
>>> + tseg2 = bt->phase_seg2 - 1;
>>> + val = (brp << TCAN4X5X_NBRP_SHIFT) | (sjw << TCAN4X5X_NSJW_SHIFT) |
>>> + (tseg1 << TCAN4X5X_NTSEG1_SHIFT) | tseg2;
>>> +
>>> + ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_NBTP, val);
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
>>> + val = 0;
>>> + brp = dbt->brp - 1;
>>> + sjw = dbt->sjw - 1;
>>> + tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
>>> + tseg2 = dbt->phase_seg2 - 1;
>>> +
>>> + /* TDC is only needed for bitrates beyond 2.5 MBit/s.
>>> + * This is mentioned in the "Bit Time Requirements for CAN FD"
>>> + * paper presented at the International CAN Conference 2013
>>> + */
>>> + if (dbt->bitrate > 2500000) {
>>> + u32 tdco, ssp;
>>> +
>>> + /* Use the same value of secondary sampling point
>>> + * as the data sampling point
>>> + */
>>> + ssp = dbt->sample_point;
>>> +
>>> + /* Equation based on Bosch's M_CAN User Manual's
>>> + * Transmitter Delay Compensation Section
>>> + */
>>> + tdco = (priv->can.clock.freq / 1000) *
>>> + ssp / dbt->bitrate;
>>> +
>>> + /* Max valid TDCO value is 127 */
>>> + if (tdco > 127) {
>>> + netdev_warn(net, "TDCO value of %u is beyond maximum. Using maximum possible value\n",
>>> + tdco);
>>> + tdco = 127;
>>> + }
>>> +
>>> + val |= DBTP_TDC;
>>> + ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_TDCR,
>>> + tdco << TCAN4X5X_TDCR_TDCO_SHIFT);
>>> + if (ret)
>>> + return -EIO;
>>> + }
>>> +
>>> + val |= (brp << DBTP_DBRP_SHIFT) |
>>> + (sjw << DBTP_DSJW_SHIFT) |
>>> + (tseg1 << DBTP_DTSEG1_SHIFT) |
>>> + (tseg2 << DBTP_DTSEG2_SHIFT);
>>> +
>>> + ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_DBTP, val);
>>> + }
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int tcan4x5x_setup(struct spi_device *spi)
>>> +{
>>> + struct tcan4x5x_priv *tcan4x5x = spi_get_drvdata(spi);
>>> + int start_reg = TCAN4X5X_MRAM_START;
>>> + int end_reg = start_reg + TCAN4X5X_MRAM_SIZE;
>>> + int ret;
>>> +
>>> + ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_REG,
>>> + TCAN4X5X_CLEAR_ALL_INT);
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_EN,
>>> + TCAN4X5X_ENABLE_MCAN_INT);
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_CCCR,
>>> + TCAN4X5X_CCCR_INIT | TCAN4X5X_CCCR_CCE);
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_CCCR,
>>> + TCAN4X5X_CCCR_INIT | TCAN4X5X_CCCR_CCE |
>>> + TCAN4X5X_CCCR_FDOE | TCAN4X5X_CCCR_BRSE);
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + ret = tcan4x5x_do_set_bittiming(tcan4x5x->net);
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXESC,
>>> + TCAN4X5X_64_BYTE);
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBC,
>>> + (TCAN4X5X_NUM_TX_BUF << TCAN4X5X_TX_QUEUE_SHIFT |
>>> + TCAN4X5X_TX_BUF_START));
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_RXF0C,
>>> + (TCAN4X5X_RX_WATER_MARK << TCAN4X5X_RX_WATER_MARK_SHIFT |
>>> + TCAN4X5X_NUM_RX_BUF << TCAN4X5X_RX_FIFO_SZ_SHIFT |
>>> + TCAN4X5X_RX_BUF_START));
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_RXESC,
>>> + (TCAN4X5X_64_BYTE << TCAN4X5X_RX_RBDS_SHIFT |
>>> + TCAN4X5X_64_BYTE << TCAN4X5X_RX_F1DS_SHIFT |
>>> + TCAN4X5X_64_BYTE));
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBTIE,
>>> + TCAN4X5X_SET_ALL_INT);
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> +
>>> + ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
>>> + TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL);
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_ILE, TCAN4X5X_EINT0);
>>> + if (ret)
>>> + return -EIO;
>>> +
>>> + /* Zero out the MCAN buffers */
>>> + while (start_reg < end_reg) {
>>> + regmap_write(tcan4x5x->regmap, start_reg, 0);
>>> + start_reg += 4;
>>> + }
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static void tcan4x5x_tx_work_handler(struct work_struct *ws)
>>> +{
>>> + struct tcan4x5x_priv *tcan4x5x = container_of(ws, struct tcan4x5x_priv,
>>> + tx_work);
>>> + struct net_device *net = tcan4x5x->net;
>>> + struct can_frame *frame;
>>> +
>>> + mutex_lock(&tcan4x5x->tcan4x5x_lock);
>>> + if (tcan4x5x->tx_skb) {
>>> + if (tcan4x5x->can.state == CAN_STATE_BUS_OFF) {
>>> + tcan4x5x_clean(net);
>>> + } else {
>>> + frame = (struct can_frame *)tcan4x5x->tx_skb->data;
>>> + tcan4x5x_hw_tx(tcan4x5x);
>>> + tcan4x5x->tx_len = 1 + frame->can_dlc;
>>> + can_put_echo_skb(tcan4x5x->tx_skb, net, 0);
>>> + tcan4x5x->tx_skb = NULL;
>>> + }
>>> + }
>>> + mutex_unlock(&tcan4x5x->tcan4x5x_lock);
>>> +}
>>> +
>>> +static void tcan4x5x_restart_work_handler(struct work_struct *ws)
>>> +{
>>> + struct tcan4x5x_priv *tcan4x5x = container_of(ws, struct tcan4x5x_priv,
>>> + restart_work);
>>> + struct spi_device *spi = tcan4x5x->spi;
>>> + struct net_device *net = tcan4x5x->net;
>>> +
>>> + mutex_lock(&tcan4x5x->tcan4x5x_lock);
>>> + if (tcan4x5x->after_suspend) {
>>> + tcan4x5x_reset(net);
>>> + tcan4x5x_setup(spi);
>>> + if (tcan4x5x->after_suspend & AFTER_SUSPEND_RESTART) {
>>> + tcan4x5x_setup(spi);
>>> + } else if (tcan4x5x->after_suspend & AFTER_SUSPEND_UP) {
>>> + netif_device_attach(net);
>>> + tcan4x5x_clean(net);
>>> + tcan4x5x_setup(spi);
>>> + netif_wake_queue(net);
>>> + } else {
>>> + tcan4x5x_sleep(spi);
>>> + }
>>> + tcan4x5x->after_suspend = 0;
>>> + tcan4x5x->force_quit = 0;
>>> + }
>>> +
>>> + if (tcan4x5x->restart_tx) {
>>> + tcan4x5x->restart_tx = 0;
>>> + tcan4x5x_reset(net);
>>> + tcan4x5x_clean(net);
>>> + tcan4x5x_setup(spi);
>>> + netif_wake_queue(net);
>>> + }
>>> + mutex_unlock(&tcan4x5x->tcan4x5x_lock);
>>> +}
>>> +
>>> +static int tcan4x5x_open(struct net_device *net)
>>> +{
>>> + struct tcan4x5x_priv *priv = netdev_priv(net);
>>> + struct spi_device *spi = priv->spi;
>>> + unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_LOW;
>>> + int ret;
>>> +
>>> + ret = open_candev(net);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + mutex_lock(&priv->tcan4x5x_lock);
>>> + tcan4x5x_power_enable(priv->power, 1);
>>> +
>>> + priv->force_quit = 0;
>>> + priv->tx_skb = NULL;
>>> + priv->tx_len = 0;
>>> +
>>> + ret = request_threaded_irq(priv->irq, NULL, tcan4x5x_can_ist,
>>> + flags, DEVICE_NAME, priv);
>>> + if (ret) {
>>> + dev_err(&spi->dev, "failed to acquire irq %d %i\n",
>>> + priv->irq, ret);
>>> + goto out_close;
>>> + }
>>> +
>>> + priv->wq = alloc_workqueue("tcan4x5x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
>>> + 0);
>>> + if (!priv->wq) {
>>> + ret = -ENOMEM;
>>> + goto out_free_irq;
>>> + }
>>> +
>>> + INIT_WORK(&priv->tx_work, tcan4x5x_tx_work_handler);
>>> + INIT_WORK(&priv->restart_work, tcan4x5x_restart_work_handler);
>>> +
>>> + priv->spi_tx_buf = devm_kzalloc(&spi->dev, TCAN4X5X_BUF_LEN,
>>> + GFP_KERNEL);
>>> + if (!priv->spi_tx_buf) {
>>> + ret = -ENOMEM;
>>> + goto out_free_wq;
>>> + }
>>> +
>>> + priv->spi_rx_buf = devm_kzalloc(&spi->dev, TCAN4X5X_BUF_LEN,
>>> + GFP_KERNEL);
>>> + if (!priv->spi_rx_buf) {
>>> + ret = -ENOMEM;
>>> + goto out_free_wq;
>>> + }
>>> +
>>> + if (priv->wake_gpio)
>>> + gpiod_set_value_cansleep(priv->wake_gpio, 1);
>>> +
>>> + ret = tcan4x5x_reset(net);
>>> + if (ret)
>>> + goto out_free_wq;
>>> +
>>> + ret = tcan4x5x_setup(spi);
>>> + if (ret)
>>> + goto out_free_wq;
>>> +
>>> + can_led_event(net, CAN_LED_EVENT_OPEN);
>>> + netif_wake_queue(net);
>>> + mutex_unlock(&priv->tcan4x5x_lock);
>>> +
>>> + return 0;
>>> +
>>> + out_free_wq:
>>> + destroy_workqueue(priv->wq);
>>> + out_free_irq:
>>> + free_irq(priv->irq, priv);
>>> + tcan4x5x_sleep(spi);
>>> + out_close:
>>> + tcan4x5x_power_enable(priv->power, 0);
>>> + close_candev(net);
>>> + mutex_unlock(&priv->tcan4x5x_lock);
>>> + return ret;
>>> +}
>>> +
>>> +static int tcan4x5x_stop(struct net_device *net)
>>> +{
>>> + struct tcan4x5x_priv *priv = netdev_priv(net);
>>> + struct spi_device *spi = priv->spi;
>>> +
>>> + close_candev(net);
>>> +
>>> + priv->force_quit = 1;
>>> + free_irq(priv->irq, priv);
>>> + destroy_workqueue(priv->wq);
>>> + priv->wq = NULL;
>>> +
>>> + mutex_lock(&priv->tcan4x5x_lock);
>>> +
>>> + priv->can.state = CAN_STATE_STOPPED;
>>> + tcan4x5x_sleep(spi);
>>> + tcan4x5x_power_enable(priv->power, 0);
>>> +
>>> + mutex_unlock(&priv->tcan4x5x_lock);
>>> +
>>> + can_led_event(net, CAN_LED_EVENT_STOP);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static netdev_tx_t tcan4x5x_hard_start_xmit(struct sk_buff *skb,
>>> + struct net_device *net)
>>> +{
>>> + struct tcan4x5x_priv *priv = netdev_priv(net);
>>> + struct spi_device *spi = priv->spi;
>>> +
>>> + if (priv->tx_skb || priv->tx_len) {
>>> + dev_warn(&spi->dev, "hard_xmit called while tx busy\n");
>>> + return NETDEV_TX_BUSY;
>>> + }
>>> +
>>> + if (can_dropped_invalid_skb(net, skb))
>>> + return NETDEV_TX_OK;
>>> +
>>> + netif_stop_queue(net);
>>> + priv->tx_skb = skb;
>>> + queue_work(priv->wq, &priv->tx_work);
>>> +
>>> + return NETDEV_TX_OK;
>>> +}
>>> +
>>> +static int tcan4x5x_do_set_mode(struct net_device *net, enum can_mode mode)
>>> +{
>>> + struct tcan4x5x_priv *priv = netdev_priv(net);
>>> +
>>> + switch (mode) {
>>> + case CAN_MODE_START:
>>> + tcan4x5x_clean(net);
>>> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
>>> + priv->restart_tx = 1;
>>> + queue_work(priv->wq, &priv->restart_work);
>>> + break;
>>> + default:
>>> + return -EOPNOTSUPP;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct net_device_ops tcan4x5x_netdev_ops = {
>>> + .ndo_open = tcan4x5x_open,
>>> + .ndo_stop = tcan4x5x_stop,
>>> + .ndo_start_xmit = tcan4x5x_hard_start_xmit,
>>> + .ndo_change_mtu = can_change_mtu,
>>> +};
>>> +
>>> +static int tcan4x5x_parse_config(struct tcan4x5x_priv *tcan4x5x)
>>> +{
>>> + tcan4x5x->reset_gpio = devm_gpiod_get_optional(&tcan4x5x->spi->dev,
>>> + "reset", GPIOD_OUT_LOW);
>>> + if (IS_ERR(tcan4x5x->reset_gpio))
>>> + tcan4x5x->reset_gpio = NULL;
>>> +
>>> + tcan4x5x->wake_gpio = devm_gpiod_get_optional(&tcan4x5x->spi->dev,
>>> + "wake-up", GPIOD_OUT_LOW);
>>> + if (IS_ERR(tcan4x5x->wake_gpio))
>>> + tcan4x5x->wake_gpio = NULL;
>>> +
>>> + tcan4x5x->interrupt_gpio = devm_gpiod_get(&tcan4x5x->spi->dev,
>>> + "data-ready", GPIOD_IN);
>>> + if (IS_ERR(tcan4x5x->interrupt_gpio)) {
>>> + dev_err(&tcan4x5x->spi->dev, "data-ready gpio not defined\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + tcan4x5x->irq = gpiod_to_irq(tcan4x5x->interrupt_gpio);
>>> +
>>> + tcan4x5x->power = devm_regulator_get_optional(&tcan4x5x->spi->dev,
>>> + "vsup");
>>> + if (PTR_ERR(tcan4x5x->power) == -EPROBE_DEFER)
>>> + return -EPROBE_DEFER;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct regmap_config tcan4x5x_regmap = {
>>> + .reg_bits = 16,
>>> + .val_bits = 32,
>>> + .cache_type = REGCACHE_NONE,
>>> + .max_register = TCAN4X5X_MAX_REGISTER,
>>> +};
>>> +
>>> +static int tcan4x5x_can_probe(struct spi_device *spi)
>>> +{
>>> + struct net_device *net;
>>> + struct tcan4x5x_priv *priv;
>>> + struct clk *clk;
>>> + int freq, ret;
>>> +
>>> + clk = devm_clk_get(&spi->dev, NULL);
>>> + if (IS_ERR(clk)) {
>>> + dev_err(&spi->dev, "no CAN clock source defined\n");
>>> + freq = TCAN4X5X_EXT_CLK_DEF;
>>> + } else {
>>> + freq = clk_get_rate(clk);
>>> + }
>>> +
>>> + /* Sanity check */
>>> + if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF)
>>> + return -ERANGE;
>>> +
>>> + /* Allocate can/net device */
>>> + net = alloc_candev(sizeof(*priv), TCAN4X5X_TX_ECHO_SKB_MAX);
>>> + if (!net)
>>> + return -ENOMEM;
>>> +
>>> + if (!IS_ERR(clk)) {
>>> + ret = clk_prepare_enable(clk);
>>> + if (ret)
>>> + goto out_free;
>>> + }
>>> +
>>> + net->netdev_ops = &tcan4x5x_netdev_ops;
>>> + net->flags |= IFF_ECHO;
>>> + net->mtu = CANFD_MTU;
>>> +
>>> + priv = netdev_priv(net);
>>> + priv->can.bittiming_const = &tcan4x5x_bittiming_const;
>>> + priv->can.data_bittiming_const = &tcan4x5x_data_bittiming_const;
>>> + priv->can.do_set_mode = tcan4x5x_do_set_mode;
>>> + priv->can.clock.freq = freq;
>>> + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
>>> + CAN_CTRLMODE_LISTENONLY |
>>> + CAN_CTRLMODE_BERR_REPORTING |
>>> + CAN_CTRLMODE_FD |
>>> + CAN_CTRLMODE_FD_NON_ISO;
>>> + priv->net = net;
>>> + priv->spi = spi;
>>> + priv->clk = clk;
>>> + spi_set_drvdata(spi, priv);
>>> +
>>> + ret = tcan4x5x_parse_config(priv);
>>> + if (ret)
>>> + goto out_clk;
>>> +
>>> + /* Configure the SPI bus */
>>> + spi->bits_per_word = 32;
>>> + ret = spi_setup(spi);
>>> + if (ret)
>>> + goto out_clk;
>>> +
>>> + mutex_init(&priv->tcan4x5x_lock);
>>> +
>>> + priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus,
>>> + &spi->dev, &tcan4x5x_regmap);
>>> +
>>> + SET_NETDEV_DEV(net, &spi->dev);
>>> + ret = register_candev(net);
>>> + if (ret)
>>> + goto error_probe;
>>> +
>>> + devm_can_led_init(net);
>>> +
>>> + netdev_info(net, "TCAN4X5X successfully initialized.\n");
>>> + return 0;
>>> +
>>> +error_probe:
>>> + tcan4x5x_power_enable(priv->power, 0);
>>> +out_clk:
>>> + if (!IS_ERR(clk))
>>> + clk_disable_unprepare(clk);
>>> +out_free:
>>> + free_candev(net);
>>> + dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
>>> + return ret;
>>> +}
>>> +
>>> +static int tcan4x5x_can_remove(struct spi_device *spi)
>>> +{
>>> + struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
>>> + struct net_device *net = priv->net;
>>> +
>>> + unregister_candev(net);
>>> +
>>> + tcan4x5x_power_enable(priv->power, 0);
>>> +
>>> + if (!IS_ERR(priv->clk))
>>> + clk_disable_unprepare(priv->clk);
>>> +
>>> + free_candev(net);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct of_device_id tcan4x5x_of_match[] = {
>>> + { .compatible = "ti,tcan4x5x", },
>>> + { }
>>> +};
>>> +MODULE_DEVICE_TABLE(of, tcan4x5x_of_match);
>>> +
>>> +static const struct spi_device_id tcan4x5x_id_table[] = {
>>> + {
>>> + .name = "tcan4x5x",
>>> + .driver_data = 0,
>>> + },
>>> + { }
>>> +};
>>> +MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table);
>>> +
>>> +static struct spi_driver tcan4x5x_can_driver = {
>>> + .driver = {
>>> + .name = DEVICE_NAME,
>>> + .of_match_table = tcan4x5x_of_match,
>>> + .pm = NULL,
>>> + },
>>> + .id_table = tcan4x5x_id_table,
>>> + .probe = tcan4x5x_can_probe,
>>> + .remove = tcan4x5x_can_remove,
>>> +};
>>> +module_spi_driver(tcan4x5x_can_driver);
>>> +
>>> +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
>>> +MODULE_DESCRIPTION("Texas Instruments TCAN4x5x CAN driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/net/can/spi/tcan4x5x.h b/drivers/net/can/spi/tcan4x5x.h
>>> new file mode 100644
>>> index 000000000000..5e14ba571d49
>>> --- /dev/null
>>> +++ b/drivers/net/can/spi/tcan4x5x.h
>>> @@ -0,0 +1,109 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +// SPI to CAN driver for the Texas Instruments TCA4x5x
>>> +// Flash driver chip family
>>> +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
>>> +
>>> +#define TCAN4X5X_DEV_ID0 0x00
>>> +#define TCAN4X5X_DEV_ID1 0x04
>>> +#define TCAN4X5X_REV 0x08
>>> +#define TCAN4X5X_STATUS 0x0C
>>> +#define TCAN4X5X_ERROR_STATUS 0x10
>>> +#define TCAN4X5X_CONTROL 0x14
>>> +
>>> +#define TCAN4X5X_CONFIG 0x800
>>> +#define TCAN4X5X_TS_PRESCALE 0x804
>>> +#define TCAN4X5X_TEST_REG 0x808
>>> +#define TCAN4X5X_INT_FLAGS 0x820
>>> +#define TCAN4X5X_MCAN_INT_REG 0x824
>>> +#define TCAN4X5X_INT_EN 0x830
>>> +
>>> +#define TCAN4X5X_MCAN_CREL 0x1000
>>> +#define TCAN4X5X_MCAN_ENDN 0x1004
>>> +#define TCAN4X5X_MCAN_CUST 0x1008
>>> +#define TCAN4X5X_MCAN_DBTP 0x100C
>>> +#define TCAN4X5X_MCAN_TEST 0x1010
>>> +#define TCAN4X5X_MCAN_RWD 0x1014
>>> +#define TCAN4X5X_MCAN_CCCR 0x1018
>>> +#define TCAN4X5X_MCAN_NBTP 0x101C
>>> +#define TCAN4X5X_MCAN_TSCC 0x1020
>>> +#define TCAN4X5X_MCAN_TSCV 0x1024
>>> +#define TCAN4X5X_MCAN_TOCC 0x1028
>>> +#define TCAN4X5X_MCAN_TOCV 0x102C
>>> +#define TCAN4X5X_MCAN_ECR 0x1040
>>> +#define TCAN4X5X_MCAN_PSR 0x1044
>>> +#define TCAN4X5X_MCAN_TDCR 0x1048
>>> +#define TCAN4X5X_MCAN_INT_FLAG 0x1050
>>> +#define TCAN4X5X_MCAN_INT_EN 0x1054
>>> +#define TCAN4X5X_MCAN_ILS 0x1058
>>> +#define TCAN4X5X_MCAN_ILE 0x105C
>>> +#define TCAN4X5X_MCAN_GFC 0x1080
>>> +#define TCAN4X5X_MCAN_SIDFC 0x1084
>>> +#define TCAN4X5X_MCAN_XIDFC 0x1088
>>> +#define TCAN4X5X_MCAN_XIDAM 0x1090
>>> +#define TCAN4X5X_MCAN_HPMS 0x1094
>>> +#define TCAN4X5X_MCAN_NDAT1 0x1098
>>> +#define TCAN4X5X_MCAN_NDAT2 0x109C
>>> +#define TCAN4X5X_MCAN_RXF0C 0x10A0
>>> +#define TCAN4X5X_MCAN_RXF0S 0x10A4
>>> +#define TCAN4X5X_MCAN_RXF0A 0x10A8
>>> +#define TCAN4X5X_MCAN_RXBC 0x10AC
>>> +#define TCAN4X5X_MCAN_RXF1C 0x10B0
>>> +#define TCAN4X5X_MCAN_RXF1S 0x10B4
>>> +#define TCAN4X5X_MCAN_RXF1A 0x10B8
>>> +#define TCAN4X5X_MCAN_RXESC 0x10BC
>>> +#define TCAN4X5X_MCAN_TXBC 0x10C0
>>> +#define TCAN4X5X_MCAN_TXFQS 0x10C4
>>> +#define TCAN4X5X_MCAN_TXESC 0x10C8
>>> +#define TCAN4X5X_MCAN_TXBRP 0x10CC
>>> +#define TCAN4X5X_MCAN_TXBAR 0x10D0
>>> +#define TCAN4X5X_MCAN_TXBCR 0x10D4
>>> +#define TCAN4X5X_MCAN_TXBTO 0x10D8
>>> +#define TCAN4X5X_MCAN_TXBCF 0x10DC
>>> +#define TCAN4X5X_MCAN_TXBTIE 0x10E0
>>> +#define TCAN4X5X_MCAN_TXBCIE 0x10E4
>>> +#define TCAN4X5X_MCAN_TXEFC 0x10F0
>>> +#define TCAN4X5X_MCAN_TXEFS 0x10F4
>>> +#define TCAN4X5X_MCAN_TXEFA 0x10F8
>>> +
>>> +#define TCAN4X5X_MRAM_START 0x8000
>>> +#define TCAN4X5X_MRAM_SIZE 2048
>>> +
>>> +#define TCAN4X5X_MAX_REGISTER 0x8fff
>>> +
>>> +/* 64 byte buffer + 8 byte MCAN header */
>>> +#define TCAN4X5X_BUF_LEN 72
>>> +
>>> +struct tcan4x5x_priv {
>>> + struct can_priv can;
>>> + struct net_device *net;
>>> + struct regmap *regmap;
>>> + struct spi_device *spi;
>>> +
>>> + struct mutex tcan4x5x_lock; /* SPI device lock */
>>> +
>>> + struct gpio_desc *reset_gpio;
>>> + struct gpio_desc *interrupt_gpio;
>>> + struct gpio_desc *wake_gpio;
>>> + struct regulator *power;
>>> + struct clk *clk;
>>> +
>>> + struct sk_buff *tx_skb;
>>> + int tx_len;
>>> +
>>> + struct workqueue_struct *wq;
>>> + struct work_struct tx_work;
>>> + struct work_struct restart_work;
>>> +
>>> + u32 *spi_tx_buf;
>>> + u32 *spi_rx_buf;
>>> +
>>> + int force_quit;
>>> + int after_suspend;
>>> +#define AFTER_SUSPEND_UP 1
>>> +#define AFTER_SUSPEND_DOWN 2
>>> +#define AFTER_SUSPEND_POWER 4
>>> +#define AFTER_SUSPEND_RESTART 8
>>> + int restart_tx;
>>> +
>>> + int irq;
>>> +};
>>>
>>
>>
--
------------------
Dan Murphy
^ permalink raw reply
* Re: [PATCH net-next v4 11/11] net: mscc: ocelot: make use of SerDes PHYs for handling their configuration
From: Florian Fainelli @ 2018-10-04 20:16 UTC (permalink / raw)
To: Quentin Schulz, alexandre.belloni, ralf, paul.burton, jhogan,
robh+dt, mark.rutland, davem, kishon, andrew
Cc: allan.nielsen, linux-mips, devicetree, linux-kernel, netdev,
thomas.petazzoni
In-Reply-To: <20181004122208.32272-12-quentin.schulz@bootlin.com>
On 10/04/2018 05:22 AM, Quentin Schulz wrote:
> Previously, the SerDes muxing was hardcoded to a given mode in the MAC
> controller driver. Now, the SerDes muxing is configured within the
> Device Tree and is enforced in the MAC controller driver so we can have
> a lot of different SerDes configurations.
>
> Make use of the SerDes PHYs in the MAC controller to set up the SerDes
> according to the SerDes<->switch port mapping and the communication mode
> with the Ethernet PHY.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
--
Florian
^ permalink raw reply
* Re: [PATCH net-next v4 09/11] dt-bindings: add constants for Microsemi Ocelot SerDes driver
From: Florian Fainelli @ 2018-10-04 20:15 UTC (permalink / raw)
To: Quentin Schulz, alexandre.belloni, ralf, paul.burton, jhogan,
robh+dt, mark.rutland, davem, kishon, andrew
Cc: allan.nielsen, linux-mips, devicetree, linux-kernel, netdev,
thomas.petazzoni
In-Reply-To: <20181004122208.32272-10-quentin.schulz@bootlin.com>
On 10/04/2018 05:22 AM, Quentin Schulz wrote:
> The Microsemi Ocelot has multiple SerDes and requires that the SerDes be
> muxed accordingly to the hardware representation.
>
> Let's add a constant for each SerDes available in the Microsemi Ocelot.
>
> Reviewed-by: Rob Herring <robh@kernel.org>
> Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
--
Florian
^ permalink raw reply
* Re: [PATCH net] net: mvpp2: Extract the correct ethtype from the skb for tx csum offload
From: Maxime Chevallier @ 2018-10-04 13:13 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: davem, netdev, linux-kernel, Antoine Tenart, thomas.petazzoni,
gregory.clement, miquel.raynal, nadavh, stefanc, ymarkman, mw
In-Reply-To: <13fb6bd4-db67-6c35-9e6d-09ed89e10a45@cogentembedded.com>
Hello Sergei,
On Thu, 4 Oct 2018 12:50:17 +0300
Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> wrote:
>Hello!
>
>On 10/4/2018 11:03 AM, Maxime Chevallier wrote:
>
>> When offloading the L3 and L4 csum computation on TX, we need to extract
>> the l3_proto from the ethtype, independently of the presence of a vlan
>> tag.
>>
>> The actual driver uses skb->protocol as-is, resulting in packets with
>> the wrong L4 checksum being sent when there's a vlan tag in the packet
>> header and checksum offloading is enabled.
>>
>> This commit makes use of vlan_protocol_get() to get the correct ethtype
>> regardless the presence of a vlan tag.
> ^ of?
>
>> Fixes: 3f51850 ("ethernet: Add new driver for Marvell Armada 375 network unit")
>
> 12 hex digits needed here.
Sorry about that, I'll resend with a proper Fixes tag.
Thanks,
Maxime
^ permalink raw reply
* [PATCH net-next v3] wireless-drivers: rtnetlink wifi simulation device
From: Cody Schuffelen @ 2018-10-04 19:59 UTC (permalink / raw)
To: Johannes Berg
Cc: Kalle Valo, David S . Miller, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA, kernel-team-z5hGa2qSFaRBDgjK7y7TUQ,
Cody Schuffelen
This device takes over an existing network device and produces a
new one that appears like a wireless connection, returning enough canned
responses to nl80211 to satisfy a standard connection manager. If
necessary, it can also be set up one step removed from an existing
network device, such as through a vlan/80211Q or macvlan connection to
not disrupt the existing network interface.
To use it to wrap a bare ethernet connection:
ip link add link eth0 name wlan0 type virt_wifi
You may have to rename or otherwise hide the eth0 from your connection
manager, as the original network link will become unusuable and only
the wireless wrapper will be functional. This can also be combined with
vlan or macvlan links on top of eth0 to share the network between
distinct links, but that requires support outside the machine for
accepting vlan-tagged packets or packets from multiple MAC addresses.
This is being used for Google's Remote Android Virtual Device project,
which runs Android devices in virtual machines. The standard network
interfaces provided inside the virtual machines are all ethernet.
However, Android is not interested in ethernet devices and would rather
connect to a wireless interface. This patch allows the virtual machine
guest to treat one of its network connections as wireless rather than
ethernet, satisfying Android's network connection requirements.
We believe this is a generally useful driver for simulating wireless
network connections in other environments where a wireless connection is
desired by some userspace process but is not available.
This is distinct from other testing efforts such as mac80211_hwsim by
being a cfg80211 device instead of mac80211 device, allowing straight
pass-through on the data plane instead of forcing packaging of ethernet
data into mac80211 frames.
Signed-off-by: A. Cody Schuffelen <schuffelen-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Acked-by: Alistair Strachan <astrachan-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Acked-by: Greg Hartman <ghartman-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
---
First version: https://lkml.org/lkml/2018/7/27/947
First review: https://lore.kernel.org/lkml/1535460343.5895.56.camel-cdvu00un1VgdHxzADdlk8Q@public.gmane.org/
Second version: https://lore.kernel.org/lkml/20180926194324.71290-1-schuffelen-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org/
Second review: https://www.lkml.org/lkml/2018/9/27/228
Second review: https://www.lkml.org/lkml/2018/9/27/229
Second review: https://www.lkml.org/lkml/2018/9/27/669
Thanks for all the comments on v2! I believe I've addressed all of them
here. I've also added changes to react better to the netdev going down,
canceling ongoing scans and rejecting wifi network connection requests.
I wasn't completely clear on whether I should change the title (net-next
to mac80211-next) so I left it as is for v3 to try to keep the patchwork
series together.
The driver is also now a bool instead of a tristate to use __ro_after_init.
drivers/net/wireless/Kconfig | 7 +
drivers/net/wireless/Makefile | 2 +
drivers/net/wireless/virt_wifi.c | 618 +++++++++++++++++++++++++++++++
3 files changed, 627 insertions(+)
create mode 100644 drivers/net/wireless/virt_wifi.c
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 166920ae23f8..f4e808411634 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -114,4 +114,11 @@ config USB_NET_RNDIS_WLAN
If you choose to build a module, it'll be called rndis_wlan.
+config VIRT_WIFI
+ bool "Wifi wrapper for ethernet drivers"
+ depends on CFG80211
+ ---help---
+ This option adds support for ethernet connections to appear as if they
+ are wifi connections through a special rtnetlink device.
+
endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 7fc96306712a..6cfe74515c95 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -27,3 +27,5 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o
obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
+
+obj-$(CONFIG_VIRT_WIFI) += virt_wifi.o
diff --git a/drivers/net/wireless/virt_wifi.c b/drivers/net/wireless/virt_wifi.c
new file mode 100644
index 000000000000..b25c1313d0e7
--- /dev/null
+++ b/drivers/net/wireless/virt_wifi.c
@@ -0,0 +1,618 @@
+// SPDX-License-Identifier: GPL-2.0
+/* drivers/net/wireless/virt_wifi.c
+ *
+ * A fake implementation of cfg80211_ops that can be tacked on to an ethernet
+ * net_device to make it appear as a wireless connection.
+ *
+ * Copyright (C) 2018 Google, Inc.
+ *
+ * Author: schuffelen-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org
+ */
+
+#include <net/cfg80211.h>
+#include <net/rtnetlink.h>
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+
+struct virt_wifi_priv {
+ bool being_deleted;
+ bool is_connected;
+ bool netdev_is_up;
+ struct net_device *netdev;
+ struct cfg80211_scan_request *scan_request;
+ u8 connect_requested_bss[ETH_ALEN];
+ struct delayed_work scan_result;
+ struct delayed_work connect;
+ struct delayed_work disconnect;
+ u16 disconnect_reason;
+};
+
+static struct ieee80211_channel channel_2ghz = {
+ .band = NL80211_BAND_2GHZ,
+ .center_freq = 2432,
+ .hw_value = 2432,
+ .max_power = 20,
+};
+
+static struct ieee80211_rate bitrates_2ghz[] = {
+ { .bitrate = 10 },
+ { .bitrate = 20 },
+ { .bitrate = 55 },
+ { .bitrate = 60 },
+ { .bitrate = 110 },
+ { .bitrate = 120 },
+ { .bitrate = 240 },
+};
+
+static struct ieee80211_supported_band band_2ghz = {
+ .channels = &channel_2ghz,
+ .bitrates = bitrates_2ghz,
+ .band = NL80211_BAND_2GHZ,
+ .n_channels = 1,
+ .n_bitrates = ARRAY_SIZE(bitrates_2ghz),
+ .ht_cap = {
+ .ht_supported = true,
+ .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_GRN_FLD |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_DSSSCCK40,
+ .ampdu_factor = 0x3,
+ .ampdu_density = 0x6,
+ .mcs = {
+ .rx_mask = {0xff, 0xff},
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+ },
+ },
+};
+
+static struct ieee80211_channel channel_5ghz = {
+ .band = NL80211_BAND_5GHZ,
+ .center_freq = 5240,
+ .hw_value = 5240,
+ .max_power = 20,
+};
+
+static struct ieee80211_rate bitrates_5ghz[] = {
+ { .bitrate = 60 },
+ { .bitrate = 120 },
+ { .bitrate = 240 },
+};
+
+#define RX_MCS_MAP \
+ cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 14)
+
+#define TX_MCS_MAP \
+ cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 14) \
+
+static struct ieee80211_supported_band band_5ghz = {
+ .channels = &channel_5ghz,
+ .bitrates = bitrates_5ghz,
+ .band = NL80211_BAND_5GHZ,
+ .n_channels = 1,
+ .n_bitrates = ARRAY_SIZE(bitrates_5ghz),
+ .ht_cap = {
+ .ht_supported = true,
+ .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_GRN_FLD |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_DSSSCCK40,
+ .ampdu_factor = 0x3,
+ .ampdu_density = 0x6,
+ .mcs = {
+ .rx_mask = {0xff, 0xff},
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+ },
+ },
+ .vht_cap = {
+ .vht_supported = true,
+ .cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
+ IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
+ IEEE80211_VHT_CAP_RXLDPC |
+ IEEE80211_VHT_CAP_SHORT_GI_80 |
+ IEEE80211_VHT_CAP_SHORT_GI_160 |
+ IEEE80211_VHT_CAP_TXSTBC |
+ IEEE80211_VHT_CAP_RXSTBC_1 |
+ IEEE80211_VHT_CAP_RXSTBC_2 |
+ IEEE80211_VHT_CAP_RXSTBC_3 |
+ IEEE80211_VHT_CAP_RXSTBC_4 |
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+ .vht_mcs = {
+ .rx_mcs_map = RX_MCS_MAP,
+ .tx_mcs_map = TX_MCS_MAP,
+ }
+ },
+};
+
+/** Assigned at module init. Guaranteed locally-administered and unicast. */
+static u8 fake_router_bssid[ETH_ALEN] __ro_after_init = {};
+
+static int virt_wifi_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request)
+{
+ struct virt_wifi_priv *priv = wiphy_priv(wiphy);
+
+ wiphy_debug(wiphy, "scan\n");
+
+ if (priv->scan_request || priv->being_deleted)
+ return -EBUSY;
+
+ priv->scan_request = request;
+ schedule_delayed_work(&priv->scan_result, HZ * 2);
+
+ return 0;
+}
+
+static void virt_wifi_scan_result(struct work_struct *work)
+{
+ const union {
+ struct {
+ u8 tag;
+ u8 len;
+ u8 ssid[8];
+ } __packed parts;
+ u8 data[10];
+ } ssid = { .parts = {
+ .tag = WLAN_EID_SSID, .len = 8, .ssid = "VirtWifi" }
+ };
+ struct cfg80211_bss *informed_bss;
+ struct virt_wifi_priv *priv =
+ container_of(work, struct virt_wifi_priv,
+ scan_result.work);
+ struct wiphy *wiphy = priv_to_wiphy(priv);
+ struct cfg80211_inform_bss mock_inform_bss = {
+ .chan = &channel_5ghz,
+ .scan_width = NL80211_BSS_CHAN_WIDTH_20,
+ .signal = -60,
+ .boottime_ns = ktime_get_boot_ns(),
+ };
+ struct cfg80211_scan_info scan_info = {};
+
+ informed_bss =
+ cfg80211_inform_bss_data(wiphy, &mock_inform_bss,
+ CFG80211_BSS_FTYPE_PRESP,
+ fake_router_bssid,
+ mock_inform_bss.boottime_ns,
+ WLAN_CAPABILITY_ESS, 0, ssid.data,
+ sizeof(ssid), GFP_KERNEL);
+ cfg80211_put_bss(wiphy, informed_bss);
+
+ rtnl_lock();
+ if (priv->scan_request) {
+ cfg80211_scan_done(priv->scan_request, &scan_info);
+ priv->scan_request = NULL;
+ }
+ rtnl_unlock();
+}
+
+static int virt_wifi_connect(struct wiphy *wiphy,
+ struct net_device *netdev,
+ struct cfg80211_connect_params *sme)
+{
+ struct virt_wifi_priv *priv = wiphy_priv(wiphy);
+ bool could_schedule;
+
+ if (priv->being_deleted)
+ return -EBUSY;
+
+ wiphy_debug(wiphy, "connect\n");
+ could_schedule = schedule_delayed_work(&priv->connect, HZ * 2);
+ if (!could_schedule)
+ return -EBUSY;
+
+ if (sme->bssid)
+ ether_addr_copy(priv->connect_requested_bss, sme->bssid);
+ else
+ eth_zero_addr(priv->connect_requested_bss);
+ return 0;
+}
+
+static void virt_wifi_connect_complete(struct work_struct *work)
+{
+ struct virt_wifi_priv *priv =
+ container_of(work, struct virt_wifi_priv, connect.work);
+ u8 *requested_bss = priv->connect_requested_bss;
+ bool has_addr = !is_zero_ether_addr(requested_bss);
+ bool right_addr = ether_addr_equal(requested_bss, fake_router_bssid);
+ u16 status = WLAN_STATUS_SUCCESS;
+
+ rtnl_lock();
+ if (!priv->netdev_is_up || (has_addr && !right_addr))
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ else
+ priv->is_connected = true;
+
+ cfg80211_connect_result(priv->netdev, requested_bss, NULL, 0, NULL, 0,
+ status, GFP_KERNEL);
+ rtnl_unlock();
+}
+
+static int virt_wifi_disconnect(struct wiphy *wiphy, struct net_device *netdev,
+ u16 reason_code)
+{
+ struct virt_wifi_priv *priv = wiphy_priv(wiphy);
+ bool could_schedule;
+
+ if (priv->being_deleted)
+ return -EBUSY;
+
+ wiphy_debug(wiphy, "disconnect\n");
+ could_schedule = schedule_delayed_work(&priv->disconnect, HZ * 2);
+ if (!could_schedule)
+ return -EBUSY;
+ priv->disconnect_reason = reason_code;
+ return 0;
+}
+
+static void virt_wifi_disconnect_complete(struct work_struct *work)
+{
+ struct virt_wifi_priv *priv =
+ container_of(work, struct virt_wifi_priv, disconnect.work);
+
+ cfg80211_disconnected(priv->netdev, priv->disconnect_reason, NULL, 0,
+ true, GFP_KERNEL);
+ priv->is_connected = false;
+}
+
+static int virt_wifi_get_station(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *mac,
+ struct station_info *sinfo)
+{
+ wiphy_debug(wiphy, "get_station\n");
+
+ if (!ether_addr_equal(mac, fake_router_bssid))
+ return -ENOENT;
+
+ sinfo->filled = BIT(NL80211_STA_INFO_TX_PACKETS) |
+ BIT(NL80211_STA_INFO_TX_FAILED) | BIT(NL80211_STA_INFO_SIGNAL) |
+ BIT(NL80211_STA_INFO_TX_BITRATE);
+ sinfo->tx_packets = 1;
+ sinfo->tx_failed = 0;
+ sinfo->signal = -60;
+ sinfo->txrate = (struct rate_info) {
+ .legacy = 10, /* units are 100kbit/s */
+ };
+ return 0;
+}
+
+static int virt_wifi_dump_station(struct wiphy *wiphy,
+ struct net_device *dev,
+ int idx,
+ u8 *mac,
+ struct station_info *sinfo)
+{
+ wiphy_debug(wiphy, "dump_station\n");
+
+ if (idx != 0)
+ return -ENOENT;
+
+ ether_addr_copy(mac, fake_router_bssid);
+ return virt_wifi_get_station(wiphy, dev, fake_router_bssid, sinfo);
+}
+
+static const struct cfg80211_ops virt_wifi_cfg80211_ops = {
+ .scan = virt_wifi_scan,
+
+ .connect = virt_wifi_connect,
+ .disconnect = virt_wifi_disconnect,
+
+ .get_station = virt_wifi_get_station,
+ .dump_station = virt_wifi_dump_station,
+};
+
+static struct wireless_dev *virt_wireless_dev(struct device *device,
+ struct net_device *netdev)
+{
+ struct wireless_dev *wdev;
+ struct wiphy *wiphy;
+ struct virt_wifi_priv *priv;
+
+ wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
+
+ if (!wdev)
+ return ERR_PTR(-ENOMEM);
+
+ wdev->iftype = NL80211_IFTYPE_STATION;
+ wiphy = wiphy_new(&virt_wifi_cfg80211_ops, sizeof(*priv));
+
+ if (!wiphy) {
+ kfree(wdev);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ wdev->wiphy = wiphy;
+
+ wiphy->max_scan_ssids = 4;
+ wiphy->max_scan_ie_len = 1000;
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+ wiphy->bands[NL80211_BAND_2GHZ] = &band_2ghz;
+ wiphy->bands[NL80211_BAND_5GHZ] = &band_5ghz;
+ wiphy->bands[NL80211_BAND_60GHZ] = NULL;
+
+ /* Don't worry about frequency regulations. */
+ wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED;
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ set_wiphy_dev(wiphy, device);
+
+ priv = wiphy_priv(wiphy);
+ priv->netdev_is_up = false;
+ priv->being_deleted = false;
+ priv->is_connected = false;
+ priv->scan_request = NULL;
+ priv->netdev = netdev;
+ INIT_DELAYED_WORK(&priv->scan_result, virt_wifi_scan_result);
+ INIT_DELAYED_WORK(&priv->connect, virt_wifi_connect_complete);
+ INIT_DELAYED_WORK(&priv->disconnect, virt_wifi_disconnect_complete);
+ return wdev;
+}
+
+struct virt_wifi_netdev_priv {
+ struct net_device *lowerdev;
+ struct net_device *upperdev;
+ struct work_struct register_wiphy_work;
+};
+
+static netdev_tx_t virt_wifi_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
+ struct virt_wifi_priv *w_priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
+
+ if (!w_priv->is_connected)
+ return NET_XMIT_DROP;
+
+ skb->dev = priv->lowerdev;
+ return dev_queue_xmit(skb);
+}
+
+/* Called with rtnl lock held. */
+static int virt_wifi_net_device_open(struct net_device *dev)
+{
+ struct virt_wifi_priv *w_priv;
+
+ if (!dev->ieee80211_ptr)
+ return 0;
+ w_priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
+
+ w_priv->netdev_is_up = true;
+ return 0;
+}
+
+/* Called with rtnl lock held. */
+static int virt_wifi_net_device_stop(struct net_device *dev)
+{
+ struct virt_wifi_priv *w_priv;
+ struct cfg80211_scan_info scan_info = { .aborted = true };
+
+ if (!dev->ieee80211_ptr)
+ return 0;
+ w_priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
+ w_priv->netdev_is_up = false;
+
+ if (w_priv->scan_request) {
+ cfg80211_scan_done(w_priv->scan_request, &scan_info);
+ w_priv->scan_request = NULL;
+ }
+
+ return 0;
+}
+
+static const struct net_device_ops virt_wifi_ops = {
+ .ndo_start_xmit = virt_wifi_start_xmit,
+ .ndo_open = virt_wifi_net_device_open,
+ .ndo_stop = virt_wifi_net_device_stop,
+};
+
+static void free_netdev_and_wiphy(struct net_device *dev)
+{
+ struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
+ struct virt_wifi_priv *w_priv;
+
+ flush_work(&priv->register_wiphy_work);
+ if (dev->ieee80211_ptr && !IS_ERR(dev->ieee80211_ptr)) {
+ w_priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
+ w_priv->being_deleted = true;
+ flush_delayed_work(&w_priv->scan_result);
+ flush_delayed_work(&w_priv->connect);
+ flush_delayed_work(&w_priv->disconnect);
+
+ if (dev->ieee80211_ptr->wiphy->registered)
+ wiphy_unregister(dev->ieee80211_ptr->wiphy);
+ wiphy_free(dev->ieee80211_ptr->wiphy);
+ kfree(dev->ieee80211_ptr);
+ }
+ free_netdev(dev);
+}
+
+static void virt_wifi_setup(struct net_device *dev)
+{
+ ether_setup(dev);
+ dev->netdev_ops = &virt_wifi_ops;
+ dev->priv_destructor = free_netdev_and_wiphy;
+}
+
+/* Called under rcu_read_lock() from netif_receive_skb */
+static rx_handler_result_t virt_wifi_rx_handler(struct sk_buff **pskb)
+{
+ struct sk_buff *skb = *pskb;
+ struct virt_wifi_netdev_priv *priv =
+ rcu_dereference(skb->dev->rx_handler_data);
+ struct virt_wifi_priv *w_priv =
+ wiphy_priv(priv->upperdev->ieee80211_ptr->wiphy);
+
+ if (!w_priv->is_connected)
+ return RX_HANDLER_PASS;
+
+ /* GFP_ATOMIC because this is a packet interrupt handler. */
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(&priv->upperdev->dev, "can't skb_share_check\n");
+ return RX_HANDLER_CONSUMED;
+ }
+
+ *pskb = skb;
+ skb->dev = priv->upperdev;
+ skb->pkt_type = PACKET_HOST;
+ return RX_HANDLER_ANOTHER;
+}
+
+static void virt_wifi_register_wiphy(struct work_struct *work)
+{
+ struct virt_wifi_netdev_priv *priv =
+ container_of(work, struct virt_wifi_netdev_priv,
+ register_wiphy_work);
+ struct wireless_dev *wdev = priv->upperdev->ieee80211_ptr;
+ int err;
+
+ err = wiphy_register(wdev->wiphy);
+ if (err < 0) {
+ dev_err(&priv->upperdev->dev, "can't wiphy_register (%d)\n",
+ err);
+
+ /* Roll back the net_device, it's not going to do wifi. */
+ rtnl_lock();
+ err = rtnl_delete_link(priv->upperdev);
+ rtnl_unlock();
+
+ /* rtnl_delete_link should only throw errors if it's not a
+ * netlink device, but we know here it is already a virt_wifi
+ * device.
+ */
+ WARN_ONCE(err, "rtnl_delete_link failed on a virt_wifi device");
+ }
+}
+
+/* Called with rtnl lock held. */
+static int virt_wifi_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[],
+ struct netlink_ext_ack *extack)
+{
+ struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
+ int err;
+
+ if (!tb[IFLA_LINK])
+ return -EINVAL;
+
+ INIT_WORK(&priv->register_wiphy_work, virt_wifi_register_wiphy);
+ priv->upperdev = dev;
+ priv->lowerdev = __dev_get_by_index(src_net,
+ nla_get_u32(tb[IFLA_LINK]));
+
+ if (!priv->lowerdev)
+ return -ENODEV;
+ if (!tb[IFLA_MTU])
+ dev->mtu = priv->lowerdev->mtu;
+ else if (dev->mtu > priv->lowerdev->mtu)
+ return -EINVAL;
+
+ err = netdev_rx_handler_register(priv->lowerdev, virt_wifi_rx_handler,
+ priv);
+ if (err) {
+ dev_err(&priv->lowerdev->dev,
+ "can't netdev_rx_handler_register: %d\n", err);
+ return err;
+ }
+
+ eth_hw_addr_inherit(dev, priv->lowerdev);
+ netif_stacked_transfer_operstate(priv->lowerdev, dev);
+
+ SET_NETDEV_DEV(dev, &priv->lowerdev->dev);
+ dev->ieee80211_ptr = virt_wireless_dev(&priv->lowerdev->dev, dev);
+
+ if (IS_ERR(dev->ieee80211_ptr)) {
+ dev_err(&priv->lowerdev->dev, "can't init wireless: %ld\n",
+ PTR_ERR(dev->ieee80211_ptr));
+ err = PTR_ERR(dev->ieee80211_ptr);
+ goto remove_handler;
+ }
+
+ err = register_netdevice(dev);
+ if (err) {
+ dev_err(&priv->lowerdev->dev, "can't register_netdevice: %d\n",
+ err);
+ goto free_wiphy;
+ }
+
+ err = netdev_upper_dev_link(priv->lowerdev, dev, extack);
+ if (err) {
+ dev_err(&priv->lowerdev->dev, "can't netdev_upper_dev_link: %d\n",
+ err);
+ goto unregister_netdev;
+ }
+
+ /* The newlink callback is invoked while holding the rtnl lock, but
+ * register_wiphy wants to claim the rtnl lock itself.
+ */
+ schedule_work(&priv->register_wiphy_work);
+
+ return 0;
+unregister_netdev:
+ unregister_netdevice(dev);
+free_wiphy: /* Safe whether or not netdev destructor runs. */
+ wiphy_free(dev->ieee80211_ptr->wiphy);
+ kfree(dev->ieee80211_ptr);
+ dev->ieee80211_ptr = NULL;
+remove_handler:
+ netdev_rx_handler_unregister(priv->lowerdev);
+
+ return err;
+}
+
+/* Called with rtnl lock held. */
+static void virt_wifi_dellink(struct net_device *dev,
+ struct list_head *head)
+{
+ struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
+
+ netdev_rx_handler_unregister(priv->lowerdev);
+ netdev_upper_dev_unlink(priv->lowerdev, dev);
+
+ unregister_netdevice_queue(dev, head);
+
+ /* Deleting the wiphy is handled in the netdev destructor. */
+}
+
+static struct rtnl_link_ops virt_wifi_link_ops = {
+ .kind = "virt_wifi",
+ .setup = virt_wifi_setup,
+ .newlink = virt_wifi_newlink,
+ .dellink = virt_wifi_dellink,
+ .priv_size = sizeof(struct virt_wifi_netdev_priv),
+};
+
+static int __init virt_wifi_init_module(void)
+{
+ /* Guaranteed to be locallly-administered and not multicast. */
+ eth_random_addr(fake_router_bssid);
+ return rtnl_link_register(&virt_wifi_link_ops);
+}
+
+static void __exit virt_wifi_cleanup_module(void)
+{
+ rtnl_link_unregister(&virt_wifi_link_ops);
+}
+
+module_init(virt_wifi_init_module);
+module_exit(virt_wifi_cleanup_module);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Cody Schuffelen <schuffelen-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("Driver for a wireless wrapper of ethernet devices");
+MODULE_ALIAS_RTNL_LINK("virt_wifi");
--
2.19.0.605.g01d371f741-goog
^ permalink raw reply related
* Re: [PATCH bpf-next 1/6] bpf: introduce BPF_PROG_TYPE_FILE_FILTER
From: Andy Lutomirski @ 2018-10-04 19:51 UTC (permalink / raw)
To: Roman Gushchin
Cc: Alexei Starovoitov, David S. Miller, Daniel Borkmann, Al Viro,
Network Development, LKML, kernel-team
In-Reply-To: <20181004194123.GA12697@castle.DHCP.thefacebook.com>
On Thu, Oct 4, 2018 at 12:41 PM Roman Gushchin <guro@fb.com> wrote:
>
> On Wed, Oct 03, 2018 at 07:57:45PM -0700, Alexei Starovoitov wrote:
> > Similar to networking sandboxing programs and cgroup-v2 based hooks
> > (BPF_CGROUP_INET_[INGRESS|EGRESS,] BPF_CGROUP_INET[4|6]_[BIND|CONNECT], etc)
> > introduce basic per-container sandboxing for file access via
> > new BPF_PROG_TYPE_FILE_FILTER program type that attaches after
> > security_file_open() LSM hook and works as additional file_open filter.
> > The new cgroup bpf hook is called BPF_CGROUP_FILE_OPEN.
> >
> > Just like other cgroup-bpf programs new BPF_PROG_TYPE_FILE_FILTER type
> > is only available to root.
> >
> > This program type has access to single argument 'struct bpf_file_info'
> > that contains standard sys_stat fields:
> > struct bpf_file_info {
> > __u64 inode;
> > __u32 dev_major;
> > __u32 dev_minor;
> > __u32 fs_magic;
> > __u32 mnt_id;
> > __u32 nlink;
> > __u32 mode; /* file mode S_ISDIR, S_ISLNK, 0755, etc */
> > __u32 flags; /* open flags O_RDWR, O_CREAT, etc */
> > };
>
> It's probably nice to have file uid/gid as well.
And an indication of which mount namespace we're looking at.
--Andy
^ permalink raw reply
* Re: [PATCH bpf-next 1/6] bpf: introduce BPF_PROG_TYPE_FILE_FILTER
From: Roman Gushchin @ 2018-10-04 19:41 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: David S . Miller, daniel@iogearbox.net, luto@amacapital.net,
viro@zeniv.linux.org.uk, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, Kernel Team
In-Reply-To: <20181004025750.498303-2-ast@kernel.org>
On Wed, Oct 03, 2018 at 07:57:45PM -0700, Alexei Starovoitov wrote:
> Similar to networking sandboxing programs and cgroup-v2 based hooks
> (BPF_CGROUP_INET_[INGRESS|EGRESS,] BPF_CGROUP_INET[4|6]_[BIND|CONNECT], etc)
> introduce basic per-container sandboxing for file access via
> new BPF_PROG_TYPE_FILE_FILTER program type that attaches after
> security_file_open() LSM hook and works as additional file_open filter.
> The new cgroup bpf hook is called BPF_CGROUP_FILE_OPEN.
>
> Just like other cgroup-bpf programs new BPF_PROG_TYPE_FILE_FILTER type
> is only available to root.
>
> This program type has access to single argument 'struct bpf_file_info'
> that contains standard sys_stat fields:
> struct bpf_file_info {
> __u64 inode;
> __u32 dev_major;
> __u32 dev_minor;
> __u32 fs_magic;
> __u32 mnt_id;
> __u32 nlink;
> __u32 mode; /* file mode S_ISDIR, S_ISLNK, 0755, etc */
> __u32 flags; /* open flags O_RDWR, O_CREAT, etc */
> };
It's probably nice to have file uid/gid as well.
Thanks!
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox