forked from mirrors/linux
		
	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:
		
							parent
							
								
									4a83abcef5
								
							
						
					
					
						commit
						30d7b67277
					
				
					 4 changed files with 142 additions and 26 deletions
				
			
		|  | @ -1730,24 +1730,28 @@ Request contents: | ||||||
| 
 | 
 | ||||||
| Kernel response contents: | Kernel response contents: | ||||||
| 
 | 
 | ||||||
|   ======================================  ======  ============================= |   ==========================================  ======  ============================= | ||||||
|   ``ETHTOOL_A_PSE_HEADER``                nested  reply header |   ``ETHTOOL_A_PSE_HEADER``                    nested  reply header | ||||||
|   ``ETHTOOL_A_PODL_PSE_ADMIN_STATE``         u32  Operational state of the PoDL |   ``ETHTOOL_A_PODL_PSE_ADMIN_STATE``             u32  Operational state of the PoDL | ||||||
|                                                   PSE functions |                                                       PSE functions | ||||||
|   ``ETHTOOL_A_PODL_PSE_PW_D_STATUS``         u32  power detection status of the |   ``ETHTOOL_A_PODL_PSE_PW_D_STATUS``             u32  power detection status of the | ||||||
|                                                   PoDL PSE. |                                                       PoDL PSE. | ||||||
|   ``ETHTOOL_A_C33_PSE_ADMIN_STATE``          u32  Operational state of the PoE |   ``ETHTOOL_A_C33_PSE_ADMIN_STATE``              u32  Operational state of the PoE | ||||||
|                                                   PSE functions. |                                                       PSE functions. | ||||||
|   ``ETHTOOL_A_C33_PSE_PW_D_STATUS``          u32  power detection status of the |   ``ETHTOOL_A_C33_PSE_PW_D_STATUS``              u32  power detection status of the | ||||||
|                                                   PoE PSE. |                                                       PoE PSE. | ||||||
|   ``ETHTOOL_A_C33_PSE_PW_CLASS``             u32  power class 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 |   ``ETHTOOL_A_C33_PSE_ACTUAL_PW``                u32  actual power drawn on the | ||||||
|                                                   PoE PSE. |                                                       PoE PSE. | ||||||
|   ``ETHTOOL_A_C33_PSE_EXT_STATE``            u32  power extended state of the |   ``ETHTOOL_A_C33_PSE_EXT_STATE``                u32  power extended state of the | ||||||
|                                                   PoE PSE. |                                                       PoE PSE. | ||||||
|   ``ETHTOOL_A_C33_PSE_EXT_SUBSTATE``         u32  power extended substatus of |   ``ETHTOOL_A_C33_PSE_EXT_SUBSTATE``             u32  power extended substatus of | ||||||
|                                                   the PoE PSE. |                                                       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 | 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 | 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_power_not_available | ||||||
| 		  ethtool_c33_pse_ext_substate_short_detected | 		  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 | PSE_SET | ||||||
| ======= | ======= | ||||||
| 
 | 
 | ||||||
