Blame view

net/ethtool/netlink.c 26.3 KB
2b4a8990b   Michal Kubecek   ethtool: introduc...
1
  // SPDX-License-Identifier: GPL-2.0-only
041b1c5d4   Michal Kubecek   ethtool: helper f...
2
  #include <net/sock.h>
2b4a8990b   Michal Kubecek   ethtool: introduc...
3
4
  #include <linux/ethtool_netlink.h>
  #include "netlink.h"
041b1c5d4   Michal Kubecek   ethtool: helper f...
5
  static struct genl_family ethtool_genl_family;
6b08d6c14   Michal Kubecek   ethtool: support ...
6
  static bool ethnl_ok __read_mostly;
5cf2a548b   Michal Kubecek   ethtool: add defa...
7
  static u32 ethnl_bcast_seq;
6b08d6c14   Michal Kubecek   ethtool: support ...
8

a0de1cd35   Jakub Kicinski   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   Jakub Kicinski   ethtool: link up ...
12
  const struct nla_policy ethnl_header_policy[] = {
041b1c5d4   Michal Kubecek   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   Jakub Kicinski   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   Michal Kubecek   ethtool: helper f...
26
27
28
  };
  
  /**
98130546d   Michal Kubecek   ethtool: rename e...
29
   * ethnl_parse_header_dev_get() - parse request header
041b1c5d4   Michal Kubecek   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   Michal Kubecek   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   Michal Kubecek   ethtool: helper f...
47
  {
ff419afa4   Jakub Kicinski   ethtool: trim pol...
48
  	struct nlattr *tb[ARRAY_SIZE(ethnl_header_policy)];
041b1c5d4   Michal Kubecek   ethtool: helper f...
49
50
  	const struct nlattr *devname_attr;
  	struct net_device *dev = NULL;
2363d73a2   Michal Kubecek   ethtool: reject u...
51
  	u32 flags = 0;
041b1c5d4   Michal Kubecek   ethtool: helper f...
52
53
54
55
56
57
  	int ret;
  
  	if (!header) {
  		NL_SET_ERR_MSG(extack, "request header missing");
  		return -EINVAL;
  	}
a0de1cd35   Jakub Kicinski   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   Jakub Kicinski   ethtool: trim pol...
61
  	ret = nla_parse_nested(tb, ARRAY_SIZE(ethnl_header_policy) - 1, header,
a0de1cd35   Jakub Kicinski   ethtool: specify ...
62
  			       NULL, extack);
041b1c5d4   Michal Kubecek   ethtool: helper f...
63
64
  	if (ret < 0)
  		return ret;
a0de1cd35   Jakub Kicinski   ethtool: specify ...
65
  	if (tb[ETHTOOL_A_HEADER_FLAGS])
2363d73a2   Michal Kubecek   ethtool: reject u...
66
  		flags = nla_get_u32(tb[ETHTOOL_A_HEADER_FLAGS]);
041b1c5d4   Michal Kubecek   ethtool: helper f...
67

2363d73a2   Michal Kubecek   ethtool: reject u...
68
  	devname_attr = tb[ETHTOOL_A_HEADER_DEV_NAME];
041b1c5d4   Michal Kubecek   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   Michal Kubecek   ethtool: reject u...
107
  	req_info->flags = flags;
041b1c5d4   Michal Kubecek   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   Michal Kubecek   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   Michal Kubecek   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, &ethtool_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   Jakub Kicinski   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,
  			   &ethtool_genl_family, 0, cmd);
  }
0df960f14   Andrew Lunn   net: ethtool: Mak...
192
  void *ethnl_bcastmsg_put(struct sk_buff *skb, u8 cmd)
5cf2a548b   Michal Kubecek   ethtool: add defa...
193
194
195
196
  {
  	return genlmsg_put(skb, 0, ++ethnl_bcast_seq, &ethtool_genl_family, 0,
  			   cmd);
  }
0df960f14   Andrew Lunn   net: ethtool: Mak...
197
  int ethnl_multicast(struct sk_buff *skb, struct net_device *dev)
5cf2a548b   Michal Kubecek   ethtool: add defa...
198
199
200
201
  {
  	return genlmsg_multicast_netns(&ethtool_genl_family, dev_net(dev), skb,
  				       0, ETHNL_MCGRP_MONITOR, GFP_KERNEL);
  }
728480f12   Michal Kubecek   ethtool: default ...
202
203
204
205
  /* GET request helpers */
  
  /**
   * struct ethnl_dump_ctx - context structure for generic dumpit() callback
d2c4b444f   Michal Kubecek   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   Michal Kubecek   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   Michal Kubecek   ethtool: provide ...
226
  	[ETHTOOL_MSG_STRSET_GET]	= &ethnl_strset_request_ops,
459e0b81b   Michal Kubecek   ethtool: provide ...
227
  	[ETHTOOL_MSG_LINKINFO_GET]	= &ethnl_linkinfo_request_ops,
f625aa9be   Michal Kubecek   ethtool: provide ...
228
  	[ETHTOOL_MSG_LINKMODES_GET]	= &ethnl_linkmodes_request_ops,
3d2b847fb   Michal Kubecek   ethtool: provide ...
229
  	[ETHTOOL_MSG_LINKSTATE_GET]	= &ethnl_linkstate_request_ops,
6a94b8ccf   Michal Kubecek   ethtool: provide ...
230
  	[ETHTOOL_MSG_DEBUG_GET]		= &ethnl_debug_request_ops,
51ea22b04   Michal Kubecek   ethtool: provide ...
231
  	[ETHTOOL_MSG_WOL_GET]		= &ethnl_wol_request_ops,
0524399d4   Michal Kubecek   ethtool: provide ...
232
  	[ETHTOOL_MSG_FEATURES_GET]	= &ethnl_features_request_ops,
e16c3386f   Michal Kubecek   ethtool: provide ...
233
  	[ETHTOOL_MSG_PRIVFLAGS_GET]	= &ethnl_privflags_request_ops,
e4a1717b6   Michal Kubecek   ethtool: provide ...
234
  	[ETHTOOL_MSG_RINGS_GET]		= &ethnl_rings_request_ops,
0c84979c9   Michal Kubecek   ethtool: provide ...
235
  	[ETHTOOL_MSG_CHANNELS_GET]	= &ethnl_channels_request_ops,
217275453   Michal Kubecek   ethtool: provide ...
236
  	[ETHTOOL_MSG_COALESCE_GET]	= &ethnl_coalesce_request_ops,
7f59fb32b   Michal Kubecek   ethtool: provide ...
237
  	[ETHTOOL_MSG_PAUSE_GET]		= &ethnl_pause_request_ops,
b7eeefe72   Michal Kubecek   ethtool: provide ...
238
  	[ETHTOOL_MSG_EEE_GET]		= &ethnl_eee_request_ops,
5b071c59e   Michal Kubecek   ethtool: provide ...
239
  	[ETHTOOL_MSG_TSINFO_GET]	= &ethnl_tsinfo_request_ops,
728480f12   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
250
   * @tb:		 parsed attributes
728480f12   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
262
  			       struct nlattr **tb, struct net *net,
728480f12   Michal Kubecek   ethtool: default ...
263
264
265
  			       const struct ethnl_request_ops *request_ops,
  			       struct netlink_ext_ack *extack, bool require_dev)
  {
728480f12   Michal Kubecek   ethtool: default ...
266
  	int ret;
98130546d   Michal Kubecek   ethtool: rename e...
267
268
  	ret = ethnl_parse_header_dev_get(req_info, tb[request_ops->hdr_attr],
  					 net, extack, require_dev);
728480f12   Michal Kubecek   ethtool: default ...
269
  	if (ret < 0)
4f30974fe   Jakub Kicinski   ethtool: wire up ...
270
  		return ret;
728480f12   Michal Kubecek   ethtool: default ...
271
272
273
274
  
  	if (request_ops->parse_request) {
  		ret = request_ops->parse_request(req_info, tb, extack);
  		if (ret < 0)
4f30974fe   Jakub Kicinski   ethtool: wire up ...
275
  			return ret;
728480f12   Michal Kubecek   ethtool: default ...
276
  	}
4f30974fe   Jakub Kicinski   ethtool: wire up ...
277
  	return 0;
728480f12   Michal Kubecek   ethtool: default ...
278
279
280
281
  }
  
  /**
   * ethnl_init_reply_data() - Initialize reply data for GET request
d2c4b444f   Michal Kubecek   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   Michal Kubecek   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   Jakub Kicinski   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   Michal Kubecek   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   Dan Carpenter   ethtool: fix ->re...
333
  	ret = ops->reply_size(req_info, reply_data);
728480f12   Michal Kubecek   ethtool: default ...
334
335
  	if (ret < 0)
  		goto err_cleanup;
7c87e32d2   Michal Kubecek   ethtool: count he...
336
  	reply_len = ret + ethnl_reply_header_size();
728480f12   Michal Kubecek   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   Michal Kubecek   ethtool: fix genl...
371
372
  				  const struct ethnl_dump_ctx *ctx,
  				  struct netlink_callback *cb)
728480f12   Michal Kubecek   ethtool: default ...
373
  {
365f9ae4e   Michal Kubecek   ethtool: fix genl...
374
  	void *ehdr;
728480f12   Michal Kubecek   ethtool: default ...
375
  	int ret;
365f9ae4e   Michal Kubecek   ethtool: fix genl...
376
377
378
379
  	ehdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
  			   &ethtool_genl_family, 0, ctx->ops->reply_cmd);
  	if (!ehdr)
  		return -EMSGSIZE;
728480f12   Michal Kubecek   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   Michal Kubecek   ethtool: fix genl...
395
396
397
398
  	if (ret < 0)
  		genlmsg_cancel(skb, ehdr);
  	else
  		genlmsg_end(skb, ehdr);
728480f12   Michal Kubecek   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   Michal Kubecek   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   Michal Kubecek   ethtool: fix genl...
433
  			ret = ethnl_default_dump_one(skb, dev, ctx, cb);
728480f12   Michal Kubecek   ethtool: default ...
434
435
  			dev_put(dev);
  			if (ret < 0) {
728480f12   Michal Kubecek   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   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
466
  	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
728480f12   Michal Kubecek   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   Dan Carpenter   ethtool: fix a me...
486
487
  		ret = -ENOMEM;
  		goto free_req_info;
728480f12   Michal Kubecek   ethtool: default ...
488
  	}
4f30974fe   Jakub Kicinski   ethtool: wire up ...
489
490
  	ret = ethnl_default_parse(req_info, info->attrs, sock_net(cb->skb->sk),
  				  ops, cb->extack, false);
728480f12   Michal Kubecek   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   Dan Carpenter   ethtool: fix a me...
500
  		goto free_reply_data;
728480f12   Michal Kubecek   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   Dan Carpenter   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   Michal Kubecek   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   Michal Kubecek   ethtool: add defa...
528
529
  static const struct ethnl_request_ops *
  ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = {
73286734c   Michal Kubecek   ethtool: add LINK...
530
  	[ETHTOOL_MSG_LINKINFO_NTF]	= &ethnl_linkinfo_request_ops,
1b1b1847c   Michal Kubecek   ethtool: add LINK...
531
  	[ETHTOOL_MSG_LINKMODES_NTF]	= &ethnl_linkmodes_request_ops,
0bda7af39   Michal Kubecek   ethtool: add DEBU...
532
  	[ETHTOOL_MSG_DEBUG_NTF]		= &ethnl_debug_request_ops,
67bffa792   Michal Kubecek   ethtool: add WOL_...
533
  	[ETHTOOL_MSG_WOL_NTF]		= &ethnl_wol_request_ops,
9c6451ef4   Michal Kubecek   ethtool: add FEAT...
534
  	[ETHTOOL_MSG_FEATURES_NTF]	= &ethnl_features_request_ops,
111dcba3c   Michal Kubecek   ethtool: add PRIV...
535
  	[ETHTOOL_MSG_PRIVFLAGS_NTF]	= &ethnl_privflags_request_ops,
bc9d1c995   Michal Kubecek   ethtool: add RING...
536
  	[ETHTOOL_MSG_RINGS_NTF]		= &ethnl_rings_request_ops,
546379b9a   Michal Kubecek   ethtool: add CHAN...
537
  	[ETHTOOL_MSG_CHANNELS_NTF]	= &ethnl_channels_request_ops,
0cf3eac8c   Michal Kubecek   ethtool: add COAL...
538
  	[ETHTOOL_MSG_COALESCE_NTF]	= &ethnl_coalesce_request_ops,
bf37faa38   Michal Kubecek   ethtool: add PAUS...
539
  	[ETHTOOL_MSG_PAUSE_NTF]		= &ethnl_pause_request_ops,
6c5bc8fe4   Michal Kubecek   ethtool: add EEE_...
540
  	[ETHTOOL_MSG_EEE_NTF]		= &ethnl_eee_request_ops,
5cf2a548b   Michal Kubecek   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   Dan Carpenter   ethtool: fix ->re...
577
  	ret = ops->reply_size(req_info, reply_data);
5cf2a548b   Michal Kubecek   ethtool: add defa...
578
579
  	if (ret < 0)
  		goto err_cleanup;
7c87e32d2   Michal Kubecek   ethtool: count he...
580
  	reply_len = ret + ethnl_reply_header_size();
5cf2a548b   Michal Kubecek   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   Michal Kubecek   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   Michal Kubecek   ethtool: add LINK...
623
  	[ETHTOOL_MSG_LINKINFO_NTF]	= ethnl_default_notify,
1b1b1847c   Michal Kubecek   ethtool: add LINK...
624
  	[ETHTOOL_MSG_LINKMODES_NTF]	= ethnl_default_notify,
0bda7af39   Michal Kubecek   ethtool: add DEBU...
625
  	[ETHTOOL_MSG_DEBUG_NTF]		= ethnl_default_notify,
67bffa792   Michal Kubecek   ethtool: add WOL_...
626
  	[ETHTOOL_MSG_WOL_NTF]		= ethnl_default_notify,
9c6451ef4   Michal Kubecek   ethtool: add FEAT...
627
  	[ETHTOOL_MSG_FEATURES_NTF]	= ethnl_default_notify,
111dcba3c   Michal Kubecek   ethtool: add PRIV...
628
  	[ETHTOOL_MSG_PRIVFLAGS_NTF]	= ethnl_default_notify,
bc9d1c995   Michal Kubecek   ethtool: add RING...
629
  	[ETHTOOL_MSG_RINGS_NTF]		= ethnl_default_notify,
546379b9a   Michal Kubecek   ethtool: add CHAN...
630
  	[ETHTOOL_MSG_CHANNELS_NTF]	= ethnl_default_notify,
0cf3eac8c   Michal Kubecek   ethtool: add COAL...
631
  	[ETHTOOL_MSG_COALESCE_NTF]	= ethnl_default_notify,
bf37faa38   Michal Kubecek   ethtool: add PAUS...
632
  	[ETHTOOL_MSG_PAUSE_NTF]		= ethnl_default_notify,
6c5bc8fe4   Michal Kubecek   ethtool: add EEE_...
633
  	[ETHTOOL_MSG_EEE_NTF]		= ethnl_default_notify,
6b08d6c14   Michal Kubecek   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   Michal Kubecek   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   Michal Kubecek   ethtool: introduc...
673
674
675
  /* genetlink setup */
  
  static const struct genl_ops ethtool_genl_ops[] = {
71921690f   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
682
683
  		.policy = ethnl_strset_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_strset_get_policy) - 1,
