Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ethtool: Add ability to flash transceiver modules' firmware

Add the ability to flash the modules' firmware by implementing the
interface between the user space and the kernel.

Example from a succeeding implementation:

# ethtool --flash-module-firmware swp40 file test.bin

Transceiver module firmware flashing started for device swp40
Transceiver module firmware flashing in progress for device swp40
Progress: 99%
Transceiver module firmware flashing completed for device swp40

In addition, add infrastructure that allows modules to set socket-specific
private data. This ensures that when a socket is closed from user space
during the flashing process, the right socket halts sending notifications
to user space until the work item is completed.

Signed-off-by: Danielle Ratson <danieller@nvidia.com>
Reviewed-by: Petr Machata <petrm@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Danielle Ratson and committed by
David S. Miller
32b4c8b5 c4f78134

+334
+277
net/ethtool/module.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 3 3 #include <linux/ethtool.h> 4 + #include <linux/firmware.h> 5 + #include <linux/sfp.h> 6 + #include <net/devlink.h> 4 7 5 8 #include "netlink.h" 6 9 #include "common.h" ··· 36 33 37 34 if (!ops->get_module_power_mode) 38 35 return 0; 36 + 37 + if (dev->module_fw_flash_in_progress) { 38 + NL_SET_ERR_MSG(extack, 39 + "Module firmware flashing is in progress"); 40 + return -EBUSY; 41 + } 39 42 40 43 return ops->get_module_power_mode(dev, &data->power, extack); 41 44 } ··· 119 110 if (!tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]) 120 111 return 0; 121 112 113 + if (req_info->dev->module_fw_flash_in_progress) { 114 + NL_SET_ERR_MSG(info->extack, 115 + "Module firmware flashing is in progress"); 116 + return -EBUSY; 117 + } 118 + 122 119 if (!ops->get_module_power_mode || !ops->set_module_power_mode) { 123 120 NL_SET_ERR_MSG_ATTR(info->extack, 124 121 tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY], ··· 174 159 .set = ethnl_set_module, 175 160 .set_ntf_cmd = ETHTOOL_MSG_MODULE_NTF, 176 161 }; 162 + 163 + /* MODULE_FW_FLASH_ACT */ 164 + 165 + const struct nla_policy 166 + ethnl_module_fw_flash_act_policy[ETHTOOL_A_MODULE_FW_FLASH_PASSWORD + 1] = { 167 + [ETHTOOL_A_MODULE_FW_FLASH_HEADER] = 168 + NLA_POLICY_NESTED(ethnl_header_policy), 169 + [ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME] = { .type = NLA_NUL_STRING }, 170 + [ETHTOOL_A_MODULE_FW_FLASH_PASSWORD] = { .type = NLA_U32 }, 171 + }; 172 + 173 + static LIST_HEAD(module_fw_flash_work_list); 174 + static DEFINE_SPINLOCK(module_fw_flash_work_list_lock); 175 + 176 + static int 177 + module_flash_fw_work_list_add(struct ethtool_module_fw_flash *module_fw, 178 + struct genl_info *info) 179 + { 180 + struct ethtool_module_fw_flash *work; 181 + 182 + /* First, check if already registered. */ 183 + spin_lock(&module_fw_flash_work_list_lock); 184 + list_for_each_entry(work, &module_fw_flash_work_list, list) { 185 + if (work->fw_update.ntf_params.portid == info->snd_portid && 186 + work->fw_update.dev == module_fw->fw_update.dev) { 187 + spin_unlock(&module_fw_flash_work_list_lock); 188 + return -EALREADY; 189 + } 190 + } 191 + 192 + list_add_tail(&module_fw->list, &module_fw_flash_work_list); 193 + spin_unlock(&module_fw_flash_work_list_lock); 194 + 195 + return 0; 196 + } 197 + 198 + static void module_flash_fw_work_list_del(struct list_head *list) 199 + { 200 + spin_lock(&module_fw_flash_work_list_lock); 201 + list_del(list); 202 + spin_unlock(&module_fw_flash_work_list_lock); 203 + } 204 + 205 + static void module_flash_fw_work(struct work_struct *work) 206 + { 207 + struct ethtool_module_fw_flash *module_fw; 208 + 209 + module_fw = container_of(work, struct ethtool_module_fw_flash, work); 210 + 211 + ethtool_cmis_fw_update(&module_fw->fw_update); 212 + 213 + module_flash_fw_work_list_del(&module_fw->list); 214 + module_fw->fw_update.dev->module_fw_flash_in_progress = false; 215 + netdev_put(module_fw->fw_update.dev, &module_fw->dev_tracker); 216 + release_firmware(module_fw->fw_update.fw); 217 + kfree(module_fw); 218 + } 219 + 220 + #define MODULE_EEPROM_PHYS_ID_PAGE 0 221 + #define MODULE_EEPROM_PHYS_ID_I2C_ADDR 0x50 222 + 223 + static int module_flash_fw_work_init(struct ethtool_module_fw_flash *module_fw, 224 + struct net_device *dev, 225 + struct netlink_ext_ack *extack) 226 + { 227 + const struct ethtool_ops *ops = dev->ethtool_ops; 228 + struct ethtool_module_eeprom page_data = {}; 229 + u8 phys_id; 230 + int err; 231 + 232 + /* Fetch the SFF-8024 Identifier Value. For all supported standards, it 233 + * is located at I2C address 0x50, byte 0. See section 4.1 in SFF-8024, 234 + * revision 4.9. 235 + */ 236 + page_data.page = MODULE_EEPROM_PHYS_ID_PAGE; 237 + page_data.offset = SFP_PHYS_ID; 238 + page_data.length = sizeof(phys_id); 239 + page_data.i2c_address = MODULE_EEPROM_PHYS_ID_I2C_ADDR; 240 + page_data.data = &phys_id; 241 + 242 + err = ops->get_module_eeprom_by_page(dev, &page_data, extack); 243 + if (err < 0) 244 + return err; 245 + 246 + switch (phys_id) { 247 + case SFF8024_ID_QSFP_DD: 248 + case SFF8024_ID_OSFP: 249 + case SFF8024_ID_DSFP: 250 + case SFF8024_ID_QSFP_PLUS_CMIS: 251 + case SFF8024_ID_SFP_DD_CMIS: 252 + case SFF8024_ID_SFP_PLUS_CMIS: 253 + INIT_WORK(&module_fw->work, module_flash_fw_work); 254 + break; 255 + default: 256 + NL_SET_ERR_MSG(extack, 257 + "Module type does not support firmware flashing"); 258 + return -EOPNOTSUPP; 259 + } 260 + 261 + return 0; 262 + } 263 + 264 + void ethnl_module_fw_flash_sock_destroy(struct ethnl_sock_priv *sk_priv) 265 + { 266 + struct ethtool_module_fw_flash *work; 267 + 268 + spin_lock(&module_fw_flash_work_list_lock); 269 + list_for_each_entry(work, &module_fw_flash_work_list, list) { 270 + if (work->fw_update.dev == sk_priv->dev && 271 + work->fw_update.ntf_params.portid == sk_priv->portid) { 272 + work->fw_update.ntf_params.closed_sock = true; 273 + break; 274 + } 275 + } 276 + spin_unlock(&module_fw_flash_work_list_lock); 277 + } 278 + 279 + static int 280 + module_flash_fw_schedule(struct net_device *dev, const char *file_name, 281 + struct ethtool_module_fw_flash_params *params, 282 + struct sk_buff *skb, struct genl_info *info) 283 + { 284 + struct ethtool_cmis_fw_update_params *fw_update; 285 + struct ethtool_module_fw_flash *module_fw; 286 + int err; 287 + 288 + module_fw = kzalloc(sizeof(*module_fw), GFP_KERNEL); 289 + if (!module_fw) 290 + return -ENOMEM; 291 + 292 + fw_update = &module_fw->fw_update; 293 + fw_update->params = *params; 294 + err = request_firmware_direct(&fw_update->fw, 295 + file_name, &dev->dev); 296 + if (err) { 297 + NL_SET_ERR_MSG(info->extack, 298 + "Failed to request module firmware image"); 299 + goto err_free; 300 + } 301 + 302 + err = module_flash_fw_work_init(module_fw, dev, info->extack); 303 + if (err < 0) 304 + goto err_release_firmware; 305 + 306 + dev->module_fw_flash_in_progress = true; 307 + netdev_hold(dev, &module_fw->dev_tracker, GFP_KERNEL); 308 + fw_update->dev = dev; 309 + fw_update->ntf_params.portid = info->snd_portid; 310 + fw_update->ntf_params.seq = info->snd_seq; 311 + fw_update->ntf_params.closed_sock = false; 312 + 313 + err = ethnl_sock_priv_set(skb, dev, fw_update->ntf_params.portid, 314 + ETHTOOL_SOCK_TYPE_MODULE_FW_FLASH); 315 + if (err < 0) 316 + goto err_release_firmware; 317 + 318 + err = module_flash_fw_work_list_add(module_fw, info); 319 + if (err < 0) 320 + goto err_release_firmware; 321 + 322 + schedule_work(&module_fw->work); 323 + 324 + return 0; 325 + 326 + err_release_firmware: 327 + release_firmware(fw_update->fw); 328 + err_free: 329 + kfree(module_fw); 330 + return err; 331 + } 332 + 333 + static int module_flash_fw(struct net_device *dev, struct nlattr **tb, 334 + struct sk_buff *skb, struct genl_info *info) 335 + { 336 + struct ethtool_module_fw_flash_params params = {}; 337 + const char *file_name; 338 + struct nlattr *attr; 339 + 340 + if (GENL_REQ_ATTR_CHECK(info, ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME)) 341 + return -EINVAL; 342 + 343 + file_name = nla_data(tb[ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME]); 344 + 345 + attr = tb[ETHTOOL_A_MODULE_FW_FLASH_PASSWORD]; 346 + if (attr) { 347 + params.password = cpu_to_be32(nla_get_u32(attr)); 348 + params.password_valid = true; 349 + } 350 + 351 + return module_flash_fw_schedule(dev, file_name, &params, skb, info); 352 + } 353 + 354 + static int ethnl_module_fw_flash_validate(struct net_device *dev, 355 + struct netlink_ext_ack *extack) 356 + { 357 + struct devlink_port *devlink_port = dev->devlink_port; 358 + const struct ethtool_ops *ops = dev->ethtool_ops; 359 + 360 + if (!ops->set_module_eeprom_by_page || 361 + !ops->get_module_eeprom_by_page) { 362 + NL_SET_ERR_MSG(extack, 363 + "Flashing module firmware is not supported by this device"); 364 + return -EOPNOTSUPP; 365 + } 366 + 367 + if (!ops->reset) { 368 + NL_SET_ERR_MSG(extack, 369 + "Reset module is not supported by this device, so flashing is not permitted"); 370 + return -EOPNOTSUPP; 371 + } 372 + 373 + if (dev->module_fw_flash_in_progress) { 374 + NL_SET_ERR_MSG(extack, "Module firmware flashing already in progress"); 375 + return -EBUSY; 376 + } 377 + 378 + if (dev->flags & IFF_UP) { 379 + NL_SET_ERR_MSG(extack, "Netdevice is up, so flashing is not permitted"); 380 + return -EBUSY; 381 + } 382 + 383 + if (devlink_port && devlink_port->attrs.split) { 384 + NL_SET_ERR_MSG(extack, "Can't perform firmware flashing on a split port"); 385 + return -EOPNOTSUPP; 386 + } 387 + 388 + return 0; 389 + } 390 + 391 + int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info) 392 + { 393 + struct ethnl_req_info req_info = {}; 394 + struct nlattr **tb = info->attrs; 395 + struct net_device *dev; 396 + int ret; 397 + 398 + ret = ethnl_parse_header_dev_get(&req_info, 399 + tb[ETHTOOL_A_MODULE_FW_FLASH_HEADER], 400 + genl_info_net(info), info->extack, 401 + true); 402 + if (ret < 0) 403 + return ret; 404 + dev = req_info.dev; 405 + 406 + rtnl_lock(); 407 + ret = ethnl_ops_begin(dev); 408 + if (ret < 0) 409 + goto out_rtnl; 410 + 411 + ret = ethnl_module_fw_flash_validate(dev, info->extack); 412 + if (ret < 0) 413 + goto out_rtnl; 414 + 415 + ret = module_flash_fw(dev, tb, skb, info); 416 + 417 + ethnl_ops_complete(dev); 418 + 419 + out_rtnl: 420 + rtnl_unlock(); 421 + ethnl_parse_header_dev_put(&req_info); 422 + return ret; 423 + } 177 424 178 425 /* MODULE_FW_FLASH_NTF */ 179 426
+3
net/ethtool/module_fw.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 3 3 #include <uapi/linux/ethtool.h> 4 + #include "netlink.h" 4 5 5 6 /** 6 7 * struct ethnl_module_fw_flash_ntf_params - module firmware flashing ··· 54 53 struct work_struct work; 55 54 struct ethtool_cmis_fw_update_params fw_update; 56 55 }; 56 + 57 + void ethnl_module_fw_flash_sock_destroy(struct ethnl_sock_priv *sk_priv); 57 58 58 59 void 59 60 ethnl_module_fw_flash_ntf_err(struct net_device *dev,
+39
net/ethtool/netlink.c
··· 4 4 #include <linux/ethtool_netlink.h> 5 5 #include <linux/pm_runtime.h> 6 6 #include "netlink.h" 7 + #include "module_fw.h" 7 8 8 9 static struct genl_family ethtool_genl_family; 9 10 ··· 30 29 [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32, 31 30 ETHTOOL_FLAGS_STATS), 32 31 }; 32 + 33 + int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid, 34 + enum ethnl_sock_type type) 35 + { 36 + struct ethnl_sock_priv *sk_priv; 37 + 38 + sk_priv = genl_sk_priv_get(&ethtool_genl_family, NETLINK_CB(skb).sk); 39 + if (IS_ERR(sk_priv)) 40 + return PTR_ERR(sk_priv); 41 + 42 + sk_priv->dev = dev; 43 + sk_priv->portid = portid; 44 + sk_priv->type = type; 45 + 46 + return 0; 47 + } 48 + 49 + static void ethnl_sock_priv_destroy(void *priv) 50 + { 51 + struct ethnl_sock_priv *sk_priv = priv; 52 + 53 + switch (sk_priv->type) { 54 + case ETHTOOL_SOCK_TYPE_MODULE_FW_FLASH: 55 + ethnl_module_fw_flash_sock_destroy(sk_priv); 56 + break; 57 + default: 58 + break; 59 + } 60 + } 33 61 34 62 int ethnl_ops_begin(struct net_device *dev) 35 63 { ··· 1172 1142 .policy = ethnl_mm_set_policy, 1173 1143 .maxattr = ARRAY_SIZE(ethnl_mm_set_policy) - 1, 1174 1144 }, 1145 + { 1146 + .cmd = ETHTOOL_MSG_MODULE_FW_FLASH_ACT, 1147 + .flags = GENL_UNS_ADMIN_PERM, 1148 + .doit = ethnl_act_module_fw_flash, 1149 + .policy = ethnl_module_fw_flash_act_policy, 1150 + .maxattr = ARRAY_SIZE(ethnl_module_fw_flash_act_policy) - 1, 1151 + }, 1175 1152 }; 1176 1153 1177 1154 static const struct genl_multicast_group ethtool_nl_mcgrps[] = { ··· 1195 1158 .resv_start_op = ETHTOOL_MSG_MODULE_GET + 1, 1196 1159 .mcgrps = ethtool_nl_mcgrps, 1197 1160 .n_mcgrps = ARRAY_SIZE(ethtool_nl_mcgrps), 1161 + .sock_priv_size = sizeof(struct ethnl_sock_priv), 1162 + .sock_priv_destroy = ethnl_sock_priv_destroy, 1198 1163 }; 1199 1164 1200 1165 /* module setup */
+15
net/ethtool/netlink.h
··· 284 284 int ethnl_ops_begin(struct net_device *dev); 285 285 void ethnl_ops_complete(struct net_device *dev); 286 286 287 + enum ethnl_sock_type { 288 + ETHTOOL_SOCK_TYPE_MODULE_FW_FLASH, 289 + }; 290 + 291 + struct ethnl_sock_priv { 292 + struct net_device *dev; 293 + u32 portid; 294 + enum ethnl_sock_type type; 295 + }; 296 + 297 + int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid, 298 + enum ethnl_sock_type type); 299 + 287 300 /** 288 301 * struct ethnl_request_ops - unified handling of GET and SET requests 289 302 * @request_cmd: command id for request (GET) ··· 455 442 extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1]; 456 443 extern const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1]; 457 444 extern const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1]; 445 + extern const struct nla_policy ethnl_module_fw_flash_act_policy[ETHTOOL_A_MODULE_FW_FLASH_PASSWORD + 1]; 458 446 459 447 int ethnl_set_features(struct sk_buff *skb, struct genl_info *info); 460 448 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info); ··· 463 449 int ethnl_tunnel_info_doit(struct sk_buff *skb, struct genl_info *info); 464 450 int ethnl_tunnel_info_start(struct netlink_callback *cb); 465 451 int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 452 + int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info); 466 453 467 454 extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; 468 455 extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];