mirror of
https://github.com/torvalds/linux.git
synced 2026-06-03 12:03:54 +02:00
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: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
c66d7c3c1f
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -318,12 +318,12 @@ enum ethnl_sock_type {
|
|||
};
|
||||
|
||||
struct ethnl_sock_priv {
|
||||
struct net_device *dev;
|
||||
struct net *net;
|
||||
u32 portid;
|
||||
enum ethnl_sock_type type;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user