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

net: ethtool: tsinfo: Enhance tsinfo to support several hwtstamp by net topology

Either the MAC or the PHY can provide hwtstamp, so we should be able to
read the tsinfo for any hwtstamp provider.

Enhance 'get' command to retrieve tsinfo of hwtstamp providers within a
network topology.

Add support for a specific dump command to retrieve all hwtstamp
providers within the network topology, with added functionality for
filtered dump to target a single interface.

Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Kory Maincent and committed by
David S. Miller
b9e3f7dc 35f7cad1

+563 -21
+20
Documentation/netlink/specs/ethtool.yaml
··· 837 837 name: tx-err 838 838 type: uint 839 839 - 840 + name: ts-hwtstamp-provider 841 + attr-cnt-name: __ethtool-a-ts-hwtstamp-provider-cnt 842 + attributes: 843 + - 844 + name: unspec 845 + type: unused 846 + value: 0 847 + - 848 + name: index 849 + type: u32 850 + - 851 + name: qualifier 852 + type: u32 853 + - 840 854 name: tsinfo 841 855 attr-cnt-name: __ethtool-a-tsinfo-cnt 842 856 attributes: ··· 881 867 name: stats 882 868 type: nest 883 869 nested-attributes: ts-stat 870 + - 871 + name: hwtstamp-provider 872 + type: nest 873 + nested-attributes: ts-hwtstamp-provider 884 874 - 885 875 name: cable-result 886 876 attr-cnt-name: __ethtool-a-cable-result-cnt ··· 1930 1912 request: 1931 1913 attributes: 1932 1914 - header 1915 + - hwtstamp-provider 1933 1916 reply: 1934 1917 attributes: 1935 1918 - header ··· 1939 1920 - rx-filters 1940 1921 - phc-index 1941 1922 - stats 1923 + - hwtstamp-provider 1942 1924 dump: *tsinfo-get-op 1943 1925 - 1944 1926 name: cable-test-act
+4 -3
Documentation/networking/ethtool-netlink.rst
··· 1245 1245 1246 1246 Request contents: 1247 1247 1248 - ===================================== ====== ========================== 1249 - ``ETHTOOL_A_TSINFO_HEADER`` nested request header 1250 - ===================================== ====== ========================== 1248 + ======================================== ====== ============================ 1249 + ``ETHTOOL_A_TSINFO_HEADER`` nested request header 1250 + ``ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER`` nested PTP hw clock provider 1251 + ======================================== ====== ============================ 1251 1252 1252 1253 Kernel response contents: 1253 1254
+4
include/linux/ethtool.h
··· 711 711 * @cmd: command number = %ETHTOOL_GET_TS_INFO 712 712 * @so_timestamping: bit mask of the sum of the supported SO_TIMESTAMPING flags 713 713 * @phc_index: device index of the associated PHC, or -1 if there is none 714 + * @phc_qualifier: qualifier of the associated PHC 714 715 * @tx_types: bit mask of the supported hwtstamp_tx_types enumeration values 715 716 * @rx_filters: bit mask of the supported hwtstamp_rx_filters enumeration values 716 717 */ ··· 719 718 u32 cmd; 720 719 u32 so_timestamping; 721 720 int phc_index; 721 + enum hwtstamp_provider_qualifier phc_qualifier; 722 722 enum hwtstamp_tx_types tx_types; 723 723 enum hwtstamp_rx_filters rx_filters; 724 724 }; ··· 751 749 * @rss_context argument to @create_rxfh_context and friends. 752 750 * @supported_coalesce_params: supported types of interrupt coalescing. 753 751 * @supported_ring_params: supported ring params. 752 + * @supported_hwtstamp_qualifiers: bitfield of supported hwtstamp qualifier. 754 753 * @get_drvinfo: Report driver/device information. Modern drivers no 755 754 * longer have to implement this callback. Most fields are 756 755 * correctly filled in by the core using system information, or ··· 969 966 u32 rxfh_max_num_contexts; 970 967 u32 supported_coalesce_params; 971 968 u32 supported_ring_params; 969 + u32 supported_hwtstamp_qualifiers; 972 970 void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *); 973 971 int (*get_regs_len)(struct net_device *); 974 972 void (*get_regs)(struct net_device *, struct ethtool_regs *, void *);
+132 -9
net/ethtool/common.c
··· 5 5 #include <linux/phy.h> 6 6 #include <linux/rtnetlink.h> 7 7 #include <linux/ptp_clock_kernel.h> 8 + #include <linux/phy_link_topology.h> 8 9 9 10 #include "netlink.h" 10 11 #include "common.h" 12 + #include "../core/dev.h" 13 + 11 14 12 15 const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = { 13 16 [NETIF_F_SG_BIT] = "tx-scatter-gather", ··· 766 763 return 0; 767 764 } 768 765 769 - int __ethtool_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info *info) 766 + static void ethtool_init_tsinfo(struct kernel_ethtool_ts_info *info) 770 767 { 771 - const struct ethtool_ops *ops = dev->ethtool_ops; 772 - struct phy_device *phydev = dev->phydev; 773 - int err = 0; 774 - 775 768 memset(info, 0, sizeof(*info)); 776 769 info->cmd = ETHTOOL_GET_TS_INFO; 777 770 info->phc_index = -1; 771 + } 778 772 779 - if (phy_is_default_hwtstamp(phydev) && phy_has_tsinfo(phydev)) 780 - err = phy_ts_info(phydev, info); 781 - else if (ops->get_ts_info) 782 - err = ops->get_ts_info(dev, info); 773 + int ethtool_net_get_ts_info_by_phc(struct net_device *dev, 774 + struct kernel_ethtool_ts_info *info, 775 + struct hwtstamp_provider_desc *hwprov_desc) 776 + { 777 + const struct ethtool_ops *ops = dev->ethtool_ops; 778 + int err; 779 + 780 + if (!ops->get_ts_info) 781 + return -ENODEV; 782 + 783 + /* Does ptp comes from netdev */ 784 + ethtool_init_tsinfo(info); 785 + info->phc_qualifier = hwprov_desc->qualifier; 786 + err = ops->get_ts_info(dev, info); 787 + if (err) 788 + return err; 789 + 790 + if (info->phc_index == hwprov_desc->index && 791 + net_support_hwtstamp_qualifier(dev, hwprov_desc->qualifier)) 792 + return 0; 793 + 794 + return -ENODEV; 795 + } 796 + 797 + int 798 + ethtool_phy_get_ts_info_by_phc(struct net_device *dev, 799 + struct kernel_ethtool_ts_info *info, 800 + struct hwtstamp_provider_desc *hwprov_desc) 801 + { 802 + int err; 803 + 804 + /* Only precise qualifier is supported in phydev */ 805 + if (hwprov_desc->qualifier != HWTSTAMP_PROVIDER_QUALIFIER_PRECISE) 806 + return -ENODEV; 807 + 808 + /* Look in the phy topology */ 809 + if (dev->link_topo) { 810 + struct phy_device_node *pdn; 811 + unsigned long phy_index; 812 + 813 + xa_for_each(&dev->link_topo->phys, phy_index, pdn) { 814 + if (!phy_has_tsinfo(pdn->phy)) 815 + continue; 816 + 817 + ethtool_init_tsinfo(info); 818 + err = phy_ts_info(pdn->phy, info); 819 + if (err) 820 + return err; 821 + 822 + if (info->phc_index == hwprov_desc->index) 823 + return 0; 824 + } 825 + return -ENODEV; 826 + } 827 + 828 + /* Look on the dev->phydev */ 829 + if (phy_has_tsinfo(dev->phydev)) { 830 + ethtool_init_tsinfo(info); 831 + err = phy_ts_info(dev->phydev, info); 832 + if (err) 833 + return err; 834 + 835 + if (info->phc_index == hwprov_desc->index) 836 + return 0; 837 + } 838 + 839 + return -ENODEV; 840 + } 841 + 842 + int ethtool_get_ts_info_by_phc(struct net_device *dev, 843 + struct kernel_ethtool_ts_info *info, 844 + struct hwtstamp_provider_desc *hwprov_desc) 845 + { 846 + int err; 847 + 848 + err = ethtool_net_get_ts_info_by_phc(dev, info, hwprov_desc); 849 + if (err == -ENODEV) 850 + err = ethtool_phy_get_ts_info_by_phc(dev, info, hwprov_desc); 783 851 784 852 info->so_timestamping |= SOF_TIMESTAMPING_RX_SOFTWARE | 785 853 SOF_TIMESTAMPING_SOFTWARE; 786 854 787 855 return err; 856 + } 857 + 858 + int __ethtool_get_ts_info(struct net_device *dev, 859 + struct kernel_ethtool_ts_info *info) 860 + { 861 + struct hwtstamp_provider *hwprov; 862 + 863 + hwprov = rtnl_dereference(dev->hwprov); 864 + /* No provider specified, use default behavior */ 865 + if (!hwprov) { 866 + const struct ethtool_ops *ops = dev->ethtool_ops; 867 + struct phy_device *phydev = dev->phydev; 868 + int err = 0; 869 + 870 + ethtool_init_tsinfo(info); 871 + if (phy_is_default_hwtstamp(phydev) && 872 + phy_has_tsinfo(phydev)) 873 + err = phy_ts_info(phydev, info); 874 + else if (ops->get_ts_info) 875 + err = ops->get_ts_info(dev, info); 876 + 877 + info->so_timestamping |= SOF_TIMESTAMPING_RX_SOFTWARE | 878 + SOF_TIMESTAMPING_SOFTWARE; 879 + 880 + return err; 881 + } 882 + 883 + return ethtool_get_ts_info_by_phc(dev, info, &hwprov->desc); 884 + } 885 + 886 + bool net_support_hwtstamp_qualifier(struct net_device *dev, 887 + enum hwtstamp_provider_qualifier qualifier) 888 + { 889 + const struct ethtool_ops *ops = dev->ethtool_ops; 890 + 891 + if (!ops) 892 + return false; 893 + 894 + /* Return true with precise qualifier and with NIC without 895 + * qualifier description to not break the old behavior. 896 + */ 897 + if (!ops->supported_hwtstamp_qualifiers && 898 + qualifier == HWTSTAMP_PROVIDER_QUALIFIER_PRECISE) 899 + return true; 900 + 901 + if (ops->supported_hwtstamp_qualifiers & BIT(qualifier)) 902 + return true; 903 + 904 + return false; 788 905 } 789 906 790 907 int ethtool_get_phc_vclocks(struct net_device *dev, int **vclock_index)
+13
net/ethtool/common.h
··· 21 21 }; 22 22 23 23 struct genl_info; 24 + struct hwtstamp_provider_desc; 24 25 25 26 extern const char 26 27 netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]; ··· 50 49 struct genl_info *info); 51 50 int ethtool_check_rss_ctx_busy(struct net_device *dev, u32 rss_context); 52 51 int __ethtool_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info *info); 52 + int ethtool_get_ts_info_by_phc(struct net_device *dev, 53 + struct kernel_ethtool_ts_info *info, 54 + struct hwtstamp_provider_desc *hwprov_desc); 55 + int ethtool_net_get_ts_info_by_phc(struct net_device *dev, 56 + struct kernel_ethtool_ts_info *info, 57 + struct hwtstamp_provider_desc *hwprov_desc); 58 + int 59 + ethtool_phy_get_ts_info_by_phc(struct net_device *dev, 60 + struct kernel_ethtool_ts_info *info, 61 + struct hwtstamp_provider_desc *hwprov_desc); 62 + bool net_support_hwtstamp_qualifier(struct net_device *dev, 63 + enum hwtstamp_provider_qualifier qualifier); 53 64 54 65 extern const struct ethtool_phy_ops *ethtool_phy_ops; 55 66 extern const struct ethtool_pse_ops *ethtool_pse_ops;
+3 -3
net/ethtool/netlink.c
··· 1074 1074 { 1075 1075 .cmd = ETHTOOL_MSG_TSINFO_GET, 1076 1076 .doit = ethnl_default_doit, 1077 - .start = ethnl_default_start, 1078 - .dumpit = ethnl_default_dumpit, 1079 - .done = ethnl_default_done, 1077 + .start = ethnl_tsinfo_start, 1078 + .dumpit = ethnl_tsinfo_dumpit, 1079 + .done = ethnl_tsinfo_done, 1080 1080 .policy = ethnl_tsinfo_get_policy, 1081 1081 .maxattr = ARRAY_SIZE(ethnl_tsinfo_get_policy) - 1, 1082 1082 },
+4 -1
net/ethtool/netlink.h
··· 464 464 extern const struct nla_policy ethnl_pause_set_policy[ETHTOOL_A_PAUSE_TX + 1]; 465 465 extern const struct nla_policy ethnl_eee_get_policy[ETHTOOL_A_EEE_HEADER + 1]; 466 466 extern const struct nla_policy ethnl_eee_set_policy[ETHTOOL_A_EEE_TX_LPI_TIMER + 1]; 467 - extern const struct nla_policy ethnl_tsinfo_get_policy[ETHTOOL_A_TSINFO_HEADER + 1]; 467 + extern const struct nla_policy ethnl_tsinfo_get_policy[ETHTOOL_A_TSINFO_MAX + 1]; 468 468 extern const struct nla_policy ethnl_cable_test_act_policy[ETHTOOL_A_CABLE_TEST_HEADER + 1]; 469 469 extern const struct nla_policy ethnl_cable_test_tdr_act_policy[ETHTOOL_A_CABLE_TEST_TDR_CFG + 1]; 470 470 extern const struct nla_policy ethnl_tunnel_info_get_policy[ETHTOOL_A_TUNNEL_INFO_HEADER + 1]; ··· 499 499 int ethnl_phy_doit(struct sk_buff *skb, struct genl_info *info); 500 500 int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 501 501 int ethnl_phy_done(struct netlink_callback *cb); 502 + int ethnl_tsinfo_start(struct netlink_callback *cb); 503 + int ethnl_tsinfo_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 504 + int ethnl_tsinfo_done(struct netlink_callback *cb); 502 505 503 506 extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; 504 507 extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
+20
net/ethtool/ts.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + 3 + #ifndef _NET_ETHTOOL_TS_H 4 + #define _NET_ETHTOOL_TS_H 5 + 6 + #include "netlink.h" 7 + 8 + static const struct nla_policy 9 + ethnl_ts_hwtst_prov_policy[ETHTOOL_A_TS_HWTSTAMP_PROVIDER_MAX + 1] = { 10 + [ETHTOOL_A_TS_HWTSTAMP_PROVIDER_INDEX] = { .type = NLA_U32 }, 11 + [ETHTOOL_A_TS_HWTSTAMP_PROVIDER_QUALIFIER] = 12 + NLA_POLICY_MAX(NLA_U32, HWTSTAMP_PROVIDER_QUALIFIER_CNT - 1) 13 + }; 14 + 15 + int ts_parse_hwtst_provider(const struct nlattr *nest, 16 + struct hwtstamp_provider_desc *hwprov_desc, 17 + struct netlink_ext_ack *extack, 18 + bool *mod); 19 + 20 + #endif /* _NET_ETHTOOL_TS_H */
+353 -5
net/ethtool/tsinfo.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 3 3 #include <linux/net_tstamp.h> 4 + #include <linux/phy.h> 5 + #include <linux/phy_link_topology.h> 6 + #include <linux/ptp_clock_kernel.h> 4 7 5 8 #include "netlink.h" 6 9 #include "common.h" 7 10 #include "bitset.h" 11 + #include "ts.h" 8 12 9 13 struct tsinfo_req_info { 10 14 struct ethnl_req_info base; 15 + struct hwtstamp_provider_desc hwprov_desc; 11 16 }; 12 17 13 18 struct tsinfo_reply_data { ··· 21 16 struct ethtool_ts_stats stats; 22 17 }; 23 18 19 + #define TSINFO_REQINFO(__req_base) \ 20 + container_of(__req_base, struct tsinfo_req_info, base) 21 + 24 22 #define TSINFO_REPDATA(__reply_base) \ 25 23 container_of(__reply_base, struct tsinfo_reply_data, base) 26 24 27 25 #define ETHTOOL_TS_STAT_CNT \ 28 26 (__ETHTOOL_A_TS_STAT_CNT - (ETHTOOL_A_TS_STAT_UNSPEC + 1)) 29 27 30 - const struct nla_policy ethnl_tsinfo_get_policy[] = { 28 + const struct nla_policy ethnl_tsinfo_get_policy[ETHTOOL_A_TSINFO_MAX + 1] = { 31 29 [ETHTOOL_A_TSINFO_HEADER] = 32 30 NLA_POLICY_NESTED(ethnl_header_policy_stats), 31 + [ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER] = 32 + NLA_POLICY_NESTED(ethnl_ts_hwtst_prov_policy), 33 33 }; 34 + 35 + int ts_parse_hwtst_provider(const struct nlattr *nest, 36 + struct hwtstamp_provider_desc *hwprov_desc, 37 + struct netlink_ext_ack *extack, 38 + bool *mod) 39 + { 40 + struct nlattr *tb[ARRAY_SIZE(ethnl_ts_hwtst_prov_policy)]; 41 + int ret; 42 + 43 + ret = nla_parse_nested(tb, 44 + ARRAY_SIZE(ethnl_ts_hwtst_prov_policy) - 1, 45 + nest, 46 + ethnl_ts_hwtst_prov_policy, extack); 47 + if (ret < 0) 48 + return ret; 49 + 50 + if (NL_REQ_ATTR_CHECK(extack, nest, tb, 51 + ETHTOOL_A_TS_HWTSTAMP_PROVIDER_INDEX) || 52 + NL_REQ_ATTR_CHECK(extack, nest, tb, 53 + ETHTOOL_A_TS_HWTSTAMP_PROVIDER_QUALIFIER)) 54 + return -EINVAL; 55 + 56 + ethnl_update_u32(&hwprov_desc->index, 57 + tb[ETHTOOL_A_TS_HWTSTAMP_PROVIDER_INDEX], 58 + mod); 59 + ethnl_update_u32(&hwprov_desc->qualifier, 60 + tb[ETHTOOL_A_TS_HWTSTAMP_PROVIDER_QUALIFIER], 61 + mod); 62 + 63 + return 0; 64 + } 65 + 66 + static int 67 + tsinfo_parse_request(struct ethnl_req_info *req_base, struct nlattr **tb, 68 + struct netlink_ext_ack *extack) 69 + { 70 + struct tsinfo_req_info *req = TSINFO_REQINFO(req_base); 71 + bool mod = false; 72 + 73 + req->hwprov_desc.index = -1; 74 + 75 + if (!tb[ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER]) 76 + return 0; 77 + 78 + return ts_parse_hwtst_provider(tb[ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER], 79 + &req->hwprov_desc, extack, &mod); 80 + } 34 81 35 82 static int tsinfo_prepare_data(const struct ethnl_req_info *req_base, 36 83 struct ethnl_reply_data *reply_base, 37 84 const struct genl_info *info) 38 85 { 39 86 struct tsinfo_reply_data *data = TSINFO_REPDATA(reply_base); 87 + struct tsinfo_req_info *req = TSINFO_REQINFO(req_base); 40 88 struct net_device *dev = reply_base->dev; 41 89 int ret; 42 90 43 91 ret = ethnl_ops_begin(dev); 44 92 if (ret < 0) 45 93 return ret; 94 + 95 + if (req->hwprov_desc.index != -1) { 96 + ret = ethtool_get_ts_info_by_phc(dev, &data->ts_info, 97 + &req->hwprov_desc); 98 + ethnl_ops_complete(dev); 99 + return ret; 100 + } 101 + 46 102 if (req_base->flags & ETHTOOL_FLAG_STATS) { 47 103 ethtool_stats_init((u64 *)&data->stats, 48 104 sizeof(data->stats) / sizeof(u64)); 49 105 if (dev->ethtool_ops->get_ts_stats) 50 106 dev->ethtool_ops->get_ts_stats(dev, &data->stats); 51 107 } 108 + 52 109 ret = __ethtool_get_ts_info(dev, &data->ts_info); 53 110 ethnl_ops_complete(dev); 54 111 ··· 154 87 return ret; 155 88 len += ret; /* _TSINFO_RX_FILTERS */ 156 89 } 157 - if (ts_info->phc_index >= 0) 90 + if (ts_info->phc_index >= 0) { 158 91 len += nla_total_size(sizeof(u32)); /* _TSINFO_PHC_INDEX */ 92 + /* _TSINFO_HWTSTAMP_PROVIDER */ 93 + len += nla_total_size(0) + 2 * nla_total_size(sizeof(u32)); 94 + } 159 95 if (req_base->flags & ETHTOOL_FLAG_STATS) 160 96 len += nla_total_size(0) + /* _TSINFO_STATS */ 161 97 nla_total_size_64bit(sizeof(u64)) * ETHTOOL_TS_STAT_CNT; ··· 233 163 if (ret < 0) 234 164 return ret; 235 165 } 236 - if (ts_info->phc_index >= 0 && 237 - nla_put_u32(skb, ETHTOOL_A_TSINFO_PHC_INDEX, ts_info->phc_index)) 238 - return -EMSGSIZE; 166 + if (ts_info->phc_index >= 0) { 167 + struct nlattr *nest; 168 + 169 + ret = nla_put_u32(skb, ETHTOOL_A_TSINFO_PHC_INDEX, 170 + ts_info->phc_index); 171 + if (ret) 172 + return -EMSGSIZE; 173 + 174 + nest = nla_nest_start(skb, ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER); 175 + if (!nest) 176 + return -EMSGSIZE; 177 + 178 + if (nla_put_u32(skb, ETHTOOL_A_TS_HWTSTAMP_PROVIDER_INDEX, 179 + ts_info->phc_index) || 180 + nla_put_u32(skb, 181 + ETHTOOL_A_TS_HWTSTAMP_PROVIDER_QUALIFIER, 182 + ts_info->phc_qualifier)) { 183 + nla_nest_cancel(skb, nest); 184 + return -EMSGSIZE; 185 + } 186 + 187 + nla_nest_end(skb, nest); 188 + } 239 189 if (req_base->flags & ETHTOOL_FLAG_STATS && 240 190 tsinfo_put_stats(skb, &data->stats)) 241 191 return -EMSGSIZE; 192 + 193 + return 0; 194 + } 195 + 196 + struct ethnl_tsinfo_dump_ctx { 197 + struct tsinfo_req_info *req_info; 198 + struct tsinfo_reply_data *reply_data; 199 + unsigned long pos_ifindex; 200 + bool netdev_dump_done; 201 + unsigned long pos_phyindex; 202 + enum hwtstamp_provider_qualifier pos_phcqualifier; 203 + }; 204 + 205 + static void *ethnl_tsinfo_prepare_dump(struct sk_buff *skb, 206 + struct net_device *dev, 207 + struct tsinfo_reply_data *reply_data, 208 + struct netlink_callback *cb) 209 + { 210 + struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx; 211 + void *ehdr = NULL; 212 + 213 + ehdr = ethnl_dump_put(skb, cb, 214 + ETHTOOL_MSG_TSINFO_GET_REPLY); 215 + if (!ehdr) 216 + return ERR_PTR(-EMSGSIZE); 217 + 218 + reply_data = ctx->reply_data; 219 + memset(reply_data, 0, sizeof(*reply_data)); 220 + reply_data->base.dev = dev; 221 + memset(&reply_data->ts_info, 0, sizeof(reply_data->ts_info)); 222 + 223 + return ehdr; 224 + } 225 + 226 + static int ethnl_tsinfo_end_dump(struct sk_buff *skb, 227 + struct net_device *dev, 228 + struct tsinfo_req_info *req_info, 229 + struct tsinfo_reply_data *reply_data, 230 + void *ehdr) 231 + { 232 + int ret; 233 + 234 + reply_data->ts_info.so_timestamping |= SOF_TIMESTAMPING_RX_SOFTWARE | 235 + SOF_TIMESTAMPING_SOFTWARE; 236 + 237 + ret = ethnl_fill_reply_header(skb, dev, ETHTOOL_A_TSINFO_HEADER); 238 + if (ret < 0) 239 + return ret; 240 + 241 + ret = tsinfo_fill_reply(skb, &req_info->base, &reply_data->base); 242 + if (ret < 0) 243 + return ret; 244 + 245 + reply_data->base.dev = NULL; 246 + genlmsg_end(skb, ehdr); 247 + 248 + return ret; 249 + } 250 + 251 + static int ethnl_tsinfo_dump_one_phydev(struct sk_buff *skb, 252 + struct net_device *dev, 253 + struct phy_device *phydev, 254 + struct netlink_callback *cb) 255 + { 256 + struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx; 257 + struct tsinfo_reply_data *reply_data; 258 + struct tsinfo_req_info *req_info; 259 + void *ehdr = NULL; 260 + int ret = 0; 261 + 262 + if (!phy_has_tsinfo(phydev)) 263 + return -EOPNOTSUPP; 264 + 265 + reply_data = ctx->reply_data; 266 + req_info = ctx->req_info; 267 + ehdr = ethnl_tsinfo_prepare_dump(skb, dev, reply_data, cb); 268 + if (IS_ERR(ehdr)) 269 + return PTR_ERR(ehdr); 270 + 271 + ret = phy_ts_info(phydev, &reply_data->ts_info); 272 + if (ret < 0) 273 + goto err; 274 + 275 + ret = ethnl_tsinfo_end_dump(skb, dev, req_info, reply_data, ehdr); 276 + if (ret < 0) 277 + goto err; 278 + 279 + return ret; 280 + err: 281 + genlmsg_cancel(skb, ehdr); 282 + return ret; 283 + } 284 + 285 + static int ethnl_tsinfo_dump_one_netdev(struct sk_buff *skb, 286 + struct net_device *dev, 287 + struct netlink_callback *cb) 288 + { 289 + struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx; 290 + const struct ethtool_ops *ops = dev->ethtool_ops; 291 + struct tsinfo_reply_data *reply_data; 292 + struct tsinfo_req_info *req_info; 293 + void *ehdr = NULL; 294 + int ret = 0; 295 + 296 + if (!ops->get_ts_info) 297 + return -EOPNOTSUPP; 298 + 299 + reply_data = ctx->reply_data; 300 + req_info = ctx->req_info; 301 + for (; ctx->pos_phcqualifier < HWTSTAMP_PROVIDER_QUALIFIER_CNT; 302 + ctx->pos_phcqualifier++) { 303 + if (!net_support_hwtstamp_qualifier(dev, 304 + ctx->pos_phcqualifier)) 305 + continue; 306 + 307 + ehdr = ethnl_tsinfo_prepare_dump(skb, dev, reply_data, cb); 308 + if (IS_ERR(ehdr)) { 309 + ret = PTR_ERR(ehdr); 310 + goto err; 311 + } 312 + 313 + reply_data->ts_info.phc_qualifier = ctx->pos_phcqualifier; 314 + ret = ops->get_ts_info(dev, &reply_data->ts_info); 315 + if (ret < 0) 316 + goto err; 317 + 318 + ret = ethnl_tsinfo_end_dump(skb, dev, req_info, reply_data, 319 + ehdr); 320 + if (ret < 0) 321 + goto err; 322 + } 323 + 324 + return ret; 325 + 326 + err: 327 + genlmsg_cancel(skb, ehdr); 328 + return ret; 329 + } 330 + 331 + static int ethnl_tsinfo_dump_one_net_topo(struct sk_buff *skb, 332 + struct net_device *dev, 333 + struct netlink_callback *cb) 334 + { 335 + struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx; 336 + struct phy_device_node *pdn; 337 + int ret = 0; 338 + 339 + if (!ctx->netdev_dump_done) { 340 + ret = ethnl_tsinfo_dump_one_netdev(skb, dev, cb); 341 + if (ret < 0 && ret != -EOPNOTSUPP) 342 + return ret; 343 + ctx->netdev_dump_done = true; 344 + } 345 + 346 + if (!dev->link_topo) { 347 + if (phy_has_tsinfo(dev->phydev)) { 348 + ret = ethnl_tsinfo_dump_one_phydev(skb, dev, 349 + dev->phydev, cb); 350 + if (ret < 0 && ret != -EOPNOTSUPP) 351 + return ret; 352 + } 353 + 354 + return 0; 355 + } 356 + 357 + xa_for_each_start(&dev->link_topo->phys, ctx->pos_phyindex, pdn, 358 + ctx->pos_phyindex) { 359 + if (phy_has_tsinfo(pdn->phy)) { 360 + ret = ethnl_tsinfo_dump_one_phydev(skb, dev, 361 + pdn->phy, cb); 362 + if (ret < 0 && ret != -EOPNOTSUPP) 363 + return ret; 364 + } 365 + } 366 + 367 + return ret; 368 + } 369 + 370 + int ethnl_tsinfo_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 371 + { 372 + struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx; 373 + struct net *net = sock_net(skb->sk); 374 + struct net_device *dev; 375 + int ret = 0; 376 + 377 + rtnl_lock(); 378 + if (ctx->req_info->base.dev) { 379 + ret = ethnl_tsinfo_dump_one_net_topo(skb, 380 + ctx->req_info->base.dev, 381 + cb); 382 + } else { 383 + for_each_netdev_dump(net, dev, ctx->pos_ifindex) { 384 + ret = ethnl_tsinfo_dump_one_net_topo(skb, dev, cb); 385 + if (ret < 0 && ret != -EOPNOTSUPP) 386 + break; 387 + ctx->pos_phyindex = 0; 388 + ctx->netdev_dump_done = false; 389 + ctx->pos_phcqualifier = HWTSTAMP_PROVIDER_QUALIFIER_PRECISE; 390 + } 391 + } 392 + rtnl_unlock(); 393 + 394 + return ret; 395 + } 396 + 397 + int ethnl_tsinfo_start(struct netlink_callback *cb) 398 + { 399 + const struct genl_dumpit_info *info = genl_dumpit_info(cb); 400 + struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx; 401 + struct nlattr **tb = info->info.attrs; 402 + struct tsinfo_reply_data *reply_data; 403 + struct tsinfo_req_info *req_info; 404 + int ret; 405 + 406 + BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); 407 + 408 + req_info = kzalloc(sizeof(*req_info), GFP_KERNEL); 409 + if (!req_info) 410 + return -ENOMEM; 411 + reply_data = kzalloc(sizeof(*reply_data), GFP_KERNEL); 412 + if (!reply_data) { 413 + ret = -ENOMEM; 414 + goto free_req_info; 415 + } 416 + 417 + ret = ethnl_parse_header_dev_get(&req_info->base, 418 + tb[ETHTOOL_A_TSINFO_HEADER], 419 + sock_net(cb->skb->sk), cb->extack, 420 + false); 421 + if (ret < 0) 422 + goto free_reply_data; 423 + 424 + ctx->req_info = req_info; 425 + ctx->reply_data = reply_data; 426 + ctx->pos_ifindex = 0; 427 + ctx->pos_phyindex = 0; 428 + ctx->netdev_dump_done = false; 429 + ctx->pos_phcqualifier = HWTSTAMP_PROVIDER_QUALIFIER_PRECISE; 430 + 431 + return 0; 432 + 433 + free_reply_data: 434 + kfree(reply_data); 435 + free_req_info: 436 + kfree(req_info); 437 + 438 + return ret; 439 + } 440 + 441 + int ethnl_tsinfo_done(struct netlink_callback *cb) 442 + { 443 + struct ethnl_tsinfo_dump_ctx *ctx = (void *)cb->ctx; 444 + struct tsinfo_req_info *req_info = ctx->req_info; 445 + 446 + ethnl_parse_header_dev_put(&req_info->base); 447 + kfree(ctx->reply_data); 448 + kfree(ctx->req_info); 242 449 243 450 return 0; 244 451 } ··· 527 180 .req_info_size = sizeof(struct tsinfo_req_info), 528 181 .reply_data_size = sizeof(struct tsinfo_reply_data), 529 182 183 + .parse_request = tsinfo_parse_request, 530 184 .prepare_data = tsinfo_prepare_data, 531 185 .reply_size = tsinfo_reply_size, 532 186 .fill_reply = tsinfo_fill_reply,