Commit b55cbbee authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-Add-extended-ACK-for-EMADs'

Ido Schimmel says:

====================
mlxsw: Add extended ACK for EMADs

Shalom says:

Ethernet Management Datagrams (EMADs) are Ethernet packets sent between
the driver and device's firmware. They are used to pass various
configurations to the device, but also to get events (e.g., port up)
from it. After the Ethernet header, these packets are built in a TLV
format.

Up until now, whenever the driver issued an erroneous register access it
only got an error code indicating a bad parameter was used. This patch
set adds a new TLV (string TLV) that can be used by the firmware to
encode a 128 character string describing the error. The new TLV is
allocated by the driver and set to zeros. In case of error, the driver
will check the length of the string in the response and report it using
devlink hwerr tracepoint.

Example:

$ perf record -a -q -e devlink:devlink_hwerr &

$ pkill -2 perf

$ perf script -F trace:event,trace | grep hwerr
devlink:devlink_hwerr: bus_name=pci dev_name=0000:03:00.0 driver_name=mlxsw_spectrum err=7 (tid=9913892d00001593,reg_id=8018(rauhtd)) bad parameter (inside er_rauhtd_write_query(), num_rec=32 is over the maximum  number of records supported)

Patch #1 parses the offsets of the different TLVs in incoming EMADs and
stores them in the skb's control block. This makes it easier to later
add new TLVs.

Patches #2-#3 remove deprecated TLVs and add string TLV definition.

Patches #4-#7 gradually add support for the new string TLV.