71921690f   Michal Kubecek   ethtool: provide ...
684
  	},
459e0b81b   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
691
692
  		.policy = ethnl_linkinfo_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_linkinfo_get_policy) - 1,
459e0b81b   Michal Kubecek   ethtool: provide ...
693
  	},
a53f3d41e   Michal Kubecek   ethtool: set link...
694
695
696
697
  	{
  		.cmd	= ETHTOOL_MSG_LINKINFO_SET,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_set_linkinfo,
5028588b6   Jakub Kicinski   ethtool: wire up ...
698
699
  		.policy = ethnl_linkinfo_set_policy,
  		.maxattr = ARRAY_SIZE(ethnl_linkinfo_set_policy) - 1,
a53f3d41e   Michal Kubecek   ethtool: set link...
700
  	},
f625aa9be   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
707
708
  		.policy = ethnl_linkmodes_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_linkmodes_get_policy) - 1,
f625aa9be   Michal Kubecek   ethtool: provide ...
709
  	},
bfbcfe203   Michal Kubecek   ethtool: set link...
710
711
712
713
  	{
  		.cmd	= ETHTOOL_MSG_LINKMODES_SET,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_set_linkmodes,
5028588b6   Jakub Kicinski   ethtool: wire up ...
714
715
  		.policy = ethnl_linkmodes_set_policy,
  		.maxattr = ARRAY_SIZE(ethnl_linkmodes_set_policy) - 1,
bfbcfe203   Michal Kubecek   ethtool: set link...
716
  	},
