Commit c66d7c3c authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'ethtool-module-fix-a-handful-of-small-bugs'

Jakub Kicinski says:

====================
ethtool: module: fix a handful of small bugs

I've been poking at the locking in ethtool and it appears
that the FW flashing is not currently taking the ops lock.
Existing drivers which implement module FW flashing seem
to have their own locking, so this series doesn't actually
add the ops lock (I'll add it in net-next). But a number
of other errors have been surfaced in the process.
====================

Link: https://patch.msgid.link/20260522231312.1710836-1-kuba@kernel.org


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents a0bfd643 d5551f4c
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -63,9 +63,9 @@ struct ethtool_cmis_cdb_request {
 * struct ethtool_cmis_cdb_cmd_args - CDB commands execution arguments
 * @req: CDB command fields as described in the CMIS standard.
 * @max_duration: Maximum duration time for command completion in msec.
 * @msleep_pre_rpl: Waiting time before checking reply in msec.
 * @read_write_len_ext: Allowable additional number of byte octets to the LPL
 *			in a READ or a WRITE commands.
 * @msleep_pre_rpl: Waiting time before checking reply in msec.
 * @rpl_exp_len: Expected reply length in bytes.
 * @flags: Validation flags for CDB commands.
 * @err_msg: Error message to be sent to user space.
@@ -73,8 +73,8 @@ struct ethtool_cmis_cdb_request {
struct ethtool_cmis_cdb_cmd_args {
	struct ethtool_cmis_cdb_request req;
	u16				max_duration;
	u16				msleep_pre_rpl;
	u8				read_write_len_ext;
	u8				msleep_pre_rpl;
	u8                              rpl_exp_len;
	u8				flags;
	char				*err_msg;
+7 −2
Original line number Diff line number Diff line
@@ -513,8 +513,13 @@ static int cmis_cdb_process_reply(struct net_device *dev,
	}

	rpl = (struct ethtool_cmis_cdb_rpl *)page_data->data;
	if ((args->rpl_exp_len > rpl->hdr.rpl_len + rpl_hdr_len) ||
	    !rpl->hdr.rpl_chk_code) {
	if (rpl->hdr.rpl_len != args->rpl_exp_len) {
		netdev_warn(dev, "CDB reply length mismatch, expected %u got %u\n",
			    args->rpl_exp_len, rpl->hdr.rpl_len);
		err = -EIO;
		goto out;
	}
	if (!rpl->hdr.rpl_chk_code) {
		err = -EIO;
		goto out;
	}
+30 −14
Original line number Diff line number Diff line
@@ -44,6 +44,20 @@ enum cmis_cdb_fw_write_mechanism {
	CMIS_CDB_FW_WRITE_MECHANISM_BOTH	= 0x11,
};

/* See section 9.7.2 "CMD 0101h: Start Firmware Download" in CMIS standard
 * revision 5.2.
 * struct cmis_cdb_start_fw_download_pl is a structured layout of the
 * flat array, ethtool_cmis_cdb_request::payload.
 */
struct cmis_cdb_start_fw_download_pl {
	__struct_group(cmis_cdb_start_fw_download_pl_h, head, /* no attrs */,
			__be32	image_size;
			__be32	resv1;
	);
	u8 vendor_data[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH -
		sizeof(struct cmis_cdb_start_fw_download_pl_h)];
};

static int
cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
				   struct net_device *dev,
@@ -86,6 +100,14 @@ cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
	 */
	cdb->read_write_len_ext = rpl->read_write_len_ext;
	fw_mng->start_cmd_payload_size = rpl->start_cmd_payload_size;
	if (fw_mng->start_cmd_payload_size >
	    sizeof_field(struct cmis_cdb_start_fw_download_pl, vendor_data)) {
		ethnl_module_fw_flash_ntf_err(dev, ntf_params,
					      "Start cmd payload size exceeds max LPL payload",
					      NULL);
		return -EINVAL;
	}

	fw_mng->write_mechanism =
		rpl->write_mechanism == CMIS_CDB_FW_WRITE_MECHANISM_LPL ?
		CMIS_CDB_FW_WRITE_MECHANISM_LPL :
@@ -97,20 +119,6 @@ cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
	return 0;
}

/* See section 9.7.2 "CMD 0101h: Start Firmware Download" in CMIS standard
 * revision 5.2.
 * struct cmis_cdb_start_fw_download_pl is a structured layout of the
 * flat array, ethtool_cmis_cdb_request::payload.
 */
struct cmis_cdb_start_fw_download_pl {
	__struct_group(cmis_cdb_start_fw_download_pl_h, head, /* no attrs */,
			__be32	image_size;
			__be32	resv1;
	);
	u8 vendor_data[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH -
		sizeof(struct cmis_cdb_start_fw_download_pl_h)];
};

static int
cmis_fw_update_start_download(struct ethtool_cmis_cdb *cdb,
			      struct ethtool_cmis_fw_update_params *fw_update,
@@ -122,6 +130,14 @@ cmis_fw_update_start_download(struct ethtool_cmis_cdb *cdb,
	u8 lpl_len;
	int err;

	if (fw_update->fw->size < vendor_data_size) {
		ethnl_module_fw_flash_ntf_err(fw_update->dev,
					      &fw_update->ntf_params,
					      "Firmware image too small for module's start payload",
					      NULL);
		return -EINVAL;
	}

	pl.image_size = cpu_to_be32(fw_update->fw->size);
	memcpy(pl.vendor_data, fw_update->fw->data, vendor_data_size);

+25 −16
Original line number Diff line number Diff line
@@ -120,12 +120,6 @@ ethnl_set_module_validate(struct ethnl_req_info *req_info,
	if (!tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY])
		return 0;

	if (req_info->dev->ethtool->module_fw_flash_in_progress) {
		NL_SET_ERR_MSG(info->extack,
			       "Module firmware flashing is in progress");
		return -EBUSY;
	}

	if (!ops->get_module_power_mode || !ops->set_module_power_mode) {
		NL_SET_ERR_MSG_ATTR(info->extack,
				    tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY],
@@ -148,6 +142,12 @@ ethnl_set_module(struct ethnl_req_info *req_info, struct genl_info *info)

	ops = dev->ethtool_ops;

	if (dev->ethtool->module_fw_flash_in_progress) {
		NL_SET_ERR_MSG(info->extack,
			       "Module firmware flashing is in progress");
		return -EBUSY;
	}

	power_new.policy = nla_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]);
	ret = ops->get_module_power_mode(dev, &power, info->extack);
	if (ret < 0)
@@ -221,14 +221,22 @@ static void module_flash_fw_work_list_del(struct list_head *list)
static void module_flash_fw_work(struct work_struct *work)
{
	struct ethtool_module_fw_flash *module_fw;
	struct net_device *dev;

	module_fw = container_of(work, struct ethtool_module_fw_flash, work);
	dev = module_fw->fw_update.dev;

	ethtool_cmis_fw_update(&module_fw->fw_update);

	module_flash_fw_work_list_del(&module_fw->list);
	module_fw->fw_update.dev->ethtool->module_fw_flash_in_progress = false;
	netdev_put(module_fw->fw_update.dev, &module_fw->dev_tracker);

	rtnl_lock();
	netdev_lock_ops(dev);
	dev->ethtool->module_fw_flash_in_progress = false;
	netdev_unlock_ops(dev);
	rtnl_unlock();

	netdev_put(dev, &module_fw->dev_tracker);
	release_firmware(module_fw->fw_update.fw);
	kfree(module_fw);
}
@@ -283,11 +291,9 @@ void ethnl_module_fw_flash_sock_destroy(struct ethnl_sock_priv *sk_priv)

	spin_lock(&module_fw_flash_work_list_lock);
	list_for_each_entry(work, &module_fw_flash_work_list, list) {
		if (work->fw_update.dev == sk_priv->dev &&
		    work->fw_update.ntf_params.portid == sk_priv->portid) {
		if (work->fw_update.ntf_params.portid == sk_priv->portid &&
		    dev_net(work->fw_update.dev) == sk_priv->net)
			work->fw_update.ntf_params.closed_sock = true;
			break;
		}
	}
	spin_unlock(&module_fw_flash_work_list_lock);
}
@@ -319,14 +325,13 @@ module_flash_fw_schedule(struct net_device *dev, const char *file_name,
	if (err < 0)
		goto err_release_firmware;

	dev->ethtool->module_fw_flash_in_progress = true;
	netdev_hold(dev, &module_fw->dev_tracker, GFP_KERNEL);
	fw_update->dev = dev;
	fw_update->ntf_params.portid = info->snd_portid;
	fw_update->ntf_params.seq = info->snd_seq;
	fw_update->ntf_params.closed_sock = false;

	err = ethnl_sock_priv_set(skb, dev, fw_update->ntf_params.portid,
	err = ethnl_sock_priv_set(skb, dev_net(dev),
				  fw_update->ntf_params.portid,
				  ETHTOOL_SOCK_TYPE_MODULE_FW_FLASH);
	if (err < 0)
		goto err_release_firmware;
@@ -335,6 +340,9 @@ module_flash_fw_schedule(struct net_device *dev, const char *file_name,
	if (err < 0)
		goto err_release_firmware;

	dev->ethtool->module_fw_flash_in_progress = true;
	netdev_hold(dev, &module_fw->dev_tracker, GFP_KERNEL);

	schedule_work(&module_fw->work);

	return 0;
@@ -427,10 +435,11 @@ int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info)

	ret = ethnl_module_fw_flash_validate(dev, info->extack);
	if (ret < 0)
		goto out_unlock;
		goto out_complete;

	ret = module_flash_fw(dev, tb, skb, info);

out_complete:
	ethnl_ops_complete(dev);

out_unlock:
+2 −2
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ const struct nla_policy ethnl_header_policy_phy_stats[] = {
	[ETHTOOL_A_HEADER_PHY_INDEX]		= NLA_POLICY_MIN(NLA_U32, 1),
};

int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid,
int ethnl_sock_priv_set(struct sk_buff *skb, struct net *net, u32 portid,
			enum ethnl_sock_type type)
{
	struct ethnl_sock_priv *sk_priv;
@@ -62,7 +62,7 @@ int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid,
	if (IS_ERR(sk_priv))
		return PTR_ERR(sk_priv);

	sk_priv->dev = dev;
	sk_priv->net = net;
	sk_priv->portid = portid;
	sk_priv->type = type;

Loading