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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-ethtool-introduce-ethnl-dump-helpers'

Maxime Chevallier says:

====================
net: ethtool: Introduce ethnl dump helpers

This is V8 for per-phy DUMP helpers, improving support for ->dumpit()
operations for PHY targetting commands.

This V8 fixes some issues spotted by Jakub (thanks !) on the multi-part
DUMP sequence. The netdev reftracking was reworked to make sure that
during a filtered DUMP, we only keep a ref on the netdev during
individual .dumpit() calls.

v1: https://lore.kernel.org/20250305141938.319282-1-maxime.chevallier@bootlin.com
v2: https://lore.kernel.org/20250308155440.267782-1-maxime.chevallier@bootlin.com
v3: https://lore.kernel.org/20250313182647.250007-1-maxime.chevallier@bootlin.com
v4: https://lore.kernel.org/20250324104012.367366-1-maxime.chevallier@bootlin.com
v5: https://lore.kernel.org/20250410123350.174105-1-maxime.chevallier@bootlin.com
v6: https://lore.kernel.org/20250415085155.132963-1-maxime.chevallier@bootlin.com
v7: https://lore.kernel.org/20250422161717.164440-1-maxime.chevallier@bootlin.com
====================

Link: https://patch.msgid.link/20250502085242.248645-1-maxime.chevallier@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+305 -272
+202 -15
net/ethtool/netlink.c
··· 357 357 unsigned long pos_ifindex; 358 358 }; 359 359 360 + /** 361 + * struct ethnl_perphy_dump_ctx - context for dumpit() PHY-aware callbacks 362 + * @ethnl_ctx: generic ethnl context 363 + * @ifindex: For Filtered DUMP requests, the ifindex of the targeted netdev 364 + * @pos_phyindex: iterator position for multi-msg DUMP 365 + */ 366 + struct ethnl_perphy_dump_ctx { 367 + struct ethnl_dump_ctx ethnl_ctx; 368 + unsigned int ifindex; 369 + unsigned long pos_phyindex; 370 + }; 371 + 360 372 static const struct ethnl_request_ops * 361 373 ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { 362 374 [ETHTOOL_MSG_STRSET_GET] = &ethnl_strset_request_ops, ··· 412 400 [ETHTOOL_MSG_MM_SET] = &ethnl_mm_request_ops, 413 401 [ETHTOOL_MSG_TSCONFIG_GET] = &ethnl_tsconfig_request_ops, 414 402 [ETHTOOL_MSG_TSCONFIG_SET] = &ethnl_tsconfig_request_ops, 403 + [ETHTOOL_MSG_PHY_GET] = &ethnl_phy_request_ops, 415 404 }; 416 405 417 406 static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) 418 407 { 419 408 return (struct ethnl_dump_ctx *)cb->ctx; 409 + } 410 + 411 + static struct ethnl_perphy_dump_ctx * 412 + ethnl_perphy_dump_context(struct netlink_callback *cb) 413 + { 414 + return (struct ethnl_perphy_dump_ctx *)cb->ctx; 420 415 } 421 416 422 417 /** ··· 603 584 { 604 585 struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb); 605 586 struct net *net = sock_net(skb->sk); 587 + netdevice_tracker dev_tracker; 606 588 struct net_device *dev; 607 589 int ret = 0; 608 590 609 591 rcu_read_lock(); 610 592 for_each_netdev_dump(net, dev, ctx->pos_ifindex) { 611 - dev_hold(dev); 593 + netdev_hold(dev, &dev_tracker, GFP_ATOMIC); 612 594 rcu_read_unlock(); 613 595 614 596 ret = ethnl_default_dump_one(skb, dev, ctx, genl_info_dump(cb)); 615 597 616 598 rcu_read_lock(); 617 - dev_put(dev); 599 + netdev_put(dev, &dev_tracker); 618 600 619 601 if (ret < 0 && ret != -EOPNOTSUPP) { 620 602 if (likely(skb->len)) ··· 680 660 kfree(req_info); 681 661 682 662 return ret; 663 + } 664 + 665 + /* per-PHY ->start() handler for GET requests */ 666 + static int ethnl_perphy_start(struct netlink_callback *cb) 667 + { 668 + struct ethnl_perphy_dump_ctx *phy_ctx = ethnl_perphy_dump_context(cb); 669 + const struct genl_dumpit_info *info = genl_dumpit_info(cb); 670 + struct ethnl_dump_ctx *ctx = &phy_ctx->ethnl_ctx; 671 + struct ethnl_reply_data *reply_data; 672 + const struct ethnl_request_ops *ops; 673 + struct ethnl_req_info *req_info; 674 + struct genlmsghdr *ghdr; 675 + int ret; 676 + 677 + BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); 678 + 679 + ghdr = nlmsg_data(cb->nlh); 680 + ops = ethnl_default_requests[ghdr->cmd]; 681 + if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops\n", ghdr->cmd)) 682 + return -EOPNOTSUPP; 683 + req_info = kzalloc(ops->req_info_size, GFP_KERNEL); 684 + if (!req_info) 685 + return -ENOMEM; 686 + reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL); 687 + if (!reply_data) { 688 + ret = -ENOMEM; 689 + goto free_req_info; 690 + } 691 + 692 + /* Unlike per-dev dump, don't ignore dev. The dump handler 693 + * will notice it and dump PHYs from given dev. We only keep track of 694 + * the dev's ifindex, .dumpit() will grab and release the netdev itself. 695 + */ 696 + ret = ethnl_default_parse(req_info, &info->info, ops, false); 697 + if (req_info->dev) { 698 + phy_ctx->ifindex = req_info->dev->ifindex; 699 + netdev_put(req_info->dev, &req_info->dev_tracker); 700 + req_info->dev = NULL; 701 + } 702 + if (ret < 0) 703 + goto free_reply_data; 704 + 705 + ctx->ops = ops; 706 + ctx->req_info = req_info; 707 + ctx->reply_data = reply_data; 708 + ctx->pos_ifindex = 0; 709 + 710 + return 0; 711 + 712 + free_reply_data: 713 + kfree(reply_data); 714 + free_req_info: 715 + kfree(req_info); 716 + 717 + return ret; 718 + } 719 + 720 + static int ethnl_perphy_dump_one_dev(struct sk_buff *skb, 721 + struct ethnl_perphy_dump_ctx *ctx, 722 + const struct genl_info *info) 723 + { 724 + struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx; 725 + struct net_device *dev = ethnl_ctx->req_info->dev; 726 + struct phy_device_node *pdn; 727 + int ret; 728 + 729 + if (!dev->link_topo) 730 + return 0; 731 + 732 + xa_for_each_start(&dev->link_topo->phys, ctx->pos_phyindex, pdn, 733 + ctx->pos_phyindex) { 734 + ethnl_ctx->req_info->phy_index = ctx->pos_phyindex; 735 + 736 + /* We can re-use the original dump_one as ->prepare_data in 737 + * commands use ethnl_req_get_phydev(), which gets the PHY from 738 + * the req_info->phy_index 739 + */ 740 + ret = ethnl_default_dump_one(skb, dev, ethnl_ctx, info); 741 + if (ret) 742 + return ret; 743 + } 744 + 745 + ctx->pos_phyindex = 0; 746 + 747 + return 0; 748 + } 749 + 750 + static int ethnl_perphy_dump_all_dev(struct sk_buff *skb, 751 + struct ethnl_perphy_dump_ctx *ctx, 752 + const struct genl_info *info) 753 + { 754 + struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx; 755 + struct net *net = sock_net(skb->sk); 756 + netdevice_tracker dev_tracker; 757 + struct net_device *dev; 758 + int ret = 0; 759 + 760 + rcu_read_lock(); 761 + for_each_netdev_dump(net, dev, ethnl_ctx->pos_ifindex) { 762 + netdev_hold(dev, &dev_tracker, GFP_ATOMIC); 763 + rcu_read_unlock(); 764 + 765 + /* per-PHY commands use ethnl_req_get_phydev(), which needs the 766 + * net_device in the req_info 767 + */ 768 + ethnl_ctx->req_info->dev = dev; 769 + ret = ethnl_perphy_dump_one_dev(skb, ctx, info); 770 + 771 + rcu_read_lock(); 772 + netdev_put(dev, &dev_tracker); 773 + ethnl_ctx->req_info->dev = NULL; 774 + 775 + if (ret < 0 && ret != -EOPNOTSUPP) { 776 + if (likely(skb->len)) 777 + ret = skb->len; 778 + break; 779 + } 780 + ret = 0; 781 + } 782 + rcu_read_unlock(); 783 + 784 + return ret; 785 + } 786 + 787 + /* per-PHY ->dumpit() handler for GET requests. */ 788 + static int ethnl_perphy_dumpit(struct sk_buff *skb, 789 + struct netlink_callback *cb) 790 + { 791 + struct ethnl_perphy_dump_ctx *ctx = ethnl_perphy_dump_context(cb); 792 + const struct genl_dumpit_info *info = genl_dumpit_info(cb); 793 + struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx; 794 + int ret = 0; 795 + 796 + if (ctx->ifindex) { 797 + netdevice_tracker dev_tracker; 798 + struct net_device *dev; 799 + 800 + dev = netdev_get_by_index(genl_info_net(&info->info), 801 + ctx->ifindex, &dev_tracker, 802 + GFP_KERNEL); 803 + if (!dev) 804 + return -ENODEV; 805 + 806 + ethnl_ctx->req_info->dev = dev; 807 + ret = ethnl_perphy_dump_one_dev(skb, ctx, genl_info_dump(cb)); 808 + 809 + if (ret < 0 && ret != -EOPNOTSUPP && likely(skb->len)) 810 + ret = skb->len; 811 + 812 + netdev_put(dev, &dev_tracker); 813 + } else { 814 + ret = ethnl_perphy_dump_all_dev(skb, ctx, genl_info_dump(cb)); 815 + } 816 + 817 + return ret; 818 + } 819 + 820 + /* per-PHY ->done() handler for GET requests */ 821 + static int ethnl_perphy_done(struct netlink_callback *cb) 822 + { 823 + struct ethnl_perphy_dump_ctx *ctx = ethnl_perphy_dump_context(cb); 824 + struct ethnl_dump_ctx *ethnl_ctx = &ctx->ethnl_ctx; 825 + 826 + kfree(ethnl_ctx->reply_data); 827 + kfree(ethnl_ctx->req_info); 828 + 829 + return 0; 683 830 } 684 831 685 832 /* default ->done() handler for GET requests */ ··· 1387 1200 { 1388 1201 .cmd = ETHTOOL_MSG_PSE_GET, 1389 1202 .doit = ethnl_default_doit, 1390 - .start = ethnl_default_start, 1391 - .dumpit = ethnl_default_dumpit, 1392 - .done = ethnl_default_done, 1203 + .start = ethnl_perphy_start, 1204 + .dumpit = ethnl_perphy_dumpit, 1205 + .done = ethnl_perphy_done, 1393 1206 .policy = ethnl_pse_get_policy, 1394 1207 .maxattr = ARRAY_SIZE(ethnl_pse_get_policy) - 1, 1395 1208 }, ··· 1411 1224 { 1412 1225 .cmd = ETHTOOL_MSG_PLCA_GET_CFG, 1413 1226 .doit = ethnl_default_doit, 1414 - .start = ethnl_default_start, 1415 - .dumpit = ethnl_default_dumpit, 1416 - .done = ethnl_default_done, 1227 + .start = ethnl_perphy_start, 1228 + .dumpit = ethnl_perphy_dumpit, 1229 + .done = ethnl_perphy_done, 1417 1230 .policy = ethnl_plca_get_cfg_policy, 1418 1231 .maxattr = ARRAY_SIZE(ethnl_plca_get_cfg_policy) - 1, 1419 1232 }, ··· 1427 1240 { 1428 1241 .cmd = ETHTOOL_MSG_PLCA_GET_STATUS, 1429 1242 .doit = ethnl_default_doit, 1430 - .start = ethnl_default_start, 1431 - .dumpit = ethnl_default_dumpit, 1432 - .done = ethnl_default_done, 1243 + .start = ethnl_perphy_start, 1244 + .dumpit = ethnl_perphy_dumpit, 1245 + .done = ethnl_perphy_done, 1433 1246 .policy = ethnl_plca_get_status_policy, 1434 1247 .maxattr = ARRAY_SIZE(ethnl_plca_get_status_policy) - 1, 1435 1248 }, ··· 1458 1271 }, 1459 1272 { 1460 1273 .cmd = ETHTOOL_MSG_PHY_GET, 1461 - .doit = ethnl_phy_doit, 1462 - .start = ethnl_phy_start, 1463 - .dumpit = ethnl_phy_dumpit, 1464 - .done = ethnl_phy_done, 1274 + .doit = ethnl_default_doit, 1275 + .start = ethnl_perphy_start, 1276 + .dumpit = ethnl_perphy_dumpit, 1277 + .done = ethnl_perphy_done, 1465 1278 .policy = ethnl_phy_get_policy, 1466 1279 .maxattr = ARRAY_SIZE(ethnl_phy_get_policy) - 1, 1467 1280 },
-4
net/ethtool/netlink.h
··· 499 499 int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info); 500 500 int ethnl_rss_dump_start(struct netlink_callback *cb); 501 501 int ethnl_rss_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 502 - int ethnl_phy_start(struct netlink_callback *cb); 503 - int ethnl_phy_doit(struct sk_buff *skb, struct genl_info *info); 504 - int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 505 - int ethnl_phy_done(struct netlink_callback *cb); 506 502 int ethnl_tsinfo_start(struct netlink_callback *cb); 507 503 int ethnl_tsinfo_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 508 504 int ethnl_tsinfo_done(struct netlink_callback *cb);
+103 -253
net/ethtool/phy.c
··· 12 12 #include <net/netdev_lock.h> 13 13 14 14 struct phy_req_info { 15 - struct ethnl_req_info base; 16 - struct phy_device_node *pdn; 15 + struct ethnl_req_info base; 17 16 }; 18 17 19 - #define PHY_REQINFO(__req_base) \ 20 - container_of(__req_base, struct phy_req_info, base) 18 + struct phy_reply_data { 19 + struct ethnl_reply_data base; 20 + u32 phyindex; 21 + char *drvname; 22 + char *name; 23 + unsigned int upstream_type; 24 + char *upstream_sfp_name; 25 + unsigned int upstream_index; 26 + char *downstream_sfp_name; 27 + }; 28 + 29 + #define PHY_REPDATA(__reply_base) \ 30 + container_of(__reply_base, struct phy_reply_data, base) 21 31 22 32 const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1] = { 23 33 [ETHTOOL_A_PHY_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 24 34 }; 25 35 26 - /* Caller holds rtnl */ 27 - static ssize_t 28 - ethnl_phy_reply_size(const struct ethnl_req_info *req_base, 29 - struct netlink_ext_ack *extack) 36 + static int phy_reply_size(const struct ethnl_req_info *req_info, 37 + const struct ethnl_reply_data *reply_data) 30 38 { 31 - struct phy_req_info *req_info = PHY_REQINFO(req_base); 32 - struct phy_device_node *pdn = req_info->pdn; 33 - struct phy_device *phydev = pdn->phy; 39 + struct phy_reply_data *rep_data = PHY_REPDATA(reply_data); 34 40 size_t size = 0; 35 - 36 - ASSERT_RTNL(); 37 41 38 42 /* ETHTOOL_A_PHY_INDEX */ 39 43 size += nla_total_size(sizeof(u32)); 40 44 41 45 /* ETHTOOL_A_DRVNAME */ 42 - if (phydev->drv) 43 - size += nla_total_size(strlen(phydev->drv->name) + 1); 46 + if (rep_data->drvname) 47 + size += nla_total_size(strlen(rep_data->drvname) + 1); 44 48 45 49 /* ETHTOOL_A_NAME */ 46 - size += nla_total_size(strlen(dev_name(&phydev->mdio.dev)) + 1); 50 + size += nla_total_size(strlen(rep_data->name) + 1); 47 51 48 52 /* ETHTOOL_A_PHY_UPSTREAM_TYPE */ 49 53 size += nla_total_size(sizeof(u32)); 50 54 51 - if (phy_on_sfp(phydev)) { 52 - const char *upstream_sfp_name = sfp_get_name(pdn->parent_sfp_bus); 55 + /* ETHTOOL_A_PHY_UPSTREAM_SFP_NAME */ 56 + if (rep_data->upstream_sfp_name) 57 + size += nla_total_size(strlen(rep_data->upstream_sfp_name) + 1); 53 58 54 - /* ETHTOOL_A_PHY_UPSTREAM_SFP_NAME */ 55 - if (upstream_sfp_name) 56 - size += nla_total_size(strlen(upstream_sfp_name) + 1); 57 - 58 - /* ETHTOOL_A_PHY_UPSTREAM_INDEX */ 59 + /* ETHTOOL_A_PHY_UPSTREAM_INDEX */ 60 + if (rep_data->upstream_index) 59 61 size += nla_total_size(sizeof(u32)); 60 - } 61 62 62 63 /* ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME */ 63 - if (phydev->sfp_bus) { 64 - const char *sfp_name = sfp_get_name(phydev->sfp_bus); 65 - 66 - if (sfp_name) 67 - size += nla_total_size(strlen(sfp_name) + 1); 68 - } 64 + if (rep_data->downstream_sfp_name) 65 + size += nla_total_size(strlen(rep_data->downstream_sfp_name) + 1); 69 66 70 67 return size; 71 68 } 72 69 73 - static int 74 - ethnl_phy_fill_reply(const struct ethnl_req_info *req_base, struct sk_buff *skb) 70 + static int phy_prepare_data(const struct ethnl_req_info *req_info, 71 + struct ethnl_reply_data *reply_data, 72 + const struct genl_info *info) 75 73 { 76 - struct phy_req_info *req_info = PHY_REQINFO(req_base); 77 - struct phy_device_node *pdn = req_info->pdn; 78 - struct phy_device *phydev = pdn->phy; 79 - enum phy_upstream ptype; 80 - 81 - ptype = pdn->upstream_type; 82 - 83 - if (nla_put_u32(skb, ETHTOOL_A_PHY_INDEX, phydev->phyindex) || 84 - nla_put_string(skb, ETHTOOL_A_PHY_NAME, dev_name(&phydev->mdio.dev)) || 85 - nla_put_u32(skb, ETHTOOL_A_PHY_UPSTREAM_TYPE, ptype)) 86 - return -EMSGSIZE; 87 - 88 - if (phydev->drv && 89 - nla_put_string(skb, ETHTOOL_A_PHY_DRVNAME, phydev->drv->name)) 90 - return -EMSGSIZE; 91 - 92 - if (ptype == PHY_UPSTREAM_PHY) { 93 - struct phy_device *upstream = pdn->upstream.phydev; 94 - const char *sfp_upstream_name; 95 - 96 - /* Parent index */ 97 - if (nla_put_u32(skb, ETHTOOL_A_PHY_UPSTREAM_INDEX, upstream->phyindex)) 98 - return -EMSGSIZE; 99 - 100 - if (pdn->parent_sfp_bus) { 101 - sfp_upstream_name = sfp_get_name(pdn->parent_sfp_bus); 102 - if (sfp_upstream_name && 103 - nla_put_string(skb, ETHTOOL_A_PHY_UPSTREAM_SFP_NAME, 104 - sfp_upstream_name)) 105 - return -EMSGSIZE; 106 - } 107 - } 108 - 109 - if (phydev->sfp_bus) { 110 - const char *sfp_name = sfp_get_name(phydev->sfp_bus); 111 - 112 - if (sfp_name && 113 - nla_put_string(skb, ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME, 114 - sfp_name)) 115 - return -EMSGSIZE; 116 - } 117 - 118 - return 0; 119 - } 120 - 121 - static int ethnl_phy_parse_request(struct ethnl_req_info *req_base, 122 - struct nlattr **tb, 123 - struct netlink_ext_ack *extack) 124 - { 125 - struct phy_link_topology *topo = req_base->dev->link_topo; 126 - struct phy_req_info *req_info = PHY_REQINFO(req_base); 74 + struct phy_link_topology *topo = reply_data->dev->link_topo; 75 + struct phy_reply_data *rep_data = PHY_REPDATA(reply_data); 76 + struct nlattr **tb = info->attrs; 77 + struct phy_device_node *pdn; 127 78 struct phy_device *phydev; 128 79 129 - phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PHY_HEADER, 130 - extack); 131 - if (!phydev) 132 - return 0; 80 + /* RTNL is held by the caller */ 81 + phydev = ethnl_req_get_phydev(req_info, tb, ETHTOOL_A_PHY_HEADER, 82 + info->extack); 83 + if (IS_ERR_OR_NULL(phydev)) 84 + return -EOPNOTSUPP; 133 85 134 - if (IS_ERR(phydev)) 135 - return PTR_ERR(phydev); 86 + pdn = xa_load(&topo->phys, phydev->phyindex); 87 + if (!pdn) 88 + return -EOPNOTSUPP; 136 89 137 - if (!topo) 138 - return 0; 90 + rep_data->phyindex = phydev->phyindex; 91 + rep_data->name = kstrdup(dev_name(&phydev->mdio.dev), GFP_KERNEL); 92 + rep_data->drvname = kstrdup(phydev->drv->name, GFP_KERNEL); 93 + rep_data->upstream_type = pdn->upstream_type; 139 94 140 - req_info->pdn = xa_load(&topo->phys, phydev->phyindex); 95 + if (pdn->upstream_type == PHY_UPSTREAM_PHY) { 96 + struct phy_device *upstream = pdn->upstream.phydev; 97 + rep_data->upstream_index = upstream->phyindex; 98 + } 99 + 100 + if (pdn->parent_sfp_bus) 101 + rep_data->upstream_sfp_name = kstrdup(sfp_get_name(pdn->parent_sfp_bus), 102 + GFP_KERNEL); 103 + 104 + if (phydev->sfp_bus) 105 + rep_data->downstream_sfp_name = kstrdup(sfp_get_name(phydev->sfp_bus), 106 + GFP_KERNEL); 141 107 142 108 return 0; 143 109 } 144 110 145 - int ethnl_phy_doit(struct sk_buff *skb, struct genl_info *info) 111 + static int phy_fill_reply(struct sk_buff *skb, 112 + const struct ethnl_req_info *req_info, 113 + const struct ethnl_reply_data *reply_data) 146 114 { 147 - struct phy_req_info req_info = {}; 148 - struct nlattr **tb = info->attrs; 149 - struct sk_buff *rskb; 150 - void *reply_payload; 151 - int reply_len; 152 - int ret; 115 + struct phy_reply_data *rep_data = PHY_REPDATA(reply_data); 153 116 154 - ret = ethnl_parse_header_dev_get(&req_info.base, 155 - tb[ETHTOOL_A_PHY_HEADER], 156 - genl_info_net(info), info->extack, 157 - true); 158 - if (ret < 0) 159 - return ret; 117 + if (nla_put_u32(skb, ETHTOOL_A_PHY_INDEX, rep_data->phyindex) || 118 + nla_put_string(skb, ETHTOOL_A_PHY_NAME, rep_data->name) || 119 + nla_put_u32(skb, ETHTOOL_A_PHY_UPSTREAM_TYPE, rep_data->upstream_type)) 120 + return -EMSGSIZE; 160 121 161 - rtnl_lock(); 162 - netdev_lock_ops(req_info.base.dev); 122 + if (rep_data->drvname && 123 + nla_put_string(skb, ETHTOOL_A_PHY_DRVNAME, rep_data->drvname)) 124 + return -EMSGSIZE; 163 125 164 - ret = ethnl_phy_parse_request(&req_info.base, tb, info->extack); 165 - if (ret < 0) 166 - goto err_unlock; 126 + if (rep_data->upstream_index && 127 + nla_put_u32(skb, ETHTOOL_A_PHY_UPSTREAM_INDEX, 128 + rep_data->upstream_index)) 129 + return -EMSGSIZE; 167 130 168 - /* No PHY, return early */ 169 - if (!req_info.pdn) 170 - goto err_unlock; 131 + if (rep_data->upstream_sfp_name && 132 + nla_put_string(skb, ETHTOOL_A_PHY_UPSTREAM_SFP_NAME, 133 + rep_data->upstream_sfp_name)) 134 + return -EMSGSIZE; 171 135 172 - ret = ethnl_phy_reply_size(&req_info.base, info->extack); 173 - if (ret < 0) 174 - goto err_unlock; 175 - reply_len = ret + ethnl_reply_header_size(); 136 + if (rep_data->downstream_sfp_name && 137 + nla_put_string(skb, ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME, 138 + rep_data->downstream_sfp_name)) 139 + return -EMSGSIZE; 176 140 177 - rskb = ethnl_reply_init(reply_len, req_info.base.dev, 178 - ETHTOOL_MSG_PHY_GET_REPLY, 179 - ETHTOOL_A_PHY_HEADER, 180 - info, &reply_payload); 181 - if (!rskb) { 182 - ret = -ENOMEM; 183 - goto err_unlock; 184 - } 185 - 186 - ret = ethnl_phy_fill_reply(&req_info.base, rskb); 187 - if (ret) 188 - goto err_free_msg; 189 - 190 - netdev_unlock_ops(req_info.base.dev); 191 - rtnl_unlock(); 192 - ethnl_parse_header_dev_put(&req_info.base); 193 - genlmsg_end(rskb, reply_payload); 194 - 195 - return genlmsg_reply(rskb, info); 196 - 197 - err_free_msg: 198 - nlmsg_free(rskb); 199 - err_unlock: 200 - netdev_unlock_ops(req_info.base.dev); 201 - rtnl_unlock(); 202 - ethnl_parse_header_dev_put(&req_info.base); 203 - return ret; 141 + return 0; 204 142 } 205 143 206 - struct ethnl_phy_dump_ctx { 207 - struct phy_req_info *phy_req_info; 208 - unsigned long ifindex; 209 - unsigned long phy_index; 144 + static void phy_cleanup_data(struct ethnl_reply_data *reply_data) 145 + { 146 + struct phy_reply_data *rep_data = PHY_REPDATA(reply_data); 147 + 148 + kfree(rep_data->drvname); 149 + kfree(rep_data->name); 150 + kfree(rep_data->upstream_sfp_name); 151 + kfree(rep_data->downstream_sfp_name); 152 + } 153 + 154 + const struct ethnl_request_ops ethnl_phy_request_ops = { 155 + .request_cmd = ETHTOOL_MSG_PHY_GET, 156 + .reply_cmd = ETHTOOL_MSG_PHY_GET_REPLY, 157 + .hdr_attr = ETHTOOL_A_PHY_HEADER, 158 + .req_info_size = sizeof(struct phy_req_info), 159 + .reply_data_size = sizeof(struct phy_reply_data), 160 + 161 + .prepare_data = phy_prepare_data, 162 + .reply_size = phy_reply_size, 163 + .fill_reply = phy_fill_reply, 164 + .cleanup_data = phy_cleanup_data, 210 165 }; 211 - 212 - int ethnl_phy_start(struct netlink_callback *cb) 213 - { 214 - const struct genl_info *info = genl_info_dump(cb); 215 - struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx; 216 - int ret; 217 - 218 - BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); 219 - 220 - ctx->phy_req_info = kzalloc(sizeof(*ctx->phy_req_info), GFP_KERNEL); 221 - if (!ctx->phy_req_info) 222 - return -ENOMEM; 223 - 224 - ret = ethnl_parse_header_dev_get(&ctx->phy_req_info->base, 225 - info->attrs[ETHTOOL_A_PHY_HEADER], 226 - sock_net(cb->skb->sk), cb->extack, 227 - false); 228 - ctx->ifindex = 0; 229 - ctx->phy_index = 0; 230 - 231 - if (ret) 232 - kfree(ctx->phy_req_info); 233 - 234 - return ret; 235 - } 236 - 237 - int ethnl_phy_done(struct netlink_callback *cb) 238 - { 239 - struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx; 240 - 241 - if (ctx->phy_req_info->base.dev) 242 - ethnl_parse_header_dev_put(&ctx->phy_req_info->base); 243 - 244 - kfree(ctx->phy_req_info); 245 - 246 - return 0; 247 - } 248 - 249 - static int ethnl_phy_dump_one_dev(struct sk_buff *skb, struct net_device *dev, 250 - struct netlink_callback *cb) 251 - { 252 - struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx; 253 - struct phy_req_info *pri = ctx->phy_req_info; 254 - struct phy_device_node *pdn; 255 - int ret = 0; 256 - void *ehdr; 257 - 258 - if (!dev->link_topo) 259 - return 0; 260 - 261 - xa_for_each_start(&dev->link_topo->phys, ctx->phy_index, pdn, ctx->phy_index) { 262 - ehdr = ethnl_dump_put(skb, cb, ETHTOOL_MSG_PHY_GET_REPLY); 263 - if (!ehdr) { 264 - ret = -EMSGSIZE; 265 - break; 266 - } 267 - 268 - ret = ethnl_fill_reply_header(skb, dev, ETHTOOL_A_PHY_HEADER); 269 - if (ret < 0) { 270 - genlmsg_cancel(skb, ehdr); 271 - break; 272 - } 273 - 274 - pri->pdn = pdn; 275 - ret = ethnl_phy_fill_reply(&pri->base, skb); 276 - if (ret < 0) { 277 - genlmsg_cancel(skb, ehdr); 278 - break; 279 - } 280 - 281 - genlmsg_end(skb, ehdr); 282 - } 283 - 284 - return ret; 285 - } 286 - 287 - int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 288 - { 289 - struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx; 290 - struct net *net = sock_net(skb->sk); 291 - struct net_device *dev; 292 - int ret = 0; 293 - 294 - rtnl_lock(); 295 - 296 - if (ctx->phy_req_info->base.dev) { 297 - dev = ctx->phy_req_info->base.dev; 298 - netdev_lock_ops(dev); 299 - ret = ethnl_phy_dump_one_dev(skb, dev, cb); 300 - netdev_unlock_ops(dev); 301 - } else { 302 - for_each_netdev_dump(net, dev, ctx->ifindex) { 303 - netdev_lock_ops(dev); 304 - ret = ethnl_phy_dump_one_dev(skb, dev, cb); 305 - netdev_unlock_ops(dev); 306 - if (ret) 307 - break; 308 - 309 - ctx->phy_index = 0; 310 - } 311 - } 312 - rtnl_unlock(); 313 - 314 - return ret; 315 - }