3d2b847fb   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
723
724
  		.policy = ethnl_linkstate_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_linkstate_get_policy) - 1,
3d2b847fb   Michal Kubecek   ethtool: provide ...
725
  	},
6a94b8ccf   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
732
733
  		.policy = ethnl_debug_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_debug_get_policy) - 1,
6a94b8ccf   Michal Kubecek   ethtool: provide ...
734
  	},
e54d04e3a   Michal Kubecek   ethtool: set mess...
735
736
737
738
  	{
  		.cmd	= ETHTOOL_MSG_DEBUG_SET,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_set_debug,
5028588b6   Jakub Kicinski   ethtool: wire up ...
739
740
  		.policy = ethnl_debug_set_policy,
  		.maxattr = ARRAY_SIZE(ethnl_debug_set_policy) - 1,
e54d04e3a   Michal Kubecek   ethtool: set mess...
741
  	},
51ea22b04   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
749
750
  		.policy = ethnl_wol_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_wol_get_policy) - 1,
51ea22b04   Michal Kubecek   ethtool: provide ...
751
  	},
8d425b19b   Michal Kubecek   ethtool: set wake...
752
753
754
755
  	{
  		.cmd	= ETHTOOL_MSG_WOL_SET,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_set_wol,
5028588b6   Jakub Kicinski   ethtool: wire up ...
756
757
  		.policy = ethnl_wol_set_policy,
  		.maxattr = ARRAY_SIZE(ethnl_wol_set_policy) - 1,
8d425b19b   Michal Kubecek   ethtool: set wake...
758
  	},
0524399d4   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
765
766
  		.policy = ethnl_features_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_features_get_policy) - 1,
0524399d4   Michal Kubecek   ethtool: provide ...
767
  	},
0980bfcd6   Michal Kubecek   ethtool: set netd...
768
769
770
771
  	{
  		.cmd	= ETHTOOL_MSG_FEATURES_SET,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_set_features,
5028588b6   Jakub Kicinski   ethtool: wire up ...
772
773
  		.policy = ethnl_features_set_policy,
  		.maxattr = ARRAY_SIZE(ethnl_features_set_policy) - 1,
0980bfcd6   Michal Kubecek   ethtool: set netd...
774
  	},
e16c3386f   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
781
782
  		.policy = ethnl_privflags_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_privflags_get_policy) - 1,
e16c3386f   Michal Kubecek   ethtool: provide ...
783
  	},
f265d7995   Michal Kubecek   ethtool: set devi...
784
785
786
787
  	{
  		.cmd	= ETHTOOL_MSG_PRIVFLAGS_SET,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_set_privflags,
5028588b6   Jakub Kicinski   ethtool: wire up ...
788
789
  		.policy = ethnl_privflags_set_policy,
  		.maxattr = ARRAY_SIZE(ethnl_privflags_set_policy) - 1,
f265d7995   Michal Kubecek   ethtool: set devi...
790
  	},