v2:
* Use existing devlink hwerr tracepoint to report the error string,
  instead of printing it to kernel log
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b2ef81dc 9032b9e8
......@@ -71,6 +71,7 @@ struct mlxsw_core {
struct list_head trans_list;
spinlock_t trans_list_lock; /* protects trans_list writes */
bool use_emad;
bool enable_string_tlv;
} emad;
struct {
u8 *mapping; /* lag_id+port_index to local_port mapping */
......@@ -249,6 +250,25 @@ MLXSW_ITEM32(emad, op_tlv, class, 0x04, 0, 8);
*/
MLXSW_ITEM64(emad, op_tlv, tid, 0x08, 0, 64);
/* emad_string_tlv_type
* Type of the TLV.
* Must be set to 0x2 (string TLV).
*/
MLXSW_ITEM32(emad, string_tlv, type, 0x00, 27, 5);
/* emad_string_tlv_len
* Length of the string TLV in u32.
*/
MLXSW_ITEM32(emad, string_tlv, len, 0x00, 16, 11);
#define MLXSW_EMAD_STRING_TLV_STRING_LEN 128
/* emad_string_tlv_string
* String provided by the device's firmware in case of erroneous register access
*/
MLXSW_ITEM_BUF(emad, string_tlv, string, 0x04,
MLXSW_EMAD_STRING_TLV_STRING_LEN);
/* emad_reg_tlv_type
* Type of the TLV.
* Must be set to 0x3 (register TLV).
......@@ -304,6 +324,12 @@ static void mlxsw_emad_pack_reg_tlv(char *reg_tlv,
memcpy(reg_tlv + sizeof(u32), payload, reg->len);
}
static void mlxsw_emad_pack_string_tlv(char *string_tlv)
{
mlxsw_emad_string_tlv_type_set(string_tlv, MLXSW_EMAD_TLV_TYPE_STRING);
mlxsw_emad_string_tlv_len_set(string_tlv, MLXSW_EMAD_STRING_TLV_LEN);
}
static void mlxsw_emad_pack_op_tlv(char *op_tlv,
const struct mlxsw_reg_info *reg,
enum mlxsw_core_reg_access_type type,
......@@ -345,7 +371,7 @@ static void mlxsw_emad_construct(struct sk_buff *skb,
const struct mlxsw_reg_info *reg,
char *payload,
enum mlxsw_core_reg_access_type type,
u64 tid)
u64 tid, bool enable_string_tlv)
{
char *buf;
......@@ -355,26 +381,82 @@ static void mlxsw_emad_construct(struct sk_buff *skb,
buf = skb_push(skb, reg->len + sizeof(u32));
mlxsw_emad_pack_reg_tlv(buf, reg, payload);
if (enable_string_tlv) {
buf = skb_push(skb, MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32));
mlxsw_emad_pack_string_tlv(buf);
}
buf = skb_push(skb, MLXSW_EMAD_OP_TLV_LEN * sizeof(u32));
mlxsw_emad_pack_op_tlv(buf, reg, type, tid);
mlxsw_emad_construct_eth_hdr(skb);
}
struct mlxsw_emad_tlv_offsets {
u16 op_tlv;
u16 string_tlv;
u16 reg_tlv;
};
static bool mlxsw_emad_tlv_is_string_tlv(const char *tlv)
{
u8 tlv_type = mlxsw_emad_string_tlv_type_get(tlv);
return tlv_type == MLXSW_EMAD_TLV_TYPE_STRING;
}
static void mlxsw_emad_tlv_parse(struct sk_buff *skb)
{
struct mlxsw_emad_tlv_offsets *offsets =
(struct mlxsw_emad_tlv_offsets *) skb->cb;
offsets->op_tlv = MLXSW_EMAD_ETH_HDR_LEN;
offsets->string_tlv = 0;
offsets->reg_tlv = MLXSW_EMAD_ETH_HDR_LEN +
MLXSW_EMAD_OP_TLV_LEN * sizeof(u32);
/* If string TLV is present, it must come after the operation TLV. */
if (mlxsw_emad_tlv_is_string_tlv(skb->data + offsets->reg_tlv)) {
offsets->string_tlv = offsets->reg_tlv;
offsets->reg_tlv += MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32);
}
}
static char *mlxsw_emad_op_tlv(const struct sk_buff *skb)
{
return ((char *) (skb->data + MLXSW_EMAD_ETH_HDR_LEN));
struct mlxsw_emad_tlv_offsets *offsets =
(struct mlxsw_emad_tlv_offsets *) skb->cb;
return ((char *) (skb->data + offsets->op_tlv));
}
static char *mlxsw_emad_string_tlv(const struct sk_buff *skb)
{
struct mlxsw_emad_tlv_offsets *offsets =
(struct mlxsw_emad_tlv_offsets *) skb->cb;
if (!offsets->string_tlv)
return NULL;
return ((char *) (skb->data + offsets->string_tlv));
}
static char *mlxsw_emad_reg_tlv(const struct sk_buff *skb)
{
return ((char *) (skb->data + MLXSW_EMAD_ETH_HDR_LEN +
MLXSW_EMAD_OP_TLV_LEN * sizeof(u32)));
struct mlxsw_emad_tlv_offsets *offsets =
(struct mlxsw_emad_tlv_offsets *) skb->cb;
return ((char *) (skb->data + offsets->reg_tlv));
}
static char *mlxsw_emad_reg_payload(const char *reg_tlv)
{
return ((char *) (reg_tlv + sizeof(u32)));
}
static char *mlxsw_emad_reg_payload(const char *op_tlv)
static char *mlxsw_emad_reg_payload_cmd(const char *mbox)
{
return ((char *) (op_tlv + (MLXSW_EMAD_OP_TLV_LEN + 1) * sizeof(u32)));
return ((char *) (mbox + (MLXSW_EMAD_OP_TLV_LEN + 1) * sizeof(u32)));
}
static u64 mlxsw_emad_get_tid(const struct sk_buff *skb)
......@@ -440,10 +522,31 @@ struct mlxsw_reg_trans {
const struct mlxsw_reg_info *reg;
enum mlxsw_core_reg_access_type type;
int err;
char *emad_err_string;
enum mlxsw_emad_op_tlv_status emad_status;
struct rcu_head rcu;
};
static void mlxsw_emad_process_string_tlv(const struct sk_buff *skb,
struct mlxsw_reg_trans *trans)
{
char *string_tlv;
char *string;
string_tlv = mlxsw_emad_string_tlv(skb);
if (!string_tlv)
return;
trans->emad_err_string = kzalloc(MLXSW_EMAD_STRING_TLV_STRING_LEN,
GFP_ATOMIC);
if (!trans->emad_err_string)
return;
string = mlxsw_emad_string_tlv_string_data(string_tlv);
strlcpy(trans->emad_err_string, string,
MLXSW_EMAD_STRING_TLV_STRING_LEN);
}
#define MLXSW_EMAD_TIMEOUT_DURING_FW_FLASH_MS 3000
#define MLXSW_EMAD_TIMEOUT_MS 200
......@@ -535,12 +638,14 @@ static void mlxsw_emad_process_response(struct mlxsw_core *mlxsw_core,
mlxsw_emad_transmit_retry(mlxsw_core, trans);
} else {
if (err == 0) {
char *op_tlv = mlxsw_emad_op_tlv(skb);
char *reg_tlv = mlxsw_emad_reg_tlv(skb);
if (trans->cb)
trans->cb(mlxsw_core,
mlxsw_emad_reg_payload(op_tlv),
mlxsw_emad_reg_payload(reg_tlv),
trans->reg->len, trans->cb_priv);
} else {
mlxsw_emad_process_string_tlv(skb, trans);
}
mlxsw_emad_trans_finish(trans, err);
}
......@@ -556,6 +661,8 @@ static void mlxsw_emad_rx_listener_func(struct sk_buff *skb, u8 local_port,
trace_devlink_hwmsg(priv_to_devlink(mlxsw_core), true, 0,
skb->data, skb->len);
mlxsw_emad_tlv_parse(skb);
if (!mlxsw_emad_is_resp(skb))
goto free_skb;
......@@ -631,7 +738,7 @@ static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core)
}
static struct sk_buff *mlxsw_emad_alloc(const struct mlxsw_core *mlxsw_core,
u16 reg_len)
u16 reg_len, bool enable_string_tlv)
{
struct sk_buff *skb;
u16 emad_len;
......@@ -639,6 +746,8 @@ static struct sk_buff *mlxsw_emad_alloc(const struct mlxsw_core *mlxsw_core,
emad_len = (reg_len + sizeof(u32) + MLXSW_EMAD_ETH_HDR_LEN +
(MLXSW_EMAD_OP_TLV_LEN + MLXSW_EMAD_END_TLV_LEN) *
sizeof(u32) + mlxsw_core->driver->txhdr_len);
if (enable_string_tlv)
emad_len += MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32);
if (emad_len > MLXSW_EMAD_MAX_FRAME_LEN)
return NULL;
......@@ -660,6 +769,7 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core,
mlxsw_reg_trans_cb_t *cb,
unsigned long cb_priv, u64 tid)
{
bool enable_string_tlv;
struct sk_buff *skb;
int err;
......@@ -667,7 +777,12 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core,
tid, reg->id, mlxsw_reg_id_str(reg->id),
mlxsw_core_reg_access_type_str(type));
skb = mlxsw_emad_alloc(mlxsw_core, reg->len);
/* Since this can be changed during emad_reg_access, read it once and
* use the value all the way.
*/
enable_string_tlv = mlxsw_core->emad.enable_string_tlv;
skb = mlxsw_emad_alloc(mlxsw_core, reg->len, enable_string_tlv);
if (!skb)
return -ENOMEM;
......@@ -684,7 +799,8 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core,
trans->reg = reg;
trans->type = type;
mlxsw_emad_construct(skb, reg, payload, type, trans->tid);
mlxsw_emad_construct(skb, reg, payload, type, trans->tid,
enable_string_tlv);
mlxsw_core->driver->txhdr_construct(skb, &trans->tx_info);
spin_lock_bh(&mlxsw_core->emad.trans_list_lock);
......@@ -1395,12 +1511,16 @@ static void mlxsw_core_event_listener_func(struct sk_buff *skb, u8 local_port,
struct mlxsw_event_listener_item *event_listener_item = priv;
struct mlxsw_reg_info reg;
char *payload;
char *op_tlv = mlxsw_emad_op_tlv(skb);
char *reg_tlv = mlxsw_emad_reg_tlv(skb);
char *reg_tlv;
char *op_tlv;
mlxsw_emad_tlv_parse(skb);
op_tlv = mlxsw_emad_op_tlv(skb);
reg_tlv = mlxsw_emad_reg_tlv(skb);
reg.id = mlxsw_emad_op_tlv_register_id_get(op_tlv);
reg.len = (mlxsw_emad_reg_tlv_len_get(reg_tlv) - 1) * sizeof(u32);
payload = mlxsw_emad_reg_payload(op_tlv);
payload = mlxsw_emad_reg_payload(reg_tlv);
event_listener_item->el.func(&reg, payload, event_listener_item->priv);
dev_kfree_skb(skb);
}
......@@ -1618,8 +1738,11 @@ int mlxsw_reg_trans_write(struct mlxsw_core *mlxsw_core,
}
EXPORT_SYMBOL(mlxsw_reg_trans_write);
#define MLXSW_REG_TRANS_ERR_STRING_SIZE 256
static int mlxsw_reg_trans_wait(struct mlxsw_reg_trans *trans)
{
char err_string[MLXSW_REG_TRANS_ERR_STRING_SIZE];
struct mlxsw_core *mlxsw_core = trans->core;
int err;
......@@ -1637,9 +1760,17 @@ static int mlxsw_reg_trans_wait(struct mlxsw_reg_trans *trans)
mlxsw_core_reg_access_type_str(trans->type),
trans->emad_status,
mlxsw_emad_op_tlv_status_str(trans->emad_status));
snprintf(err_string, MLXSW_REG_TRANS_ERR_STRING_SIZE,
"(tid=%llx,reg_id=%x(%s)) %s (%s)\n", trans->tid,
trans->reg->id, mlxsw_reg_id_str(trans->reg->id),
mlxsw_emad_op_tlv_status_str(trans->emad_status),
trans->emad_err_string ? trans->emad_err_string : "");
trace_devlink_hwerr(priv_to_devlink(mlxsw_core),
trans->emad_status,
mlxsw_emad_op_tlv_status_str(trans->emad_status));
trans->emad_status, err_string);
kfree(trans->emad_err_string);
}
list_del(&trans->bulk_list);
......@@ -1713,7 +1844,7 @@ static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core,
}
if (!err)
memcpy(payload, mlxsw_emad_reg_payload(out_mbox),
memcpy(payload, mlxsw_emad_reg_payload_cmd(out_mbox),
reg->len);
mlxsw_cmd_mbox_free(out_mbox);
......@@ -2210,6 +2341,12 @@ u32 mlxsw_core_read_frc_l(struct mlxsw_core *mlxsw_core)
}
EXPORT_SYMBOL(mlxsw_core_read_frc_l);
void mlxsw_core_emad_string_tlv_enable(struct mlxsw_core *mlxsw_core)
{
mlxsw_core->emad.enable_string_tlv = true;
}
EXPORT_SYMBOL(mlxsw_core_emad_string_tlv_enable);
static int __init mlxsw_core_module_init(void)
{
int err;
......
......@@ -347,6 +347,8 @@ void mlxsw_core_fw_flash_end(struct mlxsw_core *mlxsw_core);
u32 mlxsw_core_read_frc_h(struct mlxsw_core *mlxsw_core);
u32 mlxsw_core_read_frc_l(struct mlxsw_core *mlxsw_core);
void mlxsw_core_emad_string_tlv_enable(struct mlxsw_core *mlxsw_core);
bool mlxsw_core_res_valid(struct mlxsw_core *mlxsw_core,
enum mlxsw_res_id res_id);
......
......@@ -19,10 +19,8 @@
enum {
MLXSW_EMAD_TLV_TYPE_END,
MLXSW_EMAD_TLV_TYPE_OP,
MLXSW_EMAD_TLV_TYPE_DR,
MLXSW_EMAD_TLV_TYPE_STRING,
MLXSW_EMAD_TLV_TYPE_REG,
MLXSW_EMAD_TLV_TYPE_USERDATA,
MLXSW_EMAD_TLV_TYPE_OOBETH,
};
/* OP TLV */
......@@ -89,6 +87,9 @@ enum {
MLXSW_EMAD_OP_TLV_METHOD_EVENT = 5,
};
/* STRING TLV */
#define MLXSW_EMAD_STRING_TLV_LEN 33 /* Length in u32 */
/* END TLV */
#define MLXSW_EMAD_END_TLV_LEN 1 /* Length in u32 */
......
......@@ -4894,6 +4894,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
if (err)
return err;
mlxsw_core_emad_string_tlv_enable(mlxsw_core);
err = mlxsw_sp_base_mac_get(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to get base mac\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment