Blame view
net/ethtool/netlink.c
26.3 KB
2b4a8990b ethtool: introduc... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
041b1c5d4 ethtool: helper f... |
2 |
#include <net/sock.h> |
2b4a8990b ethtool: introduc... |
3 4 |
#include <linux/ethtool_netlink.h> #include "netlink.h" |
041b1c5d4 ethtool: helper f... |
5 |
static struct genl_family ethtool_genl_family; |
6b08d6c14 ethtool: support ... |
6 |
static bool ethnl_ok __read_mostly; |
5cf2a548b ethtool: add defa... |
7 |
static u32 ethnl_bcast_seq; |
6b08d6c14 ethtool: support ... |
8 |
|
a0de1cd35 ethtool: specify ... |
9 10 11 |
#define ETHTOOL_FLAGS_BASIC (ETHTOOL_FLAG_COMPACT_BITSETS | \ ETHTOOL_FLAG_OMIT_REPLY) #define ETHTOOL_FLAGS_STATS (ETHTOOL_FLAGS_BASIC | ETHTOOL_FLAG_STATS) |
329d9c333 ethtool: link up ... |
12 |
const struct nla_policy ethnl_header_policy[] = { |
041b1c5d4 ethtool: helper f... |
13 14 15 |
[ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 }, [ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING, .len = ALTIFNAMSIZ - 1 }, |
a0de1cd35 ethtool: specify ... |
16 17 18 19 20 21 22 23 24 25 |
[ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32, ETHTOOL_FLAGS_BASIC), }; const struct nla_policy ethnl_header_policy_stats[] = { [ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 }, [ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING, .len = ALTIFNAMSIZ - 1 }, [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32, ETHTOOL_FLAGS_STATS), |
041b1c5d4 ethtool: helper f... |
26 27 28 |
}; /** |
98130546d ethtool: rename e... |
29 |
* ethnl_parse_header_dev_get() - parse request header |
041b1c5d4 ethtool: helper f... |
30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
* @req_info: structure to put results into * @header: nest attribute with request header * @net: request netns * @extack: netlink extack for error reporting * @require_dev: fail if no device identified in header * * Parse request header in nested attribute @nest and puts results into * the structure pointed to by @req_info. Extack from @info is used for error * reporting. If req_info->dev is not null on return, reference to it has * been taken. If error is returned, *req_info is null initialized and no * reference is held. * * Return: 0 on success or negative error code */ |
98130546d ethtool: rename e... |
44 45 46 |
int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info, const struct nlattr *header, struct net *net, struct netlink_ext_ack *extack, bool require_dev) |
041b1c5d4 ethtool: helper f... |
47 |
{ |
ff419afa4 ethtool: trim pol... |
48 |
struct nlattr *tb[ARRAY_SIZE(ethnl_header_policy)]; |
041b1c5d4 ethtool: helper f... |
49 50 |
const struct nlattr *devname_attr; struct net_device *dev = NULL; |
2363d73a2 ethtool: reject u... |
51 |
u32 flags = 0; |
041b1c5d4 ethtool: helper f... |
52 53 54 55 56 57 |
int ret; if (!header) { NL_SET_ERR_MSG(extack, "request header missing"); return -EINVAL; } |
a0de1cd35 ethtool: specify ... |
58 59 60 |
/* No validation here, command policy should have a nested policy set * for the header, therefore validation should have already been done. */ |
ff419afa4 ethtool: trim pol... |
61 |
ret = nla_parse_nested(tb, ARRAY_SIZE(ethnl_header_policy) - 1, header, |
a0de1cd35 ethtool: specify ... |
62 |
NULL, extack); |
041b1c5d4 ethtool: helper f... |
63 64 |
if (ret < 0) return ret; |
a0de1cd35 ethtool: specify ... |
65 |
if (tb[ETHTOOL_A_HEADER_FLAGS]) |
2363d73a2 ethtool: reject u... |
66 |
flags = nla_get_u32(tb[ETHTOOL_A_HEADER_FLAGS]); |
041b1c5d4 ethtool: helper f... |
67 |
|
2363d73a2 ethtool: reject u... |
68 |
devname_attr = tb[ETHTOOL_A_HEADER_DEV_NAME]; |
041b1c5d4 ethtool: helper f... |
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
if (tb[ETHTOOL_A_HEADER_DEV_INDEX]) { u32 ifindex = nla_get_u32(tb[ETHTOOL_A_HEADER_DEV_INDEX]); dev = dev_get_by_index(net, ifindex); if (!dev) { NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_HEADER_DEV_INDEX], "no device matches ifindex"); return -ENODEV; } /* if both ifindex and ifname are passed, they must match */ if (devname_attr && strncmp(dev->name, nla_data(devname_attr), IFNAMSIZ)) { dev_put(dev); NL_SET_ERR_MSG_ATTR(extack, header, "ifindex and name do not match"); return -ENODEV; } } else if (devname_attr) { dev = dev_get_by_name(net, nla_data(devname_attr)); if (!dev) { NL_SET_ERR_MSG_ATTR(extack, devname_attr, "no device matches name"); return -ENODEV; } } else if (require_dev) { NL_SET_ERR_MSG_ATTR(extack, header, "neither ifindex nor name specified"); return -EINVAL; } if (dev && !netif_device_present(dev)) { dev_put(dev); NL_SET_ERR_MSG(extack, "device not present"); return -ENODEV; } req_info->dev = dev; |
2363d73a2 ethtool: reject u... |
107 |
req_info->flags = flags; |
041b1c5d4 ethtool: helper f... |
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
return 0; } /** * ethnl_fill_reply_header() - Put common header into a reply message * @skb: skb with the message * @dev: network device to describe in header * @attrtype: attribute type to use for the nest * * Create a nested attribute with attributes describing given network device. * * Return: 0 on success, error value (-EMSGSIZE only) on error */ int ethnl_fill_reply_header(struct sk_buff *skb, struct net_device *dev, u16 attrtype) { struct nlattr *nest; if (!dev) return 0; nest = nla_nest_start(skb, attrtype); if (!nest) return -EMSGSIZE; if (nla_put_u32(skb, ETHTOOL_A_HEADER_DEV_INDEX, (u32)dev->ifindex) || nla_put_string(skb, ETHTOOL_A_HEADER_DEV_NAME, dev->name)) goto nla_put_failure; /* If more attributes are put into reply header, ethnl_header_size() * must be updated to account for them. */ nla_nest_end(skb, nest); return 0; nla_put_failure: nla_nest_cancel(skb, nest); return -EMSGSIZE; } /** * ethnl_reply_init() - Create skb for a reply and fill device identification |
d2c4b444f ethtool: fix kern... |
149 150 151 152 153 154 |
* @payload: payload length (without netlink and genetlink header) * @dev: device the reply is about (may be null) * @cmd: ETHTOOL_MSG_* message type for reply * @hdr_attrtype: attribute type for common header * @info: genetlink info of the received packet we respond to * @ehdrp: place to store payload pointer returned by genlmsg_new() |
041b1c5d4 ethtool: helper f... |
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
* * Return: pointer to allocated skb on success, NULL on error */ struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev, u8 cmd, u16 hdr_attrtype, struct genl_info *info, void **ehdrp) { struct sk_buff *skb; skb = genlmsg_new(payload, GFP_KERNEL); if (!skb) goto err; *ehdrp = genlmsg_put_reply(skb, info, ðtool_genl_family, 0, cmd); if (!*ehdrp) goto err_free; if (dev) { int ret; ret = ethnl_fill_reply_header(skb, dev, hdr_attrtype); if (ret < 0) goto err_free; } return skb; err_free: nlmsg_free(skb); err: if (info) GENL_SET_ERR_MSG(info, "failed to setup reply message"); return NULL; } |
c7d759eb7 ethtool: add tunn... |
187 188 189 190 191 |
void *ethnl_dump_put(struct sk_buff *skb, struct netlink_callback *cb, u8 cmd) { return genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, ðtool_genl_family, 0, cmd); } |
0df960f14 net: ethtool: Mak... |
192 |
void *ethnl_bcastmsg_put(struct sk_buff *skb, u8 cmd) |
5cf2a548b ethtool: add defa... |
193 194 195 196 |
{ return genlmsg_put(skb, 0, ++ethnl_bcast_seq, ðtool_genl_family, 0, cmd); } |
0df960f14 net: ethtool: Mak... |
197 |
int ethnl_multicast(struct sk_buff *skb, struct net_device *dev) |
5cf2a548b ethtool: add defa... |
198 199 200 201 |
{ return genlmsg_multicast_netns(ðtool_genl_family, dev_net(dev), skb, 0, ETHNL_MCGRP_MONITOR, GFP_KERNEL); } |
728480f12 ethtool: default ... |
202 203 204 205 |
/* GET request helpers */ /** * struct ethnl_dump_ctx - context structure for generic dumpit() callback |
d2c4b444f ethtool: fix kern... |
206 207 208 209 210 |
* @ops: request ops of currently processed message type * @req_info: parsed request header of processed request * @reply_data: data needed to compose the reply * @pos_hash: saved iteration position - hashbucket * @pos_idx: saved iteration position - index |
728480f12 ethtool: default ... |
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
* * These parameters are kept in struct netlink_callback as context preserved * between iterations. They are initialized by ethnl_default_start() and used * in ethnl_default_dumpit() and ethnl_default_done(). */ struct ethnl_dump_ctx { const struct ethnl_request_ops *ops; struct ethnl_req_info *req_info; struct ethnl_reply_data *reply_data; int pos_hash; int pos_idx; }; static const struct ethnl_request_ops * ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { |
71921690f ethtool: provide ... |
226 |
[ETHTOOL_MSG_STRSET_GET] = ðnl_strset_request_ops, |
459e0b81b ethtool: provide ... |
227 |
[ETHTOOL_MSG_LINKINFO_GET] = ðnl_linkinfo_request_ops, |
f625aa9be ethtool: provide ... |
228 |
[ETHTOOL_MSG_LINKMODES_GET] = ðnl_linkmodes_request_ops, |
3d2b847fb ethtool: provide ... |
229 |
[ETHTOOL_MSG_LINKSTATE_GET] = ðnl_linkstate_request_ops, |
6a94b8ccf ethtool: provide ... |
230 |
[ETHTOOL_MSG_DEBUG_GET] = ðnl_debug_request_ops, |
51ea22b04 ethtool: provide ... |
231 |
[ETHTOOL_MSG_WOL_GET] = ðnl_wol_request_ops, |
0524399d4 ethtool: provide ... |
232 |
[ETHTOOL_MSG_FEATURES_GET] = ðnl_features_request_ops, |
e16c3386f ethtool: provide ... |
233 |
[ETHTOOL_MSG_PRIVFLAGS_GET] = ðnl_privflags_request_ops, |
e4a1717b6 ethtool: provide ... |
234 |
[ETHTOOL_MSG_RINGS_GET] = ðnl_rings_request_ops, |
0c84979c9 ethtool: provide ... |
235 |
[ETHTOOL_MSG_CHANNELS_GET] = ðnl_channels_request_ops, |
217275453 ethtool: provide ... |
236 |
[ETHTOOL_MSG_COALESCE_GET] = ðnl_coalesce_request_ops, |
7f59fb32b ethtool: provide ... |
237 |
[ETHTOOL_MSG_PAUSE_GET] = ðnl_pause_request_ops, |
b7eeefe72 ethtool: provide ... |
238 |
[ETHTOOL_MSG_EEE_GET] = ðnl_eee_request_ops, |
5b071c59e ethtool: provide ... |
239 |
[ETHTOOL_MSG_TSINFO_GET] = ðnl_tsinfo_request_ops, |
728480f12 ethtool: default ... |
240 241 242 243 244 245 246 247 248 249 |
}; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) { return (struct ethnl_dump_ctx *)cb->ctx; } /** * ethnl_default_parse() - Parse request message * @req_info: pointer to structure to put data into |
4f30974fe ethtool: wire up ... |
250 |
* @tb: parsed attributes |
728480f12 ethtool: default ... |
251 252 253 254 255 256 257 258 259 260 261 |
* @net: request netns * @request_ops: struct request_ops for request type * @extack: netlink extack for error reporting * @require_dev: fail if no device identified in header * * Parse universal request header and call request specific ->parse_request() * callback (if defined) to parse the rest of the message. * * Return: 0 on success or negative error code */ static int ethnl_default_parse(struct ethnl_req_info *req_info, |
4f30974fe ethtool: wire up ... |
262 |
struct nlattr **tb, struct net *net, |
728480f12 ethtool: default ... |
263 264 265 |
const struct ethnl_request_ops *request_ops, struct netlink_ext_ack *extack, bool require_dev) { |
728480f12 ethtool: default ... |
266 |
int ret; |
98130546d ethtool: rename e... |
267 268 |
ret = ethnl_parse_header_dev_get(req_info, tb[request_ops->hdr_attr], net, extack, require_dev); |
728480f12 ethtool: default ... |
269 |
if (ret < 0) |
4f30974fe ethtool: wire up ... |
270 |
return ret; |
728480f12 ethtool: default ... |
271 272 273 274 |
if (request_ops->parse_request) { ret = request_ops->parse_request(req_info, tb, extack); if (ret < 0) |
4f30974fe ethtool: wire up ... |
275 |
return ret; |
728480f12 ethtool: default ... |
276 |
} |
4f30974fe ethtool: wire up ... |
277 |
return 0; |
728480f12 ethtool: default ... |
278 279 280 281 |
} /** * ethnl_init_reply_data() - Initialize reply data for GET request |
d2c4b444f ethtool: fix kern... |
282 283 284 |
* @reply_data: pointer to embedded struct ethnl_reply_data * @ops: instance of struct ethnl_request_ops describing the layout * @dev: network device to initialize the reply for |
728480f12 ethtool: default ... |
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
* * Fills the reply data part with zeros and sets the dev member. Must be called * before calling the ->fill_reply() callback (for each iteration when handling * dump requests). */ static void ethnl_init_reply_data(struct ethnl_reply_data *reply_data, const struct ethnl_request_ops *ops, struct net_device *dev) { memset(reply_data, 0, ops->reply_data_size); reply_data->dev = dev; } /* default ->doit() handler for GET type requests */ static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info) { struct ethnl_reply_data *reply_data = NULL; struct ethnl_req_info *req_info = NULL; const u8 cmd = info->genlhdr->cmd; const struct ethnl_request_ops *ops; struct sk_buff *rskb; void *reply_payload; int reply_len; int ret; ops = ethnl_default_requests[cmd]; if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops ", cmd)) return -EOPNOTSUPP; req_info = kzalloc(ops->req_info_size, GFP_KERNEL); if (!req_info) return -ENOMEM; reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL); if (!reply_data) { kfree(req_info); return -ENOMEM; } |
4f30974fe ethtool: wire up ... |
322 323 |
ret = ethnl_default_parse(req_info, info->attrs, genl_info_net(info), ops, info->extack, !ops->allow_nodev_do); |
728480f12 ethtool: default ... |
324 325 326 327 328 329 330 331 332 |
if (ret < 0) goto err_dev; ethnl_init_reply_data(reply_data, ops, req_info->dev); rtnl_lock(); ret = ops->prepare_data(req_info, reply_data, info); rtnl_unlock(); if (ret < 0) goto err_cleanup; |
d97772dbd ethtool: fix ->re... |
333 |
ret = ops->reply_size(req_info, reply_data); |
728480f12 ethtool: default ... |
334 335 |
if (ret < 0) goto err_cleanup; |
7c87e32d2 ethtool: count he... |
336 |
reply_len = ret + ethnl_reply_header_size(); |
728480f12 ethtool: default ... |
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
ret = -ENOMEM; rskb = ethnl_reply_init(reply_len, req_info->dev, ops->reply_cmd, ops->hdr_attr, info, &reply_payload); if (!rskb) goto err_cleanup; ret = ops->fill_reply(rskb, req_info, reply_data); if (ret < 0) goto err_msg; if (ops->cleanup_data) ops->cleanup_data(reply_data); genlmsg_end(rskb, reply_payload); if (req_info->dev) dev_put(req_info->dev); kfree(reply_data); kfree(req_info); return genlmsg_reply(rskb, info); err_msg: WARN_ONCE(ret == -EMSGSIZE, "calculated message payload length (%d) not sufficient ", reply_len); nlmsg_free(rskb); err_cleanup: if (ops->cleanup_data) ops->cleanup_data(reply_data); err_dev: if (req_info->dev) dev_put(req_info->dev); kfree(reply_data); kfree(req_info); return ret; } static int ethnl_default_dump_one(struct sk_buff *skb, struct net_device *dev, |
365f9ae4e ethtool: fix genl... |
371 372 |
const struct ethnl_dump_ctx *ctx, struct netlink_callback *cb) |
728480f12 ethtool: default ... |
373 |
{ |
365f9ae4e ethtool: fix genl... |
374 |
void *ehdr; |
728480f12 ethtool: default ... |
375 |
int ret; |
365f9ae4e ethtool: fix genl... |
376 377 378 379 |
ehdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, ðtool_genl_family, 0, ctx->ops->reply_cmd); if (!ehdr) return -EMSGSIZE; |
728480f12 ethtool: default ... |
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 |
ethnl_init_reply_data(ctx->reply_data, ctx->ops, dev); rtnl_lock(); ret = ctx->ops->prepare_data(ctx->req_info, ctx->reply_data, NULL); rtnl_unlock(); if (ret < 0) goto out; ret = ethnl_fill_reply_header(skb, dev, ctx->ops->hdr_attr); if (ret < 0) goto out; ret = ctx->ops->fill_reply(skb, ctx->req_info, ctx->reply_data); out: if (ctx->ops->cleanup_data) ctx->ops->cleanup_data(ctx->reply_data); ctx->reply_data->dev = NULL; |
365f9ae4e ethtool: fix genl... |
395 396 397 398 |
if (ret < 0) genlmsg_cancel(skb, ehdr); else genlmsg_end(skb, ehdr); |
728480f12 ethtool: default ... |
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
return ret; } /* Default ->dumpit() handler for GET requests. Device iteration copied from * rtnl_dump_ifinfo(); we have to be more careful about device hashtable * persistence as we cannot guarantee to hold RTNL lock through the whole * function as rtnetnlink does. */ static int ethnl_default_dumpit(struct sk_buff *skb, struct netlink_callback *cb) { struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb); struct net *net = sock_net(skb->sk); int s_idx = ctx->pos_idx; int h, idx = 0; int ret = 0; |
728480f12 ethtool: default ... |
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
rtnl_lock(); for (h = ctx->pos_hash; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { struct hlist_head *head; struct net_device *dev; unsigned int seq; head = &net->dev_index_head[h]; restart_chain: seq = net->dev_base_seq; cb->seq = seq; idx = 0; hlist_for_each_entry(dev, head, index_hlist) { if (idx < s_idx) goto cont; dev_hold(dev); rtnl_unlock(); |
365f9ae4e ethtool: fix genl... |
433 |
ret = ethnl_default_dump_one(skb, dev, ctx, cb); |
728480f12 ethtool: default ... |
434 435 |
dev_put(dev); if (ret < 0) { |
728480f12 ethtool: default ... |
436 437 438 439 440 441 |
if (ret == -EOPNOTSUPP) goto lock_and_cont; if (likely(skb->len)) ret = skb->len; goto out; } |
728480f12 ethtool: default ... |
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
lock_and_cont: rtnl_lock(); if (net->dev_base_seq != seq) { s_idx = idx + 1; goto restart_chain; } cont: idx++; } } rtnl_unlock(); out: ctx->pos_hash = h; ctx->pos_idx = idx; nl_dump_check_consistent(cb, nlmsg_hdr(skb)); return ret; } /* generic ->start() handler for GET requests */ static int ethnl_default_start(struct netlink_callback *cb) { |
4f30974fe ethtool: wire up ... |
466 |
const struct genl_dumpit_info *info = genl_dumpit_info(cb); |
728480f12 ethtool: default ... |
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb); struct ethnl_reply_data *reply_data; const struct ethnl_request_ops *ops; struct ethnl_req_info *req_info; struct genlmsghdr *ghdr; int ret; BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); ghdr = nlmsg_data(cb->nlh); ops = ethnl_default_requests[ghdr->cmd]; if (WARN_ONCE(!ops, "cmd %u has no ethnl_request_ops ", ghdr->cmd)) return -EOPNOTSUPP; req_info = kzalloc(ops->req_info_size, GFP_KERNEL); if (!req_info) return -ENOMEM; reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL); if (!reply_data) { |
a6dd04807 ethtool: fix a me... |
486 487 |
ret = -ENOMEM; goto free_req_info; |
728480f12 ethtool: default ... |
488 |
} |
4f30974fe ethtool: wire up ... |
489 490 |
ret = ethnl_default_parse(req_info, info->attrs, sock_net(cb->skb->sk), ops, cb->extack, false); |
728480f12 ethtool: default ... |
491 492 493 494 495 496 497 498 499 |
if (req_info->dev) { /* We ignore device specification in dump requests but as the * same parser as for non-dump (doit) requests is used, it * would take reference to the device if it finds one */ dev_put(req_info->dev); req_info->dev = NULL; } if (ret < 0) |
a6dd04807 ethtool: fix a me... |
500 |
goto free_reply_data; |
728480f12 ethtool: default ... |
501 502 503 504 505 506 507 508 |
ctx->ops = ops; ctx->req_info = req_info; ctx->reply_data = reply_data; ctx->pos_hash = 0; ctx->pos_idx = 0; return 0; |
a6dd04807 ethtool: fix a me... |
509 510 511 512 513 514 515 |
free_reply_data: kfree(reply_data); free_req_info: kfree(req_info); return ret; |
728480f12 ethtool: default ... |
516 517 518 519 520 521 522 523 524 525 526 527 |
} /* default ->done() handler for GET requests */ static int ethnl_default_done(struct netlink_callback *cb) { struct ethnl_dump_ctx *ctx = ethnl_dump_context(cb); kfree(ctx->reply_data); kfree(ctx->req_info); return 0; } |
5cf2a548b ethtool: add defa... |
528 529 |
static const struct ethnl_request_ops * ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = { |
73286734c ethtool: add LINK... |
530 |
[ETHTOOL_MSG_LINKINFO_NTF] = ðnl_linkinfo_request_ops, |
1b1b1847c ethtool: add LINK... |
531 |
[ETHTOOL_MSG_LINKMODES_NTF] = ðnl_linkmodes_request_ops, |
0bda7af39 ethtool: add DEBU... |
532 |
[ETHTOOL_MSG_DEBUG_NTF] = ðnl_debug_request_ops, |
67bffa792 ethtool: add WOL_... |
533 |
[ETHTOOL_MSG_WOL_NTF] = ðnl_wol_request_ops, |
9c6451ef4 ethtool: add FEAT... |
534 |
[ETHTOOL_MSG_FEATURES_NTF] = ðnl_features_request_ops, |
111dcba3c ethtool: add PRIV... |
535 |
[ETHTOOL_MSG_PRIVFLAGS_NTF] = ðnl_privflags_request_ops, |
bc9d1c995 ethtool: add RING... |
536 |
[ETHTOOL_MSG_RINGS_NTF] = ðnl_rings_request_ops, |
546379b9a ethtool: add CHAN... |
537 |
[ETHTOOL_MSG_CHANNELS_NTF] = ðnl_channels_request_ops, |
0cf3eac8c ethtool: add COAL... |
538 |
[ETHTOOL_MSG_COALESCE_NTF] = ðnl_coalesce_request_ops, |
bf37faa38 ethtool: add PAUS... |
539 |
[ETHTOOL_MSG_PAUSE_NTF] = ðnl_pause_request_ops, |
6c5bc8fe4 ethtool: add EEE_... |
540 |
[ETHTOOL_MSG_EEE_NTF] = ðnl_eee_request_ops, |
5cf2a548b ethtool: add defa... |
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
}; /* default notification handler */ static void ethnl_default_notify(struct net_device *dev, unsigned int cmd, const void *data) { struct ethnl_reply_data *reply_data; const struct ethnl_request_ops *ops; struct ethnl_req_info *req_info; struct sk_buff *skb; void *reply_payload; int reply_len; int ret; if (WARN_ONCE(cmd > ETHTOOL_MSG_KERNEL_MAX || !ethnl_default_notify_ops[cmd], "unexpected notification type %u ", cmd)) return; ops = ethnl_default_notify_ops[cmd]; req_info = kzalloc(ops->req_info_size, GFP_KERNEL); if (!req_info) return; reply_data = kmalloc(ops->reply_data_size, GFP_KERNEL); if (!reply_data) { kfree(req_info); return; } req_info->dev = dev; req_info->flags |= ETHTOOL_FLAG_COMPACT_BITSETS; ethnl_init_reply_data(reply_data, ops, dev); ret = ops->prepare_data(req_info, reply_data, NULL); if (ret < 0) goto err_cleanup; |
d97772dbd ethtool: fix ->re... |
577 |
ret = ops->reply_size(req_info, reply_data); |
5cf2a548b ethtool: add defa... |
578 579 |
if (ret < 0) goto err_cleanup; |
7c87e32d2 ethtool: count he... |
580 |
reply_len = ret + ethnl_reply_header_size(); |
5cf2a548b ethtool: add defa... |
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
ret = -ENOMEM; skb = genlmsg_new(reply_len, GFP_KERNEL); if (!skb) goto err_cleanup; reply_payload = ethnl_bcastmsg_put(skb, cmd); if (!reply_payload) goto err_skb; ret = ethnl_fill_reply_header(skb, dev, ops->hdr_attr); if (ret < 0) goto err_msg; ret = ops->fill_reply(skb, req_info, reply_data); if (ret < 0) goto err_msg; if (ops->cleanup_data) ops->cleanup_data(reply_data); genlmsg_end(skb, reply_payload); kfree(reply_data); kfree(req_info); ethnl_multicast(skb, dev); return; err_msg: WARN_ONCE(ret == -EMSGSIZE, "calculated message payload length (%d) not sufficient ", reply_len); err_skb: nlmsg_free(skb); err_cleanup: if (ops->cleanup_data) ops->cleanup_data(reply_data); kfree(reply_data); kfree(req_info); return; } |
6b08d6c14 ethtool: support ... |
617 618 619 620 621 622 |
/* notifications */ typedef void (*ethnl_notify_handler_t)(struct net_device *dev, unsigned int cmd, const void *data); static const ethnl_notify_handler_t ethnl_notify_handlers[] = { |
73286734c ethtool: add LINK... |
623 |
[ETHTOOL_MSG_LINKINFO_NTF] = ethnl_default_notify, |
1b1b1847c ethtool: add LINK... |
624 |
[ETHTOOL_MSG_LINKMODES_NTF] = ethnl_default_notify, |
0bda7af39 ethtool: add DEBU... |
625 |
[ETHTOOL_MSG_DEBUG_NTF] = ethnl_default_notify, |
67bffa792 ethtool: add WOL_... |
626 |
[ETHTOOL_MSG_WOL_NTF] = ethnl_default_notify, |
9c6451ef4 ethtool: add FEAT... |
627 |
[ETHTOOL_MSG_FEATURES_NTF] = ethnl_default_notify, |
111dcba3c ethtool: add PRIV... |
628 |
[ETHTOOL_MSG_PRIVFLAGS_NTF] = ethnl_default_notify, |
bc9d1c995 ethtool: add RING... |
629 |
[ETHTOOL_MSG_RINGS_NTF] = ethnl_default_notify, |
546379b9a ethtool: add CHAN... |
630 |
[ETHTOOL_MSG_CHANNELS_NTF] = ethnl_default_notify, |
0cf3eac8c ethtool: add COAL... |
631 |
[ETHTOOL_MSG_COALESCE_NTF] = ethnl_default_notify, |
bf37faa38 ethtool: add PAUS... |
632 |
[ETHTOOL_MSG_PAUSE_NTF] = ethnl_default_notify, |
6c5bc8fe4 ethtool: add EEE_... |
633 |
[ETHTOOL_MSG_EEE_NTF] = ethnl_default_notify, |
6b08d6c14 ethtool: support ... |
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 |
}; void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) { if (unlikely(!ethnl_ok)) return; ASSERT_RTNL(); if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) && ethnl_notify_handlers[cmd])) ethnl_notify_handlers[cmd](dev, cmd, data); else WARN_ONCE(1, "notification %u not implemented (dev=%s) ", cmd, netdev_name(dev)); } EXPORT_SYMBOL(ethtool_notify); |
9c6451ef4 ethtool: add FEAT... |
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 |
static void ethnl_notify_features(struct netdev_notifier_info *info) { struct net_device *dev = netdev_notifier_info_to_dev(info); ethtool_notify(dev, ETHTOOL_MSG_FEATURES_NTF, NULL); } static int ethnl_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { switch (event) { case NETDEV_FEAT_CHANGE: ethnl_notify_features(ptr); break; } return NOTIFY_DONE; } static struct notifier_block ethnl_netdev_notifier = { .notifier_call = ethnl_netdev_event, }; |
2b4a8990b ethtool: introduc... |
673 674 675 |
/* genetlink setup */ static const struct genl_ops ethtool_genl_ops[] = { |
71921690f ethtool: provide ... |
676 677 678 679 680 681 |
{ .cmd = ETHTOOL_MSG_STRSET_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
682 683 |
.policy = ethnl_strset_get_policy, .maxattr = ARRAY_SIZE(ethnl_strset_get_policy) - 1, |
71921690f ethtool: provide ... |
684 |
}, |
459e0b81b ethtool: provide ... |
685 686 687 688 689 690 |
{ .cmd = ETHTOOL_MSG_LINKINFO_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
691 692 |
.policy = ethnl_linkinfo_get_policy, .maxattr = ARRAY_SIZE(ethnl_linkinfo_get_policy) - 1, |
459e0b81b ethtool: provide ... |
693 |
}, |
a53f3d41e ethtool: set link... |
694 695 696 697 |
{ .cmd = ETHTOOL_MSG_LINKINFO_SET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_linkinfo, |
5028588b6 ethtool: wire up ... |
698 699 |
.policy = ethnl_linkinfo_set_policy, .maxattr = ARRAY_SIZE(ethnl_linkinfo_set_policy) - 1, |
a53f3d41e ethtool: set link... |
700 |
}, |
f625aa9be ethtool: provide ... |
701 702 703 704 705 706 |
{ .cmd = ETHTOOL_MSG_LINKMODES_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
707 708 |
.policy = ethnl_linkmodes_get_policy, .maxattr = ARRAY_SIZE(ethnl_linkmodes_get_policy) - 1, |
f625aa9be ethtool: provide ... |
709 |
}, |
bfbcfe203 ethtool: set link... |
710 711 712 713 |
{ .cmd = ETHTOOL_MSG_LINKMODES_SET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_linkmodes, |
5028588b6 ethtool: wire up ... |
714 715 |
.policy = ethnl_linkmodes_set_policy, .maxattr = ARRAY_SIZE(ethnl_linkmodes_set_policy) - 1, |
bfbcfe203 ethtool: set link... |
716 |
}, |
3d2b847fb ethtool: provide ... |
717 718 719 720 721 722 |
{ .cmd = ETHTOOL_MSG_LINKSTATE_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
723 724 |
.policy = ethnl_linkstate_get_policy, .maxattr = ARRAY_SIZE(ethnl_linkstate_get_policy) - 1, |
3d2b847fb ethtool: provide ... |
725 |
}, |
6a94b8ccf ethtool: provide ... |
726 727 728 729 730 731 |
{ .cmd = ETHTOOL_MSG_DEBUG_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
732 733 |
.policy = ethnl_debug_get_policy, .maxattr = ARRAY_SIZE(ethnl_debug_get_policy) - 1, |
6a94b8ccf ethtool: provide ... |
734 |
}, |
e54d04e3a ethtool: set mess... |
735 736 737 738 |
{ .cmd = ETHTOOL_MSG_DEBUG_SET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_debug, |
5028588b6 ethtool: wire up ... |
739 740 |
.policy = ethnl_debug_set_policy, .maxattr = ARRAY_SIZE(ethnl_debug_set_policy) - 1, |
e54d04e3a ethtool: set mess... |
741 |
}, |
51ea22b04 ethtool: provide ... |
742 743 744 745 746 747 748 |
{ .cmd = ETHTOOL_MSG_WOL_GET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
749 750 |
.policy = ethnl_wol_get_policy, .maxattr = ARRAY_SIZE(ethnl_wol_get_policy) - 1, |
51ea22b04 ethtool: provide ... |
751 |
}, |
8d425b19b ethtool: set wake... |
752 753 754 755 |
{ .cmd = ETHTOOL_MSG_WOL_SET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_wol, |
5028588b6 ethtool: wire up ... |
756 757 |
.policy = ethnl_wol_set_policy, .maxattr = ARRAY_SIZE(ethnl_wol_set_policy) - 1, |
8d425b19b ethtool: set wake... |
758 |
}, |
0524399d4 ethtool: provide ... |
759 760 761 762 763 764 |
{ .cmd = ETHTOOL_MSG_FEATURES_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
765 766 |
.policy = ethnl_features_get_policy, .maxattr = ARRAY_SIZE(ethnl_features_get_policy) - 1, |
0524399d4 ethtool: provide ... |
767 |
}, |
0980bfcd6 ethtool: set netd... |
768 769 770 771 |
{ .cmd = ETHTOOL_MSG_FEATURES_SET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_features, |
5028588b6 ethtool: wire up ... |
772 773 |
.policy = ethnl_features_set_policy, .maxattr = ARRAY_SIZE(ethnl_features_set_policy) - 1, |
0980bfcd6 ethtool: set netd... |
774 |
}, |
e16c3386f ethtool: provide ... |
775 776 777 778 779 780 |
{ .cmd = ETHTOOL_MSG_PRIVFLAGS_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
781 782 |
.policy = ethnl_privflags_get_policy, .maxattr = ARRAY_SIZE(ethnl_privflags_get_policy) - 1, |
e16c3386f ethtool: provide ... |
783 |
}, |
f265d7995 ethtool: set devi... |
784 785 786 787 |
{ .cmd = ETHTOOL_MSG_PRIVFLAGS_SET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_privflags, |
5028588b6 ethtool: wire up ... |
788 789 |
.policy = ethnl_privflags_set_policy, .maxattr = ARRAY_SIZE(ethnl_privflags_set_policy) - 1, |
f265d7995 ethtool: set devi... |
790 |
}, |
e4a1717b6 ethtool: provide ... |
791 792 793 794 795 796 |
{ .cmd = ETHTOOL_MSG_RINGS_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
797 798 |
.policy = ethnl_rings_get_policy, .maxattr = ARRAY_SIZE(ethnl_rings_get_policy) - 1, |
e4a1717b6 ethtool: provide ... |
799 |
}, |
2fc2929e8 ethtool: set devi... |
800 801 802 803 |
{ .cmd = ETHTOOL_MSG_RINGS_SET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_rings, |
5028588b6 ethtool: wire up ... |
804 805 |
.policy = ethnl_rings_set_policy, .maxattr = ARRAY_SIZE(ethnl_rings_set_policy) - 1, |
2fc2929e8 ethtool: set devi... |
806 |
}, |
0c84979c9 ethtool: provide ... |
807 808 809 810 811 812 |
{ .cmd = ETHTOOL_MSG_CHANNELS_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
813 814 |
.policy = ethnl_channels_get_policy, .maxattr = ARRAY_SIZE(ethnl_channels_get_policy) - 1, |
0c84979c9 ethtool: provide ... |
815 |
}, |
e19c591ea ethtool: set devi... |
816 817 818 819 |
{ .cmd = ETHTOOL_MSG_CHANNELS_SET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_channels, |
fd15dd058 ethtool: correct ... |
820 821 |
.policy = ethnl_channels_set_policy, .maxattr = ARRAY_SIZE(ethnl_channels_set_policy) - 1, |
e19c591ea ethtool: set devi... |
822 |
}, |
217275453 ethtool: provide ... |
823 824 825 826 827 828 |
{ .cmd = ETHTOOL_MSG_COALESCE_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
829 830 |
.policy = ethnl_coalesce_get_policy, .maxattr = ARRAY_SIZE(ethnl_coalesce_get_policy) - 1, |
217275453 ethtool: provide ... |
831 |
}, |
9881418c7 ethtool: set coal... |
832 833 834 835 |
{ .cmd = ETHTOOL_MSG_COALESCE_SET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_coalesce, |
5028588b6 ethtool: wire up ... |
836 837 |
.policy = ethnl_coalesce_set_policy, .maxattr = ARRAY_SIZE(ethnl_coalesce_set_policy) - 1, |
9881418c7 ethtool: set coal... |
838 |
}, |
7f59fb32b ethtool: provide ... |
839 840 841 842 843 844 |
{ .cmd = ETHTOOL_MSG_PAUSE_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
845 846 |
.policy = ethnl_pause_get_policy, .maxattr = ARRAY_SIZE(ethnl_pause_get_policy) - 1, |
7f59fb32b ethtool: provide ... |
847 |
}, |
3ab879933 ethtool: set paus... |
848 849 850 851 |
{ .cmd = ETHTOOL_MSG_PAUSE_SET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_pause, |
5028588b6 ethtool: wire up ... |
852 853 |
.policy = ethnl_pause_set_policy, .maxattr = ARRAY_SIZE(ethnl_pause_set_policy) - 1, |
3ab879933 ethtool: set paus... |
854 |
}, |
b7eeefe72 ethtool: provide ... |
855 856 857 858 859 860 |
{ .cmd = ETHTOOL_MSG_EEE_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
861 862 |
.policy = ethnl_eee_get_policy, .maxattr = ARRAY_SIZE(ethnl_eee_get_policy) - 1, |
b7eeefe72 ethtool: provide ... |
863 |
}, |
fd77be7bd ethtool: set EEE ... |
864 865 866 867 |
{ .cmd = ETHTOOL_MSG_EEE_SET, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_eee, |
5028588b6 ethtool: wire up ... |
868 869 |
.policy = ethnl_eee_set_policy, .maxattr = ARRAY_SIZE(ethnl_eee_set_policy) - 1, |
fd77be7bd ethtool: set EEE ... |
870 |
}, |
5b071c59e ethtool: provide ... |
871 872 873 874 875 876 |
{ .cmd = ETHTOOL_MSG_TSINFO_GET, .doit = ethnl_default_doit, .start = ethnl_default_start, .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, |
4f30974fe ethtool: wire up ... |
877 878 |
.policy = ethnl_tsinfo_get_policy, .maxattr = ARRAY_SIZE(ethnl_tsinfo_get_policy) - 1, |
5b071c59e ethtool: provide ... |
879 |
}, |
11ca3c426 net: ethtool: net... |
880 881 882 883 |
{ .cmd = ETHTOOL_MSG_CABLE_TEST_ACT, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_act_cable_test, |
5028588b6 ethtool: wire up ... |
884 885 |
.policy = ethnl_cable_test_act_policy, .maxattr = ARRAY_SIZE(ethnl_cable_test_act_policy) - 1, |
11ca3c426 net: ethtool: net... |
886 |
}, |
1a644de29 net: ethtool: Add... |
887 888 889 890 |
{ .cmd = ETHTOOL_MSG_CABLE_TEST_TDR_ACT, .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_act_cable_test_tdr, |
5028588b6 ethtool: wire up ... |
891 892 |
.policy = ethnl_cable_test_tdr_act_policy, .maxattr = ARRAY_SIZE(ethnl_cable_test_tdr_act_policy) - 1, |
1a644de29 net: ethtool: Add... |
893 |
}, |
c7d759eb7 ethtool: add tunn... |
894 895 896 897 898 |
{ .cmd = ETHTOOL_MSG_TUNNEL_INFO_GET, .doit = ethnl_tunnel_info_doit, .start = ethnl_tunnel_info_start, .dumpit = ethnl_tunnel_info_dumpit, |
4f30974fe ethtool: wire up ... |
899 900 |
.policy = ethnl_tunnel_info_get_policy, .maxattr = ARRAY_SIZE(ethnl_tunnel_info_get_policy) - 1, |
c7d759eb7 ethtool: add tunn... |
901 |
}, |
2b4a8990b ethtool: introduc... |
902 |
}; |
6b08d6c14 ethtool: support ... |
903 904 905 |
static const struct genl_multicast_group ethtool_nl_mcgrps[] = { [ETHNL_MCGRP_MONITOR] = { .name = ETHTOOL_MCGRP_MONITOR_NAME }, }; |
78b70155d ethtool: mark net... |
906 |
static struct genl_family ethtool_genl_family __ro_after_init = { |
2b4a8990b ethtool: introduc... |
907 908 909 910 911 912 |
.name = ETHTOOL_GENL_NAME, .version = ETHTOOL_GENL_VERSION, .netnsok = true, .parallel_ops = true, .ops = ethtool_genl_ops, .n_ops = ARRAY_SIZE(ethtool_genl_ops), |
6b08d6c14 ethtool: support ... |
913 914 |
.mcgrps = ethtool_nl_mcgrps, .n_mcgrps = ARRAY_SIZE(ethtool_nl_mcgrps), |
2b4a8990b ethtool: introduc... |
915 916 917 918 919 920 921 922 923 924 925 |
}; /* module setup */ static int __init ethnl_init(void) { int ret; ret = genl_register_family(ðtool_genl_family); if (WARN(ret < 0, "ethtool: genetlink family registration failed")) return ret; |
6b08d6c14 ethtool: support ... |
926 |
ethnl_ok = true; |
2b4a8990b ethtool: introduc... |
927 |
|
9c6451ef4 ethtool: add FEAT... |
928 929 930 |
ret = register_netdevice_notifier(ðnl_netdev_notifier); WARN(ret < 0, "ethtool: net device notifier registration failed"); return ret; |
2b4a8990b ethtool: introduc... |
931 932 933 |
} subsys_initcall(ethnl_init); |