Commit 5c624a1d authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'netlink-formatted-extacks'

Edward Cree says:

====================
netlink: formatted extacks

Currently, netlink extacks can only carry fixed string messages, which
 is limiting when reporting failures in complex systems.  This series
 adds the ability to return printf-formatted messages, and uses it in
 the sfc driver's TC offload code.
Formatted extack messages are limited in length to a fixed buffer size,
 currently 80 characters.  If the message exceeds this, the full message
 will be logged (ratelimited) to the console and a truncated version
 returned over netlink.
There is no change to the netlink uAPI; only internal kernel changes
 are needed.
====================

Link: https://lore.kernel.org/r/cover.1666102698.git.ecree.xilinx@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents a526a3cc b799f052
......@@ -43,8 +43,6 @@ const struct ethtool_ops ef100_ethtool_ops = {
.get_pauseparam = efx_ethtool_get_pauseparam,
.set_pauseparam = efx_ethtool_set_pauseparam,
.get_sset_count = efx_ethtool_get_sset_count,
.get_priv_flags = efx_ethtool_get_priv_flags,
.set_priv_flags = efx_ethtool_set_priv_flags,
.self_test = efx_ethtool_self_test,
.get_strings = efx_ethtool_get_strings,
.get_link_ksettings = efx_ethtool_get_link_ksettings,
......
......@@ -101,14 +101,6 @@ static const struct efx_sw_stat_desc efx_sw_stat_desc[] = {
#define EFX_ETHTOOL_SW_STAT_COUNT ARRAY_SIZE(efx_sw_stat_desc)
static const char efx_ethtool_priv_flags_strings[][ETH_GSTRING_LEN] = {
"log-tc-errors",
};
#define EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS BIT(0)
#define EFX_ETHTOOL_PRIV_FLAGS_COUNT ARRAY_SIZE(efx_ethtool_priv_flags_strings)
void efx_ethtool_get_drvinfo(struct net_device *net_dev,
struct ethtool_drvinfo *info)
{
......@@ -460,8 +452,6 @@ int efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set)
efx_ptp_describe_stats(efx, NULL);
case ETH_SS_TEST:
return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL);
case ETH_SS_PRIV_FLAGS:
return EFX_ETHTOOL_PRIV_FLAGS_COUNT;
default:
return -EINVAL;
}
......@@ -488,39 +478,12 @@ void efx_ethtool_get_strings(struct net_device *net_dev,
case ETH_SS_TEST:
efx_ethtool_fill_self_tests(efx, NULL, strings, NULL);
break;
case ETH_SS_PRIV_FLAGS:
for (i = 0; i < EFX_ETHTOOL_PRIV_FLAGS_COUNT; i++)
strscpy(strings + i * ETH_GSTRING_LEN,
efx_ethtool_priv_flags_strings[i],
ETH_GSTRING_LEN);
break;
default:
/* No other string sets */
break;
}
}
u32 efx_ethtool_get_priv_flags(struct net_device *net_dev)
{
struct efx_nic *efx = efx_netdev_priv(net_dev);
u32 ret_flags = 0;
if (efx->log_tc_errs)
ret_flags |= EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS;
return ret_flags;
}
int efx_ethtool_set_priv_flags(struct net_device *net_dev, u32 flags)
{
struct efx_nic *efx = efx_netdev_priv(net_dev);
efx->log_tc_errs =
!!(flags & EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS);
return 0;
}
void efx_ethtool_get_stats(struct net_device *net_dev,
struct ethtool_stats *stats,
u64 *data)
......
......@@ -27,8 +27,6 @@ int efx_ethtool_fill_self_tests(struct efx_nic *efx,
int efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set);
void efx_ethtool_get_strings(struct net_device *net_dev, u32 string_set,
u8 *strings);
u32 efx_ethtool_get_priv_flags(struct net_device *net_dev);
int efx_ethtool_set_priv_flags(struct net_device *net_dev, u32 flags);
void efx_ethtool_get_stats(struct net_device *net_dev,
struct ethtool_stats *stats __attribute__ ((unused)),
u64 *data);
......
......@@ -265,9 +265,8 @@ int efx_mae_match_check_caps(struct efx_nic *efx,
rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT],
ingress_port_mask_type);
if (rc) {
efx_tc_err(efx, "No support for %s mask in field ingress_port\n",
mask_type_name(ingress_port_mask_type));
NL_SET_ERR_MSG_MOD(extack, "Unsupported mask type for ingress_port");
NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field ingress_port",
mask_type_name(ingress_port_mask_type));
return rc;
}
return 0;
......
......@@ -855,7 +855,6 @@ enum efx_xdp_tx_queues_mode {
* @timer_max_ns: Interrupt timer maximum value, in nanoseconds
* @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues
* @irqs_hooked: Channel interrupts are hooked
* @log_tc_errs: Error logging for TC filter insertion is enabled
* @irq_rx_mod_step_us: Step size for IRQ moderation for RX event queues
* @irq_rx_moderation_us: IRQ moderation time for RX event queues
* @msg_enable: Log message enable flags
......@@ -1018,7 +1017,6 @@ struct efx_nic {
unsigned int timer_max_ns;
bool irq_rx_adaptive;
bool irqs_hooked;
bool log_tc_errs;
unsigned int irq_mod_step_us;
unsigned int irq_rx_moderation_us;
u32 msg_enable;
......
......@@ -137,17 +137,16 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx,
flow_rule_match_control(rule, &fm);
if (fm.mask->flags) {
efx_tc_err(efx, "Unsupported match on control.flags %#x\n",
fm.mask->flags);
NL_SET_ERR_MSG_MOD(extack, "Unsupported match on control.flags");
NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported match on control.flags %#x",
fm.mask->flags);
return -EOPNOTSUPP;
}
}
if (dissector->used_keys &
~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
BIT(FLOW_DISSECTOR_KEY_BASIC))) {
efx_tc_err(efx, "Unsupported flower keys %#x\n", dissector->used_keys);
NL_SET_ERR_MSG_MOD(extack, "Unsupported flower keys encountered");
NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported flower keys %#x",
dissector->used_keys);
return -EOPNOTSUPP;
}
......@@ -156,11 +155,11 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx,
flow_rule_match_basic(rule, &fm);
if (fm.mask->n_proto) {
EFX_TC_ERR_MSG(efx, extack, "Unsupported eth_proto match\n");
NL_SET_ERR_MSG_MOD(extack, "Unsupported eth_proto match");
return -EOPNOTSUPP;
}
if (fm.mask->ip_proto) {
EFX_TC_ERR_MSG(efx, extack, "Unsupported ip_proto match\n");
NL_SET_ERR_MSG_MOD(extack, "Unsupported ip_proto match");
return -EOPNOTSUPP;
}
}
......@@ -200,13 +199,9 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
if (efv != from_efv) {
/* can't happen */
efx_tc_err(efx, "for %s efv is %snull but from_efv is %snull\n",
netdev_name(net_dev), efv ? "non-" : "",
from_efv ? "non-" : "");
if (efv)
NL_SET_ERR_MSG_MOD(extack, "vfrep filter has PF net_dev (can't happen)");
else
NL_SET_ERR_MSG_MOD(extack, "PF filter has vfrep net_dev (can't happen)");
NL_SET_ERR_MSG_FMT_MOD(extack, "for %s efv is %snull but from_efv is %snull (can't happen)",
netdev_name(net_dev), efv ? "non-" : "",
from_efv ? "non-" : "");
return -EINVAL;
}
......@@ -214,7 +209,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
memset(&match, 0, sizeof(match));
rc = efx_tc_flower_external_mport(efx, from_efv);
if (rc < 0) {
EFX_TC_ERR_MSG(efx, extack, "Failed to identify ingress m-port");
NL_SET_ERR_MSG_MOD(extack, "Failed to identify ingress m-port");
return rc;
}
match.value.ingress_port = rc;
......@@ -224,7 +219,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
return rc;
if (tc->common.chain_index) {
EFX_TC_ERR_MSG(efx, extack, "No support for nonzero chain_index");
NL_SET_ERR_MSG_MOD(extack, "No support for nonzero chain_index");
return -EOPNOTSUPP;
}
match.mask.recirc_id = 0xff;
......@@ -261,7 +256,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
if (!act) {
/* more actions after a non-pipe action */
EFX_TC_ERR_MSG(efx, extack, "Action follows non-pipe action");
NL_SET_ERR_MSG_MOD(extack, "Action follows non-pipe action");
rc = -EINVAL;
goto release;
}
......@@ -270,7 +265,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
case FLOW_ACTION_DROP:
rc = efx_mae_alloc_action_set(efx, act);
if (rc) {
EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (drop)");
NL_SET_ERR_MSG_MOD(extack, "Failed to write action set to hw (drop)");
goto release;
}
list_add_tail(&act->list, &rule->acts.list);
......@@ -281,20 +276,20 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
save = *act;
to_efv = efx_tc_flower_lookup_efv(efx, fa->dev);
if (IS_ERR(to_efv)) {
EFX_TC_ERR_MSG(efx, extack, "Mirred egress device not on switch");
NL_SET_ERR_MSG_MOD(extack, "Mirred egress device not on switch");
rc = PTR_ERR(to_efv);
goto release;
}
rc = efx_tc_flower_external_mport(efx, to_efv);
if (rc < 0) {
EFX_TC_ERR_MSG(efx, extack, "Failed to identify egress m-port");
NL_SET_ERR_MSG_MOD(extack, "Failed to identify egress m-port");
goto release;
}
act->dest_mport = rc;
act->deliver = 1;
rc = efx_mae_alloc_action_set(efx, act);
if (rc) {
EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (mirred)");
NL_SET_ERR_MSG_MOD(extack, "Failed to write action set to hw (mirred)");
goto release;
}
list_add_tail(&act->list, &rule->acts.list);
......@@ -310,9 +305,9 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
*act = save;
break;
default:
efx_tc_err(efx, "Unhandled action %u\n", fa->id);
NL_SET_ERR_MSG_FMT_MOD(extack, "Unhandled action %u",
fa->id);
rc = -EOPNOTSUPP;
NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
goto release;
}
}
......@@ -334,7 +329,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
act->deliver = 1;
rc = efx_mae_alloc_action_set(efx, act);
if (rc) {
EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (deliver)");
NL_SET_ERR_MSG_MOD(extack, "Failed to write action set to hw (deliver)");
goto release;
}
list_add_tail(&act->list, &rule->acts.list);
......@@ -349,13 +344,13 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
rc = efx_mae_alloc_action_set_list(efx, &rule->acts);
if (rc) {
EFX_TC_ERR_MSG(efx, extack, "Failed to write action set list to hw");
NL_SET_ERR_MSG_MOD(extack, "Failed to write action set list to hw");
goto release;
}
rc = efx_mae_insert_rule(efx, &rule->match, EFX_TC_PRIO_TC,
rule->acts.fw_id, &rule->fw_id);
if (rc) {
EFX_TC_ERR_MSG(efx, extack, "Failed to insert rule in hw");
NL_SET_ERR_MSG_MOD(extack, "Failed to insert rule in hw");
goto release_acts;
}
return 0;
......
......@@ -15,24 +15,6 @@
#include <linux/rhashtable.h>
#include "net_driver.h"
/* Error reporting: convenience macros. For indicating why a given filter
* insertion is not supported; errors in internal operation or in the
* hardware should be netif_err()s instead.
*/
/* Used when error message is constant. */
#define EFX_TC_ERR_MSG(efx, extack, message) do { \
NL_SET_ERR_MSG_MOD(extack, message); \
if (efx->log_tc_errs) \
netif_info(efx, drv, efx->net_dev, "%s\n", message); \
} while (0)
/* Used when error message is not constant; caller should also supply a
* constant extack message with NL_SET_ERR_MSG_MOD().
*/
#define efx_tc_err(efx, fmt, args...) do { \
if (efx->log_tc_errs) \
netif_info(efx, drv, efx->net_dev, fmt, ##args);\
} while (0)
struct efx_tc_action_set {
u16 deliver:1;
u32 dest_mport;
......
......@@ -64,6 +64,7 @@ netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
/* this can be increased when necessary - don't expose to userland */
#define NETLINK_MAX_COOKIE_LEN 20
#define NETLINK_MAX_FMTMSG_LEN 80
/**
* struct netlink_ext_ack - netlink extended ACK report struct
......@@ -75,6 +76,8 @@ netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
* @miss_nest: nest missing an attribute (%NULL if missing top level attr)
* @cookie: cookie data to return to userspace (for success)
* @cookie_len: actual cookie data length
* @_msg_buf: output buffer for formatted message strings - don't access
* directly, use %NL_SET_ERR_MSG_FMT
*/
struct netlink_ext_ack {
const char *_msg;
......@@ -84,13 +87,13 @@ struct netlink_ext_ack {
u16 miss_type;
u8 cookie[NETLINK_MAX_COOKIE_LEN];
u8 cookie_len;
char _msg_buf[NETLINK_MAX_FMTMSG_LEN];
};
/* Always use this macro, this allows later putting the
* message into a separate section or such for things
* like translation or listing all possible messages.
* Currently string formatting is not supported (due
* to the lack of an output buffer.)
* If string formatting is needed use NL_SET_ERR_MSG_FMT.
*/
#define NL_SET_ERR_MSG(extack, msg) do { \
static const char __msg[] = msg; \
......@@ -102,9 +105,31 @@ struct netlink_ext_ack {
__extack->_msg = __msg; \
} while (0)
/* We splice fmt with %s at each end even in the snprintf so that both calls
* can use the same string constant, avoiding its duplication in .ro
*/
#define NL_SET_ERR_MSG_FMT(extack, fmt, args...) do { \
struct netlink_ext_ack *__extack = (extack); \
\
if (!__extack) \
break; \
if (snprintf(__extack->_msg_buf, NETLINK_MAX_FMTMSG_LEN, \
"%s" fmt "%s", "", ##args, "") >= \
NETLINK_MAX_FMTMSG_LEN) \
net_warn_ratelimited("%s" fmt "%s", "truncated extack: ", \
##args, "\n"); \
\
do_trace_netlink_extack(__extack->_msg_buf); \
\
__extack->_msg = __extack->_msg_buf; \
} while (0)
#define NL_SET_ERR_MSG_MOD(extack, msg) \
NL_SET_ERR_MSG((extack), KBUILD_MODNAME ": " msg)
#define NL_SET_ERR_MSG_FMT_MOD(extack, fmt, args...) \
NL_SET_ERR_MSG_FMT((extack), KBUILD_MODNAME ": " fmt, ##args)
#define NL_SET_BAD_ATTR_POLICY(extack, attr, pol) do { \
if ((extack)) { \
(extack)->bad_attr = (attr); \
......
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