diff options
Diffstat (limited to 'net/core/net-sysfs.c')
-rw-r--r-- | net/core/net-sysfs.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c new file mode 100644 index 000000000000..060f703659e8 --- /dev/null +++ b/net/core/net-sysfs.c | |||
@@ -0,0 +1,461 @@ | |||
1 | /* | ||
2 | * net-sysfs.c - network device class and attributes | ||
3 | * | ||
4 | * Copyright (c) 2003 Stephen Hemminger <shemminger@osdl.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/netdevice.h> | ||
15 | #include <linux/if_arp.h> | ||
16 | #include <net/sock.h> | ||
17 | #include <linux/rtnetlink.h> | ||
18 | #include <linux/wireless.h> | ||
19 | |||
20 | #define to_class_dev(obj) container_of(obj,struct class_device,kobj) | ||
21 | #define to_net_dev(class) container_of(class, struct net_device, class_dev) | ||
22 | |||
23 | static const char fmt_hex[] = "%#x\n"; | ||
24 | static const char fmt_dec[] = "%d\n"; | ||
25 | static const char fmt_ulong[] = "%lu\n"; | ||
26 | |||
27 | static inline int dev_isalive(const struct net_device *dev) | ||
28 | { | ||
29 | return dev->reg_state == NETREG_REGISTERED; | ||
30 | } | ||
31 | |||
32 | /* use same locking rules as GIF* ioctl's */ | ||
33 | static ssize_t netdev_show(const struct class_device *cd, char *buf, | ||
34 | ssize_t (*format)(const struct net_device *, char *)) | ||
35 | { | ||
36 | struct net_device *net = to_net_dev(cd); | ||
37 | ssize_t ret = -EINVAL; | ||
38 | |||
39 | read_lock(&dev_base_lock); | ||
40 | if (dev_isalive(net)) | ||
41 | ret = (*format)(net, buf); | ||
42 | read_unlock(&dev_base_lock); | ||
43 | |||
44 | return ret; | ||
45 | } | ||
46 | |||
47 | /* generate a show function for simple field */ | ||
48 | #define NETDEVICE_SHOW(field, format_string) \ | ||
49 | static ssize_t format_##field(const struct net_device *net, char *buf) \ | ||
50 | { \ | ||
51 | return sprintf(buf, format_string, net->field); \ | ||
52 | } \ | ||
53 | static ssize_t show_##field(struct class_device *cd, char *buf) \ | ||
54 | { \ | ||
55 | return netdev_show(cd, buf, format_##field); \ | ||
56 | } | ||
57 | |||
58 | |||
59 | /* use same locking and permission rules as SIF* ioctl's */ | ||
60 | static ssize_t netdev_store(struct class_device *dev, | ||
61 | const char *buf, size_t len, | ||
62 | int (*set)(struct net_device *, unsigned long)) | ||
63 | { | ||
64 | struct net_device *net = to_net_dev(dev); | ||
65 | char *endp; | ||
66 | unsigned long new; | ||
67 | int ret = -EINVAL; | ||
68 | |||
69 | if (!capable(CAP_NET_ADMIN)) | ||
70 | return -EPERM; | ||
71 | |||
72 | new = simple_strtoul(buf, &endp, 0); | ||
73 | if (endp == buf) | ||
74 | goto err; | ||
75 | |||
76 | rtnl_lock(); | ||
77 | if (dev_isalive(net)) { | ||
78 | if ((ret = (*set)(net, new)) == 0) | ||
79 | ret = len; | ||
80 | } | ||
81 | rtnl_unlock(); | ||
82 | err: | ||
83 | return ret; | ||
84 | } | ||
85 | |||
86 | /* generate a read-only network device class attribute */ | ||
87 | #define NETDEVICE_ATTR(field, format_string) \ | ||
88 | NETDEVICE_SHOW(field, format_string) \ | ||
89 | static CLASS_DEVICE_ATTR(field, S_IRUGO, show_##field, NULL) \ | ||
90 | |||
91 | NETDEVICE_ATTR(addr_len, fmt_dec); | ||
92 | NETDEVICE_ATTR(iflink, fmt_dec); | ||
93 | NETDEVICE_ATTR(ifindex, fmt_dec); | ||
94 | NETDEVICE_ATTR(features, fmt_hex); | ||
95 | NETDEVICE_ATTR(type, fmt_dec); | ||
96 | |||
97 | /* use same locking rules as GIFHWADDR ioctl's */ | ||
98 | static ssize_t format_addr(char *buf, const unsigned char *addr, int len) | ||
99 | { | ||
100 | int i; | ||
101 | char *cp = buf; | ||
102 | |||
103 | for (i = 0; i < len; i++) | ||
104 | cp += sprintf(cp, "%02x%c", addr[i], | ||
105 | i == (len - 1) ? '\n' : ':'); | ||
106 | return cp - buf; | ||
107 | } | ||
108 | |||
109 | static ssize_t show_address(struct class_device *dev, char *buf) | ||
110 | { | ||
111 | struct net_device *net = to_net_dev(dev); | ||
112 | ssize_t ret = -EINVAL; | ||
113 | |||
114 | read_lock(&dev_base_lock); | ||
115 | if (dev_isalive(net)) | ||
116 | ret = format_addr(buf, net->dev_addr, net->addr_len); | ||
117 | read_unlock(&dev_base_lock); | ||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | static ssize_t show_broadcast(struct class_device *dev, char *buf) | ||
122 | { | ||
123 | struct net_device *net = to_net_dev(dev); | ||
124 | if (dev_isalive(net)) | ||
125 | return format_addr(buf, net->broadcast, net->addr_len); | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | |||
129 | static ssize_t show_carrier(struct class_device *dev, char *buf) | ||
130 | { | ||
131 | struct net_device *netdev = to_net_dev(dev); | ||
132 | if (netif_running(netdev)) { | ||
133 | return sprintf(buf, fmt_dec, !!netif_carrier_ok(netdev)); | ||
134 | } | ||
135 | return -EINVAL; | ||
136 | } | ||
137 | |||
138 | static CLASS_DEVICE_ATTR(address, S_IRUGO, show_address, NULL); | ||
139 | static CLASS_DEVICE_ATTR(broadcast, S_IRUGO, show_broadcast, NULL); | ||
140 | static CLASS_DEVICE_ATTR(carrier, S_IRUGO, show_carrier, NULL); | ||
141 | |||
142 | /* read-write attributes */ | ||
143 | NETDEVICE_SHOW(mtu, fmt_dec); | ||
144 | |||
145 | static int change_mtu(struct net_device *net, unsigned long new_mtu) | ||
146 | { | ||
147 | return dev_set_mtu(net, (int) new_mtu); | ||
148 | } | ||
149 | |||
150 | static ssize_t store_mtu(struct class_device *dev, const char *buf, size_t len) | ||
151 | { | ||
152 | return netdev_store(dev, buf, len, change_mtu); | ||
153 | } | ||
154 | |||
155 | static CLASS_DEVICE_ATTR(mtu, S_IRUGO | S_IWUSR, show_mtu, store_mtu); | ||
156 | |||
157 | NETDEVICE_SHOW(flags, fmt_hex); | ||
158 | |||
159 | static int change_flags(struct net_device *net, unsigned long new_flags) | ||
160 | { | ||
161 | return dev_change_flags(net, (unsigned) new_flags); | ||
162 | } | ||
163 | |||
164 | static ssize_t store_flags(struct class_device *dev, const char *buf, size_t len) | ||
165 | { | ||
166 | return netdev_store(dev, buf, len, change_flags); | ||
167 | } | ||
168 | |||
169 | static CLASS_DEVICE_ATTR(flags, S_IRUGO | S_IWUSR, show_flags, store_flags); | ||
170 | |||
171 | NETDEVICE_SHOW(tx_queue_len, fmt_ulong); | ||
172 | |||
173 | static int change_tx_queue_len(struct net_device *net, unsigned long new_len) | ||
174 | { | ||
175 | net->tx_queue_len = new_len; | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static ssize_t store_tx_queue_len(struct class_device *dev, const char *buf, size_t len) | ||
180 | { | ||
181 | return netdev_store(dev, buf, len, change_tx_queue_len); | ||
182 | } | ||
183 | |||
184 | static CLASS_DEVICE_ATTR(tx_queue_len, S_IRUGO | S_IWUSR, show_tx_queue_len, | ||
185 | store_tx_queue_len); | ||
186 | |||
187 | |||
188 | static struct class_device_attribute *net_class_attributes[] = { | ||
189 | &class_device_attr_ifindex, | ||
190 | &class_device_attr_iflink, | ||
191 | &class_device_attr_addr_len, | ||
192 | &class_device_attr_tx_queue_len, | ||
193 | &class_device_attr_features, | ||
194 | &class_device_attr_mtu, | ||
195 | &class_device_attr_flags, | ||
196 | &class_device_attr_type, | ||
197 | &class_device_attr_address, | ||
198 | &class_device_attr_broadcast, | ||
199 | &class_device_attr_carrier, | ||
200 | NULL | ||
201 | }; | ||
202 | |||
203 | /* Show a given an attribute in the statistics group */ | ||
204 | static ssize_t netstat_show(const struct class_device *cd, char *buf, | ||
205 | unsigned long offset) | ||
206 | { | ||
207 | struct net_device *dev = to_net_dev(cd); | ||
208 | struct net_device_stats *stats; | ||
209 | ssize_t ret = -EINVAL; | ||
210 | |||
211 | if (offset > sizeof(struct net_device_stats) || | ||
212 | offset % sizeof(unsigned long) != 0) | ||
213 | WARN_ON(1); | ||
214 | |||
215 | read_lock(&dev_base_lock); | ||
216 | if (dev_isalive(dev) && dev->get_stats && | ||
217 | (stats = (*dev->get_stats)(dev))) | ||
218 | ret = sprintf(buf, fmt_ulong, | ||
219 | *(unsigned long *)(((u8 *) stats) + offset)); | ||
220 | |||
221 | read_unlock(&dev_base_lock); | ||
222 | return ret; | ||
223 | } | ||
224 | |||
225 | /* generate a read-only statistics attribute */ | ||
226 | #define NETSTAT_ENTRY(name) \ | ||
227 | static ssize_t show_##name(struct class_device *cd, char *buf) \ | ||
228 | { \ | ||
229 | return netstat_show(cd, buf, \ | ||
230 | offsetof(struct net_device_stats, name)); \ | ||
231 | } \ | ||
232 | static CLASS_DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) | ||
233 | |||
234 | NETSTAT_ENTRY(rx_packets); | ||
235 | NETSTAT_ENTRY(tx_packets); | ||
236 | NETSTAT_ENTRY(rx_bytes); | ||
237 | NETSTAT_ENTRY(tx_bytes); | ||
238 | NETSTAT_ENTRY(rx_errors); | ||
239 | NETSTAT_ENTRY(tx_errors); | ||
240 | NETSTAT_ENTRY(rx_dropped); | ||
241 | NETSTAT_ENTRY(tx_dropped); | ||
242 | NETSTAT_ENTRY(multicast); | ||
243 | NETSTAT_ENTRY(collisions); | ||
244 | NETSTAT_ENTRY(rx_length_errors); | ||
245 | NETSTAT_ENTRY(rx_over_errors); | ||
246 | NETSTAT_ENTRY(rx_crc_errors); | ||
247 | NETSTAT_ENTRY(rx_frame_errors); | ||
248 | NETSTAT_ENTRY(rx_fifo_errors); | ||
249 | NETSTAT_ENTRY(rx_missed_errors); | ||
250 | NETSTAT_ENTRY(tx_aborted_errors); | ||
251 | NETSTAT_ENTRY(tx_carrier_errors); | ||
252 | NETSTAT_ENTRY(tx_fifo_errors); | ||
253 | NETSTAT_ENTRY(tx_heartbeat_errors); | ||
254 | NETSTAT_ENTRY(tx_window_errors); | ||
255 | NETSTAT_ENTRY(rx_compressed); | ||
256 | NETSTAT_ENTRY(tx_compressed); | ||
257 | |||
258 | static struct attribute *netstat_attrs[] = { | ||
259 | &class_device_attr_rx_packets.attr, | ||
260 | &class_device_attr_tx_packets.attr, | ||
261 | &class_device_attr_rx_bytes.attr, | ||
262 | &class_device_attr_tx_bytes.attr, | ||
263 | &class_device_attr_rx_errors.attr, | ||
264 | &class_device_attr_tx_errors.attr, | ||
265 | &class_device_attr_rx_dropped.attr, | ||
266 | &class_device_attr_tx_dropped.attr, | ||
267 | &class_device_attr_multicast.attr, | ||
268 | &class_device_attr_collisions.attr, | ||
269 | &class_device_attr_rx_length_errors.attr, | ||
270 | &class_device_attr_rx_over_errors.attr, | ||
271 | &class_device_attr_rx_crc_errors.attr, | ||
272 | &class_device_attr_rx_frame_errors.attr, | ||
273 | &class_device_attr_rx_fifo_errors.attr, | ||
274 | &class_device_attr_rx_missed_errors.attr, | ||
275 | &class_device_attr_tx_aborted_errors.attr, | ||
276 | &class_device_attr_tx_carrier_errors.attr, | ||
277 | &class_device_attr_tx_fifo_errors.attr, | ||
278 | &class_device_attr_tx_heartbeat_errors.attr, | ||
279 | &class_device_attr_tx_window_errors.attr, | ||
280 | &class_device_attr_rx_compressed.attr, | ||
281 | &class_device_attr_tx_compressed.attr, | ||
282 | NULL | ||
283 | }; | ||
284 | |||
285 | |||
286 | static struct attribute_group netstat_group = { | ||
287 | .name = "statistics", | ||
288 | .attrs = netstat_attrs, | ||
289 | }; | ||
290 | |||
291 | #ifdef WIRELESS_EXT | ||
292 | /* helper function that does all the locking etc for wireless stats */ | ||
293 | static ssize_t wireless_show(struct class_device *cd, char *buf, | ||
294 | ssize_t (*format)(const struct iw_statistics *, | ||
295 | char *)) | ||
296 | { | ||
297 | struct net_device *dev = to_net_dev(cd); | ||
298 | const struct iw_statistics *iw; | ||
299 | ssize_t ret = -EINVAL; | ||
300 | |||
301 | read_lock(&dev_base_lock); | ||
302 | if (dev_isalive(dev) && dev->get_wireless_stats | ||
303 | && (iw = dev->get_wireless_stats(dev)) != NULL) | ||
304 | ret = (*format)(iw, buf); | ||
305 | read_unlock(&dev_base_lock); | ||
306 | |||
307 | return ret; | ||
308 | } | ||
309 | |||
310 | /* show function template for wireless fields */ | ||
311 | #define WIRELESS_SHOW(name, field, format_string) \ | ||
312 | static ssize_t format_iw_##name(const struct iw_statistics *iw, char *buf) \ | ||
313 | { \ | ||
314 | return sprintf(buf, format_string, iw->field); \ | ||
315 | } \ | ||
316 | static ssize_t show_iw_##name(struct class_device *cd, char *buf) \ | ||
317 | { \ | ||
318 | return wireless_show(cd, buf, format_iw_##name); \ | ||
319 | } \ | ||
320 | static CLASS_DEVICE_ATTR(name, S_IRUGO, show_iw_##name, NULL) | ||
321 | |||
322 | WIRELESS_SHOW(status, status, fmt_hex); | ||
323 | WIRELESS_SHOW(link, qual.qual, fmt_dec); | ||
324 | WIRELESS_SHOW(level, qual.level, fmt_dec); | ||
325 | WIRELESS_SHOW(noise, qual.noise, fmt_dec); | ||
326 | WIRELESS_SHOW(nwid, discard.nwid, fmt_dec); | ||
327 | WIRELESS_SHOW(crypt, discard.code, fmt_dec); | ||
328 | WIRELESS_SHOW(fragment, discard.fragment, fmt_dec); | ||
329 | WIRELESS_SHOW(misc, discard.misc, fmt_dec); | ||
330 | WIRELESS_SHOW(retries, discard.retries, fmt_dec); | ||
331 | WIRELESS_SHOW(beacon, miss.beacon, fmt_dec); | ||
332 | |||
333 | static struct attribute *wireless_attrs[] = { | ||
334 | &class_device_attr_status.attr, | ||
335 | &class_device_attr_link.attr, | ||
336 | &class_device_attr_level.attr, | ||
337 | &class_device_attr_noise.attr, | ||
338 | &class_device_attr_nwid.attr, | ||
339 | &class_device_attr_crypt.attr, | ||
340 | &class_device_attr_fragment.attr, | ||
341 | &class_device_attr_retries.attr, | ||
342 | &class_device_attr_misc.attr, | ||
343 | &class_device_attr_beacon.attr, | ||
344 | NULL | ||
345 | }; | ||
346 | |||
347 | static struct attribute_group wireless_group = { | ||
348 | .name = "wireless", | ||
349 | .attrs = wireless_attrs, | ||
350 | }; | ||
351 | #endif | ||
352 | |||
353 | #ifdef CONFIG_HOTPLUG | ||
354 | static int netdev_hotplug(struct class_device *cd, char **envp, | ||
355 | int num_envp, char *buf, int size) | ||
356 | { | ||
357 | struct net_device *dev = to_net_dev(cd); | ||
358 | int i = 0; | ||
359 | int n; | ||
360 | |||
361 | /* pass interface in env to hotplug. */ | ||
362 | envp[i++] = buf; | ||
363 | n = snprintf(buf, size, "INTERFACE=%s", dev->name) + 1; | ||
364 | buf += n; | ||
365 | size -= n; | ||
366 | |||
367 | if ((size <= 0) || (i >= num_envp)) | ||
368 | return -ENOMEM; | ||
369 | |||
370 | envp[i] = NULL; | ||
371 | return 0; | ||
372 | } | ||
373 | #endif | ||
374 | |||
375 | /* | ||
376 | * netdev_release -- destroy and free a dead device. | ||
377 | * Called when last reference to class_device kobject is gone. | ||
378 | */ | ||
379 | static void netdev_release(struct class_device *cd) | ||
380 | { | ||
381 | struct net_device *dev | ||
382 | = container_of(cd, struct net_device, class_dev); | ||
383 | |||
384 | BUG_ON(dev->reg_state != NETREG_RELEASED); | ||
385 | |||
386 | kfree((char *)dev - dev->padded); | ||
387 | } | ||
388 | |||
389 | static struct class net_class = { | ||
390 | .name = "net", | ||
391 | .release = netdev_release, | ||
392 | #ifdef CONFIG_HOTPLUG | ||
393 | .hotplug = netdev_hotplug, | ||
394 | #endif | ||
395 | }; | ||
396 | |||
397 | void netdev_unregister_sysfs(struct net_device * net) | ||
398 | { | ||
399 | struct class_device * class_dev = &(net->class_dev); | ||
400 | |||
401 | if (net->get_stats) | ||
402 | sysfs_remove_group(&class_dev->kobj, &netstat_group); | ||
403 | |||
404 | #ifdef WIRELESS_EXT | ||
405 | if (net->get_wireless_stats) | ||
406 | sysfs_remove_group(&class_dev->kobj, &wireless_group); | ||
407 | #endif | ||
408 | class_device_del(class_dev); | ||
409 | |||
410 | } | ||
411 | |||
412 | /* Create sysfs entries for network device. */ | ||
413 | int netdev_register_sysfs(struct net_device *net) | ||
414 | { | ||
415 | struct class_device *class_dev = &(net->class_dev); | ||
416 | int i; | ||
417 | struct class_device_attribute *attr; | ||
418 | int ret; | ||
419 | |||
420 | class_dev->class = &net_class; | ||
421 | class_dev->class_data = net; | ||
422 | |||
423 | strlcpy(class_dev->class_id, net->name, BUS_ID_SIZE); | ||
424 | if ((ret = class_device_register(class_dev))) | ||
425 | goto out; | ||
426 | |||
427 | for (i = 0; (attr = net_class_attributes[i]) != NULL; i++) { | ||
428 | if ((ret = class_device_create_file(class_dev, attr))) | ||
429 | goto out_unreg; | ||
430 | } | ||
431 | |||
432 | |||
433 | if (net->get_stats && | ||
434 | (ret = sysfs_create_group(&class_dev->kobj, &netstat_group))) | ||
435 | goto out_unreg; | ||
436 | |||
437 | #ifdef WIRELESS_EXT | ||
438 | if (net->get_wireless_stats && | ||
439 | (ret = sysfs_create_group(&class_dev->kobj, &wireless_group))) | ||
440 | goto out_cleanup; | ||
441 | |||
442 | return 0; | ||
443 | out_cleanup: | ||
444 | if (net->get_stats) | ||
445 | sysfs_remove_group(&class_dev->kobj, &netstat_group); | ||
446 | #else | ||
447 | return 0; | ||
448 | #endif | ||
449 | |||
450 | out_unreg: | ||
451 | printk(KERN_WARNING "%s: sysfs attribute registration failed %d\n", | ||
452 | net->name, ret); | ||
453 | class_device_unregister(class_dev); | ||
454 | out: | ||
455 | return ret; | ||
456 | } | ||
457 | |||
458 | int netdev_sysfs_init(void) | ||
459 | { | ||
460 | return class_register(&net_class); | ||
461 | } | ||