e4a1717b6   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
797
798
  		.policy = ethnl_rings_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_rings_get_policy) - 1,
e4a1717b6   Michal Kubecek   ethtool: provide ...
799
  	},
2fc2929e8   Michal Kubecek   ethtool: set devi...
800
801
802
803
  	{
  		.cmd	= ETHTOOL_MSG_RINGS_SET,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_set_rings,
5028588b6   Jakub Kicinski   ethtool: wire up ...
804
805
  		.policy = ethnl_rings_set_policy,
  		.maxattr = ARRAY_SIZE(ethnl_rings_set_policy) - 1,
2fc2929e8   Michal Kubecek   ethtool: set devi...
806
  	},
0c84979c9   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
813
814
  		.policy = ethnl_channels_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_channels_get_policy) - 1,
0c84979c9   Michal Kubecek   ethtool: provide ...
815
  	},
e19c591ea   Michal Kubecek   ethtool: set devi...
816
817
818
819
  	{
  		.cmd	= ETHTOOL_MSG_CHANNELS_SET,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_set_channels,
fd15dd058   Johannes Berg   ethtool: correct ...
820
821
  		.policy = ethnl_channels_set_policy,
  		.maxattr = ARRAY_SIZE(ethnl_channels_set_policy) - 1,
e19c591ea   Michal Kubecek   ethtool: set devi...
822
  	},
217275453   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
829
830
  		.policy = ethnl_coalesce_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_coalesce_get_policy) - 1,
217275453   Michal Kubecek   ethtool: provide ...
831
  	},
9881418c7   Michal Kubecek   ethtool: set coal...
832
833
834
835
  	{
  		.cmd	= ETHTOOL_MSG_COALESCE_SET,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_set_coalesce,
5028588b6   Jakub Kicinski   ethtool: wire up ...
836
837
  		.policy = ethnl_coalesce_set_policy,
  		.maxattr = ARRAY_SIZE(ethnl_coalesce_set_policy) - 1,
9881418c7   Michal Kubecek   ethtool: set coal...
838
  	},
