~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ethtool/phy.c

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Copyright 2023 Bootlin
  4  *
  5  */
  6 #include "common.h"
  7 #include "netlink.h"
  8 
  9 #include <linux/phy.h>
 10 #include <linux/phy_link_topology.h>
 11 #include <linux/sfp.h>
 12 
 13 struct phy_req_info {
 14         struct ethnl_req_info           base;
 15         struct phy_device_node          *pdn;
 16 };
 17 
 18 #define PHY_REQINFO(__req_base) \
 19         container_of(__req_base, struct phy_req_info, base)
 20 
 21 const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1] = {
 22         [ETHTOOL_A_PHY_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
 23 };
 24 
 25 /* Caller holds rtnl */
 26 static ssize_t
 27 ethnl_phy_reply_size(const struct ethnl_req_info *req_base,
 28                      struct netlink_ext_ack *extack)
 29 {
 30         struct phy_req_info *req_info = PHY_REQINFO(req_base);
 31         struct phy_device_node *pdn = req_info->pdn;
 32         struct phy_device *phydev = pdn->phy;
 33         size_t size = 0;
 34 
 35         ASSERT_RTNL();
 36 
 37         /* ETHTOOL_A_PHY_INDEX */
 38         size += nla_total_size(sizeof(u32));
 39 
 40         /* ETHTOOL_A_DRVNAME */
 41         if (phydev->drv)
 42                 size += nla_total_size(strlen(phydev->drv->name) + 1);
 43 
 44         /* ETHTOOL_A_NAME */
 45         size += nla_total_size(strlen(dev_name(&phydev->mdio.dev)) + 1);
 46 
 47         /* ETHTOOL_A_PHY_UPSTREAM_TYPE */
 48         size += nla_total_size(sizeof(u32));
 49 
 50         if (phy_on_sfp(phydev)) {
 51                 const char *upstream_sfp_name = sfp_get_name(pdn->parent_sfp_bus);
 52 
 53                 /* ETHTOOL_A_PHY_UPSTREAM_SFP_NAME */
 54                 if (upstream_sfp_name)
 55                         size += nla_total_size(strlen(upstream_sfp_name) + 1);
 56 
 57                 /* ETHTOOL_A_PHY_UPSTREAM_INDEX */
 58                 size += nla_total_size(sizeof(u32));
 59         }
 60 
 61         /* ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME */
 62         if (phydev->sfp_bus) {
 63                 const char *sfp_name = sfp_get_name(phydev->sfp_bus);
 64 
 65                 if (sfp_name)
 66                         size += nla_total_size(strlen(sfp_name) + 1);
 67         }
 68 
 69         return size;
 70 }
 71 
 72 static int
 73 ethnl_phy_fill_reply(const struct ethnl_req_info *req_base, struct sk_buff *skb)
 74 {
 75         struct phy_req_info *req_info = PHY_REQINFO(req_base);
 76         struct phy_device_node *pdn = req_info->pdn;
 77         struct phy_device *phydev = pdn->phy;
 78         enum phy_upstream ptype;
 79 
 80         ptype = pdn->upstream_type;
 81 
 82         if (nla_put_u32(skb, ETHTOOL_A_PHY_INDEX, phydev->phyindex) ||
 83             nla_put_string(skb, ETHTOOL_A_PHY_NAME, dev_name(&phydev->mdio.dev)) ||
 84             nla_put_u32(skb, ETHTOOL_A_PHY_UPSTREAM_TYPE, ptype))
 85                 return -EMSGSIZE;
 86 
 87         if (phydev->drv &&
 88             nla_put_string(skb, ETHTOOL_A_PHY_DRVNAME, phydev->drv->name))
 89                 return -EMSGSIZE;
 90 
 91         if (ptype == PHY_UPSTREAM_PHY) {
 92                 struct phy_device *upstream = pdn->upstream.phydev;
 93                 const char *sfp_upstream_name;
 94 
 95                 /* Parent index */
 96                 if (nla_put_u32(skb, ETHTOOL_A_PHY_UPSTREAM_INDEX, upstream->phyindex))
 97                         return -EMSGSIZE;
 98 
 99                 if (pdn->parent_sfp_bus) {
100                         sfp_upstream_name = sfp_get_name(pdn->parent_sfp_bus);
101                         if (sfp_upstream_name &&
102                             nla_put_string(skb, ETHTOOL_A_PHY_UPSTREAM_SFP_NAME,
103                                            sfp_upstream_name))
104                                 return -EMSGSIZE;
105                 }
106         }
107 
108         if (phydev->sfp_bus) {
109                 const char *sfp_name = sfp_get_name(phydev->sfp_bus);
110 
111                 if (sfp_name &&
112                     nla_put_string(skb, ETHTOOL_A_PHY_DOWNSTREAM_SFP_NAME,
113                                    sfp_name))
114                         return -EMSGSIZE;
115         }
116 
117         return 0;
118 }
119 
120 static int ethnl_phy_parse_request(struct ethnl_req_info *req_base,
121                                    struct nlattr **tb,
122                                    struct netlink_ext_ack *extack)
123 {
124         struct phy_link_topology *topo = req_base->dev->link_topo;
125         struct phy_req_info *req_info = PHY_REQINFO(req_base);
126         struct phy_device *phydev;
127 
128         phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PHY_HEADER],
129                                       extack);
130         if (!phydev)
131                 return 0;
132 
133         if (IS_ERR(phydev))
134                 return PTR_ERR(phydev);
135 
136         if (!topo)
137                 return 0;
138 
139         req_info->pdn = xa_load(&topo->phys, phydev->phyindex);
140 
141         return 0;
142 }
143 
144 int ethnl_phy_doit(struct sk_buff *skb, struct genl_info *info)
145 {
146         struct phy_req_info req_info = {};
147         struct nlattr **tb = info->attrs;
148         struct sk_buff *rskb;
149         void *reply_payload;
150         int reply_len;
151         int ret;
152 
153         ret = ethnl_parse_header_dev_get(&req_info.base,
154                                          tb[ETHTOOL_A_PHY_HEADER],
155                                          genl_info_net(info), info->extack,
156                                          true);
157         if (ret < 0)
158                 return ret;
159 
160         rtnl_lock();
161 
162         ret = ethnl_phy_parse_request(&req_info.base, tb, info->extack);
163         if (ret < 0)
164                 goto err_unlock_rtnl;
165 
166         /* No PHY, return early */
167         if (!req_info.pdn)
168                 goto err_unlock_rtnl;
169 
170         ret = ethnl_phy_reply_size(&req_info.base, info->extack);
171         if (ret < 0)
172                 goto err_unlock_rtnl;
173         reply_len = ret + ethnl_reply_header_size();
174 
175         rskb = ethnl_reply_init(reply_len, req_info.base.dev,
176                                 ETHTOOL_MSG_PHY_GET_REPLY,
177                                 ETHTOOL_A_PHY_HEADER,
178                                 info, &reply_payload);
179         if (!rskb) {
180                 ret = -ENOMEM;
181                 goto err_unlock_rtnl;
182         }
183 
184         ret = ethnl_phy_fill_reply(&req_info.base, rskb);
185         if (ret)
186                 goto err_free_msg;
187 
188         rtnl_unlock();
189         ethnl_parse_header_dev_put(&req_info.base);
190         genlmsg_end(rskb, reply_payload);
191 
192         return genlmsg_reply(rskb, info);
193 
194 err_free_msg:
195         nlmsg_free(rskb);
196 err_unlock_rtnl:
197         rtnl_unlock();
198         ethnl_parse_header_dev_put(&req_info.base);
199         return ret;
200 }
201 
202 struct ethnl_phy_dump_ctx {
203         struct phy_req_info     *phy_req_info;
204         unsigned long ifindex;
205         unsigned long phy_index;
206 };
207 
208 int ethnl_phy_start(struct netlink_callback *cb)
209 {
210         const struct genl_info *info = genl_info_dump(cb);
211         struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
212         int ret;
213 
214         BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));
215 
216         ctx->phy_req_info = kzalloc(sizeof(*ctx->phy_req_info), GFP_KERNEL);
217         if (!ctx->phy_req_info)
218                 return -ENOMEM;
219 
220         ret = ethnl_parse_header_dev_get(&ctx->phy_req_info->base,
221                                          info->attrs[ETHTOOL_A_PHY_HEADER],
222                                          sock_net(cb->skb->sk), cb->extack,
223                                          false);
224         ctx->ifindex = 0;
225         ctx->phy_index = 0;
226 
227         if (ret)
228                 kfree(ctx->phy_req_info);
229 
230         return ret;
231 }
232 
233 int ethnl_phy_done(struct netlink_callback *cb)
234 {
235         struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
236 
237         if (ctx->phy_req_info->base.dev)
238                 ethnl_parse_header_dev_put(&ctx->phy_req_info->base);
239 
240         kfree(ctx->phy_req_info);
241 
242         return 0;
243 }
244 
245 static int ethnl_phy_dump_one_dev(struct sk_buff *skb, struct net_device *dev,
246                                   struct netlink_callback *cb)
247 {
248         struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
249         struct phy_req_info *pri = ctx->phy_req_info;
250         struct phy_device_node *pdn;
251         int ret = 0;
252         void *ehdr;
253 
254         if (!dev->link_topo)
255                 return 0;
256 
257         xa_for_each_start(&dev->link_topo->phys, ctx->phy_index, pdn, ctx->phy_index) {
258                 ehdr = ethnl_dump_put(skb, cb, ETHTOOL_MSG_PHY_GET_REPLY);
259                 if (!ehdr) {
260                         ret = -EMSGSIZE;
261                         break;
262                 }
263 
264                 ret = ethnl_fill_reply_header(skb, dev, ETHTOOL_A_PHY_HEADER);
265                 if (ret < 0) {
266                         genlmsg_cancel(skb, ehdr);
267                         break;
268                 }
269 
270                 pri->pdn = pdn;
271                 ret = ethnl_phy_fill_reply(&pri->base, skb);
272                 if (ret < 0) {
273                         genlmsg_cancel(skb, ehdr);
274                         break;
275                 }
276 
277                 genlmsg_end(skb, ehdr);
278         }
279 
280         return ret;
281 }
282 
283 int ethnl_phy_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
284 {
285         struct ethnl_phy_dump_ctx *ctx = (void *)cb->ctx;
286         struct net *net = sock_net(skb->sk);
287         struct net_device *dev;
288         int ret = 0;
289 
290         rtnl_lock();
291 
292         if (ctx->phy_req_info->base.dev) {
293                 ret = ethnl_phy_dump_one_dev(skb, ctx->phy_req_info->base.dev, cb);
294         } else {
295                 for_each_netdev_dump(net, dev, ctx->ifindex) {
296                         ret = ethnl_phy_dump_one_dev(skb, dev, cb);
297                         if (ret)
298                                 break;
299 
300                         ctx->phy_index = 0;
301                 }
302         }
303         rtnl_unlock();
304 
305         return ret;
306 }
307 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php