|  | @ -1820,6 +1834,8 @@ Request contents: | ||||||
|   ``ETHTOOL_A_PSE_HEADER``                nested  request header |   ``ETHTOOL_A_PSE_HEADER``                nested  request header | ||||||
|   ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL``       u32  Control PoDL PSE Admin state |   ``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_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 | 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 | The same goes for ``ETHTOOL_A_C33_PSE_ADMIN_CONTROL`` implementing | ||||||
| ``IEEE 802.3-2022`` 30.9.1.2.1 acPSEAdminControl. | ``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 | RSS_GET | ||||||
| ======= | ======= | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1288,4 +1288,9 @@ struct ethtool_c33_pse_ext_state_info { | ||||||
| 		u32 __c33_pse_ext_substate; | 		u32 __c33_pse_ext_substate; | ||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | struct ethtool_c33_pse_pw_limit_range { | ||||||
|  | 	u32 min; | ||||||
|  | 	u32 max; | ||||||
|  | }; | ||||||
| #endif /* _LINUX_ETHTOOL_H */ | #endif /* _LINUX_ETHTOOL_H */ | ||||||
|  |  | ||||||
|  | @ -930,6 +930,12 @@ enum { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* Power Sourcing Equipment */ | /* 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 { | enum { | ||||||
| 	ETHTOOL_A_PSE_UNSPEC, | 	ETHTOOL_A_PSE_UNSPEC, | ||||||
| 	ETHTOOL_A_PSE_HEADER,			/* nest - _A_HEADER_* */ | 	ETHTOOL_A_PSE_HEADER,			/* nest - _A_HEADER_* */ | ||||||
|  | @ -943,6 +949,8 @@ enum { | ||||||
| 	ETHTOOL_A_C33_PSE_ACTUAL_PW,		/* u32 */ | 	ETHTOOL_A_C33_PSE_ACTUAL_PW,		/* u32 */ | ||||||
| 	ETHTOOL_A_C33_PSE_EXT_STATE,		/* u32 */ | 	ETHTOOL_A_C33_PSE_EXT_STATE,		/* u32 */ | ||||||
| 	ETHTOOL_A_C33_PSE_EXT_SUBSTATE,		/* 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 */ | 	/* add new constants above here */ | ||||||
| 	__ETHTOOL_A_PSE_CNT, | 	__ETHTOOL_A_PSE_CNT, | ||||||
|  |  | ||||||
|  | @ -96,9 +96,46 @@ static int pse_reply_size(const struct ethnl_req_info *req_base, | ||||||
| 			/* _C33_PSE_EXT_SUBSTATE */ | 			/* _C33_PSE_EXT_SUBSTATE */ | ||||||
| 			len += nla_total_size(sizeof(u32)); | 			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; | 	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, | static int pse_fill_reply(struct sk_buff *skb, | ||||||
| 			  const struct ethnl_req_info *req_base, | 			  const struct ethnl_req_info *req_base, | ||||||
| 			  const struct ethnl_reply_data *reply_base) | 			  const struct ethnl_reply_data *reply_base) | ||||||
|  | @ -147,9 +184,25 @@ static int pse_fill_reply(struct sk_buff *skb, | ||||||
| 			return -EMSGSIZE; | 			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; | 	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 */ | /* PSE_SET */ | ||||||
| 
 | 
 | ||||||
| const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = { | 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] = | 	[ETHTOOL_A_C33_PSE_ADMIN_CONTROL] = | ||||||
| 		NLA_POLICY_RANGE(NLA_U32, ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED, | 		NLA_POLICY_RANGE(NLA_U32, ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED, | ||||||
| 				 ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED), | 				 ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED), | ||||||
|  | 	[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT] = { .type = NLA_U32 }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static int | static int | ||||||
|  | @ -202,19 +256,39 @@ static int | ||||||
| ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info) | ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info) | ||||||
| { | { | ||||||
| 	struct net_device *dev = req_info->dev; | 	struct net_device *dev = req_info->dev; | ||||||
| 	struct pse_control_config config = {}; |  | ||||||
| 	struct nlattr **tb = info->attrs; | 	struct nlattr **tb = info->attrs; | ||||||
| 	struct phy_device *phydev; | 	struct phy_device *phydev; | ||||||
|  | 	int ret = 0; | ||||||
| 
 | 
 | ||||||
| 	phydev = dev->phydev; | 	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 */ | 	if (tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]) { | ||||||
| 	return pse_ethtool_set_config(phydev->psec, info->extack, &config); | 		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 = { | 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, | 	.prepare_data		= pse_prepare_data, | ||||||
| 	.reply_size		= pse_reply_size, | 	.reply_size		= pse_reply_size, | ||||||
| 	.fill_reply		= pse_fill_reply, | 	.fill_reply		= pse_fill_reply, | ||||||
|  | 	.cleanup_data		= pse_cleanup_data, | ||||||
| 
 | 
 | ||||||
| 	.set_validate		= ethnl_set_pse_validate, | 	.set_validate		= ethnl_set_pse_validate, | ||||||
| 	.set			= ethnl_set_pse, | 	.set			= ethnl_set_pse, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Kory Maincent (Dent Project)
						Kory Maincent (Dent Project)