7f59fb32b   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
845
846
  		.policy = ethnl_pause_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_pause_get_policy) - 1,
7f59fb32b   Michal Kubecek   ethtool: provide ...
847
  	},
3ab879933   Michal Kubecek   ethtool: set paus...
848
849
850
851
  	{
  		.cmd	= ETHTOOL_MSG_PAUSE_SET,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_set_pause,
5028588b6   Jakub Kicinski   ethtool: wire up ...
852
853
  		.policy = ethnl_pause_set_policy,
  		.maxattr = ARRAY_SIZE(ethnl_pause_set_policy) - 1,
3ab879933   Michal Kubecek   ethtool: set paus...
854
  	},
b7eeefe72   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
861
862
  		.policy = ethnl_eee_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_eee_get_policy) - 1,
b7eeefe72   Michal Kubecek   ethtool: provide ...
863
  	},
fd77be7bd   Michal Kubecek   ethtool: set EEE ...
864
865
866
867
  	{
  		.cmd	= ETHTOOL_MSG_EEE_SET,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_set_eee,
5028588b6   Jakub Kicinski   ethtool: wire up ...
868
869
  		.policy = ethnl_eee_set_policy,
  		.maxattr = ARRAY_SIZE(ethnl_eee_set_policy) - 1,
fd77be7bd   Michal Kubecek   ethtool: set EEE ...
870
  	},
5b071c59e   Michal Kubecek   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   Jakub Kicinski   ethtool: wire up ...
877
878
  		.policy = ethnl_tsinfo_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_tsinfo_get_policy) - 1,
5b071c59e   Michal Kubecek   ethtool: provide ...
879
  	},
11ca3c426   Andrew Lunn   net: ethtool: net...
880
881
882
883
  	{
  		.cmd	= ETHTOOL_MSG_CABLE_TEST_ACT,
  		.flags	= GENL_UNS_ADMIN_PERM,
  		.doit	= ethnl_act_cable_test,
5028588b6   Jakub Kicinski   ethtool: wire up ...
884
885
  		.policy = ethnl_cable_test_act_policy,
  		.maxattr = ARRAY_SIZE(ethnl_cable_test_act_policy) - 1,
11ca3c426   Andrew Lunn   net: ethtool: net...
886
  	},
1a644de29   Andrew Lunn   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   Jakub Kicinski   ethtool: wire up ...
891
892
  		.policy = ethnl_cable_test_tdr_act_policy,
  		.maxattr = ARRAY_SIZE(ethnl_cable_test_tdr_act_policy) - 1,
1a644de29   Andrew Lunn   net: ethtool: Add...
893
  	},
c7d759eb7   Jakub Kicinski   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   Jakub Kicinski   ethtool: wire up ...
899
900
  		.policy = ethnl_tunnel_info_get_policy,
  		.maxattr = ARRAY_SIZE(ethnl_tunnel_info_get_policy) - 1,
c7d759eb7   Jakub Kicinski   ethtool: add tunn...
901
  	},
2b4a8990b   Michal Kubecek   ethtool: introduc...
902
  };
6b08d6c14   Michal Kubecek   ethtool: support ...
903
904
905
  static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
  	[ETHNL_MCGRP_MONITOR] = { .name = ETHTOOL_MCGRP_MONITOR_NAME },
  };
78b70155d   Jakub Kicinski   ethtool: mark net...
906
  static struct genl_family ethtool_genl_family __ro_after_init = {
2b4a8990b   Michal Kubecek   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   Michal Kubecek   ethtool: support ...
913
914
  	.mcgrps		= ethtool_nl_mcgrps,
  	.n_mcgrps	= ARRAY_SIZE(ethtool_nl_mcgrps),
2b4a8990b   Michal Kubecek   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(&ethtool_genl_family);
  	if (WARN(ret < 0, "ethtool: genetlink family registration failed"))
  		return ret;
6b08d6c14   Michal Kubecek   ethtool: support ...
926
  	ethnl_ok = true;
2b4a8990b   Michal Kubecek   ethtool: introduc...
927

9c6451ef4   Michal Kubecek   ethtool: add FEAT...
928
929
930
  	ret = register_netdevice_notifier(&ethnl_netdev_notifier);
  	WARN(ret < 0, "ethtool: net device notifier registration failed");
  	return ret;
2b4a8990b   Michal Kubecek   ethtool: introduc...
931
932
933
  }
  
  subsys_initcall(ethnl_init);