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 = { ...@@ -43,8 +43,6 @@ const struct ethtool_ops ef100_ethtool_ops = {
.get_pauseparam = efx_ethtool_get_pauseparam, .get_pauseparam = efx_ethtool_get_pauseparam,
.set_pauseparam = efx_ethtool_set_pauseparam, .set_pauseparam = efx_ethtool_set_pauseparam,
.get_sset_count = efx_ethtool_get_sset_count, .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, .self_test = efx_ethtool_self_test,
.get_strings = efx_ethtool_get_strings, .get_strings = efx_ethtool_get_strings,
.get_link_ksettings = efx_ethtool_get_link_ksettings, .get_link_ksettings = efx_ethtool_get_link_ksettings,
......
...@@ -101,14 +101,6 @@ static const struct efx_sw_stat_desc efx_sw_stat_desc[] = { ...@@ -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) #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, void efx_ethtool_get_drvinfo(struct net_device *net_dev,
struct ethtool_drvinfo *info) struct ethtool_drvinfo *info)
{ {
...@@ -460,8 +452,6 @@ int efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set) ...@@ -460,8 +452,6 @@ int efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set)
efx_ptp_describe_stats(efx, NULL); efx_ptp_describe_stats(efx, NULL);
case ETH_SS_TEST: case ETH_SS_TEST:
return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL); return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL);
case ETH_SS_PRIV_FLAGS:
return EFX_ETHTOOL_PRIV_FLAGS_COUNT;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -488,39 +478,12 @@ void efx_ethtool_get_strings(struct net_device *net_dev, ...@@ -488,39 +478,12 @@ void efx_ethtool_get_strings(struct net_device *net_dev,
case ETH_SS_TEST: case ETH_SS_TEST:
efx_ethtool_fill_self_tests(efx, NULL, strings, NULL); efx_ethtool_fill_self_tests(efx, NULL, strings, NULL);
break; 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: default:
/* No other string sets */ /* No other string sets */
break; 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, void efx_ethtool_get_stats(struct net_device *net_dev,
struct ethtool_stats *stats, struct ethtool_stats *stats,
u64 *data) u64 *data)
......
...@@ -27,8 +27,6 @@ int efx_ethtool_fill_self_tests(struct efx_nic *efx, ...@@ -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); 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, void efx_ethtool_get_strings(struct net_device *net_dev, u32 string_set,
u8 *strings); 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, void efx_ethtool_get_stats(struct net_device *net_dev,
struct ethtool_stats *stats __attribute__ ((unused)), struct ethtool_stats *stats __attribute__ ((unused)),
u64 *data); u64 *data);
......
...@@ -265,9 +265,8 @@ int efx_mae_match_check_caps(struct efx_nic *efx, ...@@ -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], rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT],
ingress_port_mask_type); ingress_port_mask_type);
if (rc) { if (rc) {
efx_tc_err(efx, "No support for %s mask in field ingress_port\n", NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field ingress_port",
mask_type_name(ingress_port_mask_type)); mask_type_name(ingress_port_mask_type));
NL_SET_ERR_MSG_MOD(extack, "Unsupported mask type for ingress_port");
return rc; return rc;
} }
return 0; return 0;
......
...@@ -855,7 +855,6 @@ enum efx_xdp_tx_queues_mode { ...@@ -855,7 +855,6 @@ enum efx_xdp_tx_queues_mode {
* @timer_max_ns: Interrupt timer maximum value, in nanoseconds * @timer_max_ns: Interrupt timer maximum value, in nanoseconds
* @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues * @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues
* @irqs_hooked: Channel interrupts are hooked * @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_mod_step_us: Step size for IRQ moderation for RX event queues
* @irq_rx_moderation_us: IRQ moderation time for RX event queues * @irq_rx_moderation_us: IRQ moderation time for RX event queues
* @msg_enable: Log message enable flags * @msg_enable: Log message enable flags
...@@ -1018,7 +1017,6 @@ struct efx_nic { ...@@ -1018,7 +1017,6 @@ struct efx_nic {
unsigned int timer_max_ns; unsigned int timer_max_ns;
bool irq_rx_adaptive; bool irq_rx_adaptive;
bool irqs_hooked; bool irqs_hooked;
bool log_tc_errs;
unsigned int irq_mod_step_us; unsigned int irq_mod_step_us;
unsigned int irq_rx_moderation_us; unsigned int irq_rx_moderation_us;
u32 msg_enable; u32 msg_enable;
......
...@@ -137,17 +137,16 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, ...@@ -137,17 +137,16 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx,
flow_rule_match_control(rule, &fm); flow_rule_match_control(rule, &fm);
if (fm.mask->flags) { if (fm.mask->flags) {
efx_tc_err(efx, "Unsupported match on control.flags %#x\n", NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported match on control.flags %#x",
fm.mask->flags); fm.mask->flags);
NL_SET_ERR_MSG_MOD(extack, "Unsupported match on control.flags");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
} }
if (dissector->used_keys & if (dissector->used_keys &
~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
BIT(FLOW_DISSECTOR_KEY_BASIC))) { BIT(FLOW_DISSECTOR_KEY_BASIC))) {
efx_tc_err(efx, "Unsupported flower keys %#x\n", dissector->used_keys); NL_SET_ERR_MSG_FMT_MOD(extack, "Unsupported flower keys %#x",
NL_SET_ERR_MSG_MOD(extack, "Unsupported flower keys encountered"); dissector->used_keys);
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -156,11 +155,11 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx, ...@@ -156,11 +155,11 @@ static int efx_tc_flower_parse_match(struct efx_nic *efx,
flow_rule_match_basic(rule, &fm); flow_rule_match_basic(rule, &fm);
if (fm.mask->n_proto) { 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; return -EOPNOTSUPP;
} }
if (fm.mask->ip_proto) { 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; return -EOPNOTSUPP;
} }
} }
...@@ -200,13 +199,9 @@ static int efx_tc_flower_replace(struct efx_nic *efx, ...@@ -200,13 +199,9 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
if (efv != from_efv) { if (efv != from_efv) {
/* can't happen */ /* can't happen */
efx_tc_err(efx, "for %s efv is %snull but from_efv is %snull\n", 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-" : "", netdev_name(net_dev), efv ? "non-" : "",
from_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)");
return -EINVAL; return -EINVAL;
} }
...@@ -214,7 +209,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, ...@@ -214,7 +209,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
memset(&match, 0, sizeof(match)); memset(&match, 0, sizeof(match));
rc = efx_tc_flower_external_mport(efx, from_efv); rc = efx_tc_flower_external_mport(efx, from_efv);
if (rc < 0) { 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; return rc;
} }
match.value.ingress_port = rc; match.value.ingress_port = rc;
...@@ -224,7 +219,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, ...@@ -224,7 +219,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
return rc; return rc;
if (tc->common.chain_index) { 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; return -EOPNOTSUPP;
} }
match.mask.recirc_id = 0xff; match.mask.recirc_id = 0xff;
...@@ -261,7 +256,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, ...@@ -261,7 +256,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
if (!act) { if (!act) {
/* more actions after a non-pipe action */ /* 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; rc = -EINVAL;
goto release; goto release;
} }
...@@ -270,7 +265,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, ...@@ -270,7 +265,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
case FLOW_ACTION_DROP: case FLOW_ACTION_DROP:
rc = efx_mae_alloc_action_set(efx, act); rc = efx_mae_alloc_action_set(efx, act);
if (rc) { 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; goto release;
} }
list_add_tail(&act->list, &rule->acts.list); list_add_tail(&act->list, &rule->acts.list);
...@@ -281,20 +276,20 @@ static int efx_tc_flower_replace(struct efx_nic *efx, ...@@ -281,20 +276,20 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
save = *act; save = *act;
to_efv = efx_tc_flower_lookup_efv(efx, fa->dev); to_efv = efx_tc_flower_lookup_efv(efx, fa->dev);
if (IS_ERR(to_efv)) { 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); rc = PTR_ERR(to_efv);
goto release; goto release;
} }
rc = efx_tc_flower_external_mport(efx, to_efv); rc = efx_tc_flower_external_mport(efx, to_efv);
if (rc < 0) { 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; goto release;
} }
act->dest_mport = rc; act->dest_mport = rc;
act->deliver = 1; act->deliver = 1;
rc = efx_mae_alloc_action_set(efx, act); rc = efx_mae_alloc_action_set(efx, act);
if (rc) { 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; goto release;
} }
list_add_tail(&act->list, &rule->acts.list); list_add_tail(&act->list, &rule->acts.list);
...@@ -310,9 +305,9 @@ static int efx_tc_flower_replace(struct efx_nic *efx, ...@@ -310,9 +305,9 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
*act = save; *act = save;
break; break;
default: 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; rc = -EOPNOTSUPP;
NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
goto release; goto release;
} }
} }
...@@ -334,7 +329,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx, ...@@ -334,7 +329,7 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
act->deliver = 1; act->deliver = 1;
rc = efx_mae_alloc_action_set(efx, act); rc = efx_mae_alloc_action_set(efx, act);
if (rc) { 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; goto release;
} }
list_add_tail(&act->list, &rule->acts.list); list_add_tail(&act->list, &rule->acts.list);
...@@ -349,13 +344,13 @@ static int efx_tc_flower_replace(struct efx_nic *efx, ...@@ -349,13 +344,13 @@ static int efx_tc_flower_replace(struct efx_nic *efx,
rc = efx_mae_alloc_action_set_list(efx, &rule->acts); rc = efx_mae_alloc_action_set_list(efx, &rule->acts);
if (rc) { 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; goto release;
} }
rc = efx_mae_insert_rule(efx, &rule->match, EFX_TC_PRIO_TC, rc = efx_mae_insert_rule(efx, &rule->match, EFX_TC_PRIO_TC,
rule->acts.fw_id, &rule->fw_id); rule->acts.fw_id, &rule->fw_id);
if (rc) { 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; goto release_acts;
} }
return 0; return 0;
......
...@@ -15,24 +15,6 @@ ...@@ -15,24 +15,6 @@
#include <linux/rhashtable.h> #include <linux/rhashtable.h>
#include "net_driver.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 { struct efx_tc_action_set {
u16 deliver:1; u16 deliver:1;
u32 dest_mport; u32 dest_mport;
......
...@@ -64,6 +64,7 @@ netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg) ...@@ -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 */ /* this can be increased when necessary - don't expose to userland */
#define NETLINK_MAX_COOKIE_LEN 20 #define NETLINK_MAX_COOKIE_LEN 20
#define NETLINK_MAX_FMTMSG_LEN 80
/** /**
* struct netlink_ext_ack - netlink extended ACK report struct * 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) ...@@ -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) * @miss_nest: nest missing an attribute (%NULL if missing top level attr)
* @cookie: cookie data to return to userspace (for success) * @cookie: cookie data to return to userspace (for success)
* @cookie_len: actual cookie data length * @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 { struct netlink_ext_ack {
const char *_msg; const char *_msg;
...@@ -84,13 +87,13 @@ struct netlink_ext_ack { ...@@ -84,13 +87,13 @@ struct netlink_ext_ack {
u16 miss_type; u16 miss_type;
u8 cookie[NETLINK_MAX_COOKIE_LEN]; u8 cookie[NETLINK_MAX_COOKIE_LEN];
u8 cookie_len; u8 cookie_len;
char _msg_buf[NETLINK_MAX_FMTMSG_LEN];
}; };
/* Always use this macro, this allows later putting the /* Always use this macro, this allows later putting the
* message into a separate section or such for things * message into a separate section or such for things
* like translation or listing all possible messages. * like translation or listing all possible messages.
* Currently string formatting is not supported (due * If string formatting is needed use NL_SET_ERR_MSG_FMT.
* to the lack of an output buffer.)
*/ */
#define NL_SET_ERR_MSG(extack, msg) do { \ #define NL_SET_ERR_MSG(extack, msg) do { \
static const char __msg[] = msg; \ static const char __msg[] = msg; \
...@@ -102,9 +105,31 @@ struct netlink_ext_ack { ...@@ -102,9 +105,31 @@ struct netlink_ext_ack {
__extack->_msg = __msg; \ __extack->_msg = __msg; \
} while (0) } 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) \ #define NL_SET_ERR_MSG_MOD(extack, msg) \
NL_SET_ERR_MSG((extack), KBUILD_MODNAME ": " 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 { \ #define NL_SET_BAD_ATTR_POLICY(extack, attr, pol) do { \
if ((extack)) { \ if ((extack)) { \
(extack)->bad_attr = (attr); \ (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