1

net: ethtool: Add new power limit get and set features

This patch expands the status information provided by ethtool for PSE c33
with available power limit and available power limit ranges. It also adds
a call to pse_ethtool_set_pw_limit() to configure the PSE control power
limit.

Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
Link: https://patch.msgid.link/20240704-feature_poe_power_cap-v6-5-320003204264@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Kory Maincent (Dent Project) 2024-07-04 10:12:00 +02:00 committed by Jakub Kicinski
parent 4a83abcef5
commit 30d7b67277
4 changed files with 142 additions and 26 deletions

View File

@ -1730,24 +1730,28 @@ Request contents:
Kernel response contents:
====================================== ====== =============================
``ETHTOOL_A_PSE_HEADER`` nested reply header
``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` u32 Operational state of the PoDL
PSE functions
``ETHTOOL_A_PODL_PSE_PW_D_STATUS`` u32 power detection status of the
PoDL PSE.
``ETHTOOL_A_C33_PSE_ADMIN_STATE`` u32 Operational state of the PoE
PSE functions.
``ETHTOOL_A_C33_PSE_PW_D_STATUS`` u32 power detection status of the
PoE PSE.
``ETHTOOL_A_C33_PSE_PW_CLASS`` u32 power class of the PoE PSE.
``ETHTOOL_A_C33_PSE_ACTUAL_PW`` u32 actual power drawn on the
PoE PSE.
``ETHTOOL_A_C33_PSE_EXT_STATE`` u32 power extended state of the
PoE PSE.
``ETHTOOL_A_C33_PSE_EXT_SUBSTATE`` u32 power extended substatus of
the PoE PSE.
====================================== ====== =============================
========================================== ====== =============================
``ETHTOOL_A_PSE_HEADER`` nested reply header
``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` u32 Operational state of the PoDL
PSE functions
``ETHTOOL_A_PODL_PSE_PW_D_STATUS`` u32 power detection status of the
PoDL PSE.
``ETHTOOL_A_C33_PSE_ADMIN_STATE`` u32 Operational state of the PoE
PSE functions.
``ETHTOOL_A_C33_PSE_PW_D_STATUS`` u32 power detection status of the
PoE PSE.
``ETHTOOL_A_C33_PSE_PW_CLASS`` u32 power class of the PoE PSE.
``ETHTOOL_A_C33_PSE_ACTUAL_PW`` u32 actual power drawn on the
PoE PSE.
``ETHTOOL_A_C33_PSE_EXT_STATE`` u32 power extended state of the
PoE PSE.
``ETHTOOL_A_C33_PSE_EXT_SUBSTATE`` u32 power extended substatus of
the PoE PSE.
``ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT`` u32 currently configured power
limit of the PoE PSE.
``ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES`` nested Supported power limit
configuration ranges.
========================================== ====== =============================
When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` attribute identifies
the operational state of the PoDL PSE functions. The operational state of the
@ -1809,6 +1813,16 @@ Possible values are:
ethtool_c33_pse_ext_substate_power_not_available
ethtool_c33_pse_ext_substate_short_detected
When set, the optional ``ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT`` attribute
identifies the C33 PSE power limit in mW.
When set the optional ``ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES`` nested attribute
identifies the C33 PSE power limit ranges through
``ETHTOOL_A_C33_PSE_PWR_VAL_LIMIT_RANGE_MIN`` and
``ETHTOOL_A_C33_PSE_PWR_VAL_LIMIT_RANGE_MAX``.
If the controller works with fixed classes, the min and max values will be
equal.
PSE_SET
=======
@ -1820,6 +1834,8 @@ Request contents:
``ETHTOOL_A_PSE_HEADER`` nested request header
``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` u32 Control PoDL PSE Admin state
``ETHTOOL_A_C33_PSE_ADMIN_CONTROL`` u32 Control PSE Admin state
``ETHTOOL_A_C33_PSE_AVAIL_PWR_LIMIT`` u32 Control PoE PSE available
power limit
====================================== ====== =============================
When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` attribute is used
@ -1830,6 +1846,18 @@ to control PoDL PSE Admin functions. This option is implementing
The same goes for ``ETHTOOL_A_C33_PSE_ADMIN_CONTROL`` implementing
``IEEE 802.3-2022`` 30.9.1.2.1 acPSEAdminControl.
When set, the optional ``ETHTOOL_A_C33_PSE_AVAIL_PWR_LIMIT`` attribute is
used to control the available power value limit for C33 PSE in milliwatts.
This attribute corresponds to the `pse_available_power` variable described in
``IEEE 802.3-2022`` 33.2.4.4 Variables and `pse_avail_pwr` in 145.2.5.4
Variables, which are described in power classes.
It was decided to use milliwatts for this interface to unify it with other
power monitoring interfaces, which also use milliwatts, and to align with
various existing products that document power consumption in watts rather than
classes. If power limit configuration based on classes is needed, the
conversion can be done in user space, for example by ethtool.
RSS_GET
=======

View File

@ -1288,4 +1288,9 @@ struct ethtool_c33_pse_ext_state_info {
u32 __c33_pse_ext_substate;
};
};
struct ethtool_c33_pse_pw_limit_range {
u32 min;
u32 max;
};
#endif /* _LINUX_ETHTOOL_H */

View File

@ -930,6 +930,12 @@ enum {
};
/* Power Sourcing Equipment */
enum {
ETHTOOL_A_C33_PSE_PW_LIMIT_UNSPEC,
ETHTOOL_A_C33_PSE_PW_LIMIT_MIN, /* u32 */
ETHTOOL_A_C33_PSE_PW_LIMIT_MAX, /* u32 */
};
enum {
ETHTOOL_A_PSE_UNSPEC,
ETHTOOL_A_PSE_HEADER, /* nest - _A_HEADER_* */
@ -943,6 +949,8 @@ enum {
ETHTOOL_A_C33_PSE_ACTUAL_PW, /* u32 */
ETHTOOL_A_C33_PSE_EXT_STATE, /* u32 */
ETHTOOL_A_C33_PSE_EXT_SUBSTATE, /* u32 */
ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT, /* u32 */
ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES, /* nest - _C33_PSE_PW_LIMIT_* */
/* add new constants above here */
__ETHTOOL_A_PSE_CNT,

View File

@ -96,9 +96,46 @@ static int pse_reply_size(const struct ethnl_req_info *req_base,
/* _C33_PSE_EXT_SUBSTATE */
len += nla_total_size(sizeof(u32));
}
if (st->c33_avail_pw_limit > 0)
/* _C33_AVAIL_PSE_PW_LIMIT */
len += nla_total_size(sizeof(u32));
if (st->c33_pw_limit_nb_ranges > 0)
/* _C33_PSE_PW_LIMIT_RANGES */
len += st->c33_pw_limit_nb_ranges *
(nla_total_size(0) +
nla_total_size(sizeof(u32)) * 2);
return len;
}
static int pse_put_pw_limit_ranges(struct sk_buff *skb,
const struct pse_control_status *st)
{
const struct ethtool_c33_pse_pw_limit_range *pw_limit_ranges;
int i;
pw_limit_ranges = st->c33_pw_limit_ranges;
for (i = 0; i < st->c33_pw_limit_nb_ranges; i++) {
struct nlattr *nest;
nest = nla_nest_start(skb, ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES);
if (!nest)
return -EMSGSIZE;
if (nla_put_u32(skb, ETHTOOL_A_C33_PSE_PW_LIMIT_MIN,
pw_limit_ranges->min) ||
nla_put_u32(skb, ETHTOOL_A_C33_PSE_PW_LIMIT_MAX,
pw_limit_ranges->max)) {
nla_nest_cancel(skb, nest);
return -EMSGSIZE;
}
nla_nest_end(skb, nest);
pw_limit_ranges++;
}
return 0;
}
static int pse_fill_reply(struct sk_buff *skb,
const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
@ -147,9 +184,25 @@ static int pse_fill_reply(struct sk_buff *skb,
return -EMSGSIZE;
}
if (st->c33_avail_pw_limit > 0 &&
nla_put_u32(skb, ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT,
st->c33_avail_pw_limit))
return -EMSGSIZE;
if (st->c33_pw_limit_nb_ranges > 0 &&
pse_put_pw_limit_ranges(skb, st))
return -EMSGSIZE;
return 0;
}
static void pse_cleanup_data(struct ethnl_reply_data *reply_base)
{
const struct pse_reply_data *data = PSE_REPDATA(reply_base);
kfree(data->status.c33_pw_limit_ranges);
}
/* PSE_SET */
const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = {
@ -160,6 +213,7 @@ const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = {
[ETHTOOL_A_C33_PSE_ADMIN_CONTROL] =
NLA_POLICY_RANGE(NLA_U32, ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED,
ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED),
[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT] = { .type = NLA_U32 },
};
static int
@ -202,19 +256,39 @@ static int
ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info)
{
struct net_device *dev = req_info->dev;
struct pse_control_config config = {};
struct nlattr **tb = info->attrs;
struct phy_device *phydev;
int ret = 0;
phydev = dev->phydev;
/* These values are already validated by the ethnl_pse_set_policy */
if (pse_has_podl(phydev->psec))
config.podl_admin_control = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]);
if (pse_has_c33(phydev->psec))
config.c33_admin_control = nla_get_u32(tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL]);
/* Return errno directly - PSE has no notification */
return pse_ethtool_set_config(phydev->psec, info->extack, &config);
if (tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]) {
unsigned int pw_limit;
pw_limit = nla_get_u32(tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]);
ret = pse_ethtool_set_pw_limit(phydev->psec, info->extack,
pw_limit);
if (ret)
return ret;
}
/* These values are already validated by the ethnl_pse_set_policy */
if (tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL] ||
tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL]) {
struct pse_control_config config = {};
if (pse_has_podl(phydev->psec))
config.podl_admin_control = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]);
if (pse_has_c33(phydev->psec))
config.c33_admin_control = nla_get_u32(tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL]);
ret = pse_ethtool_set_config(phydev->psec, info->extack,
&config);
if (ret)
return ret;
}
return ret;
}
const struct ethnl_request_ops ethnl_pse_request_ops = {
@ -227,6 +301,7 @@ const struct ethnl_request_ops ethnl_pse_request_ops = {
.prepare_data = pse_prepare_data,
.reply_size = pse_reply_size,
.fill_reply = pse_fill_reply,
.cleanup_data = pse_cleanup_data,
.set_validate = ethnl_set_pse_validate,
.set = ethnl_set_pse,