2 * Copyright (c) 2009, 2010 Nicira Networks.
3 * Distributed under the terms of the GNU GPL version 2.
5 * Significant portions of this file may be copied from parts of the Linux
6 * kernel, by Linus Torvalds and others.
9 #include <linux/version.h>
12 * Sysfs attributes of bridge for Open vSwitch
14 * This has been shamelessly copied from the kernel sources.
17 #include <linux/capability.h>
18 #include <linux/device.h>
19 #include <linux/kernel.h>
20 #include <linux/netdevice.h>
21 #include <linux/if_bridge.h>
22 #include <linux/rtnetlink.h>
23 #include <linux/spinlock.h>
24 #include <linux/times.h>
25 #include <linux/version.h>
29 #include "vport-internal_dev.h"
32 #define to_dev(obj) container_of(obj, struct device, kobj)
34 /* Hack to attempt to build on more platforms. */
35 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
36 #define INTERNAL_DEVICE_ATTR CLASS_DEVICE_ATTR
37 #define DEVICE_PARAMS struct class_device *d
39 #define DEV_ATTR(NAME) class_device_attr_##NAME
41 #define INTERNAL_DEVICE_ATTR DEVICE_ATTR
42 #define DEVICE_PARAMS struct device *d, struct device_attribute *attr
43 #define DEVICE_ARGS d, attr
44 #define DEV_ATTR(NAME) dev_attr_##NAME
47 struct datapath *sysfs_get_dp(struct net_device *netdev)
49 return vport_get_dp_port(internal_dev_get_vport(netdev))->dp;
53 * Common code for storing bridge parameters.
55 static ssize_t store_bridge_parm(DEVICE_PARAMS,
56 const char *buf, size_t len,
57 void (*set)(struct datapath *, unsigned long))
59 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
63 if (!capable(CAP_NET_ADMIN))
66 val = simple_strtoul(buf, &endp, 0);
71 spin_lock_bh(&br->lock);
73 spin_unlock_bh(&br->lock);
75 /* xxx We use a default value of 0 for all fields. If the caller is
76 * xxx attempting to set the value to our default, just silently
77 * xxx ignore the request.
80 printk("%s: xxx writing dp parms not supported yet!\n",
88 static ssize_t show_forward_delay(DEVICE_PARAMS, char *buf)
91 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
92 return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
94 return sprintf(buf, "%d\n", 0);
98 static void set_forward_delay(struct datapath *dp, unsigned long val)
101 unsigned long delay = clock_t_to_jiffies(val);
102 br->forward_delay = delay;
103 if (br_is_root_bridge(br))
104 br->bridge_forward_delay = delay;
106 printk("%s: xxx attempt to set_forward_delay()\n", dp_name(dp));
110 static ssize_t store_forward_delay(DEVICE_PARAMS,
111 const char *buf, size_t len)
113 return store_bridge_parm(DEVICE_ARGS, buf, len, set_forward_delay);
115 static INTERNAL_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
116 show_forward_delay, store_forward_delay);
118 static ssize_t show_hello_time(DEVICE_PARAMS, char *buf)
121 return sprintf(buf, "%lu\n",
122 jiffies_to_clock_t(to_bridge(d)->hello_time));
124 return sprintf(buf, "%d\n", 0);
128 static void set_hello_time(struct datapath *dp, unsigned long val)
131 unsigned long t = clock_t_to_jiffies(val);
133 if (br_is_root_bridge(br))
134 br->bridge_hello_time = t;
136 printk("%s: xxx attempt to set_hello_time()\n", dp_name(dp));
140 static ssize_t store_hello_time(DEVICE_PARAMS,
144 return store_bridge_parm(DEVICE_ARGS, buf, len, set_hello_time);
146 static INTERNAL_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
149 static ssize_t show_max_age(DEVICE_PARAMS, char *buf)
152 return sprintf(buf, "%lu\n",
153 jiffies_to_clock_t(to_bridge(d)->max_age));
155 return sprintf(buf, "%d\n", 0);
159 static void set_max_age(struct datapath *dp, unsigned long val)
162 unsigned long t = clock_t_to_jiffies(val);
164 if (br_is_root_bridge(br))
165 br->bridge_max_age = t;
167 printk("%s: xxx attempt to set_max_age()\n", dp_name(dp));
171 static ssize_t store_max_age(DEVICE_PARAMS,
172 const char *buf, size_t len)
174 return store_bridge_parm(DEVICE_ARGS, buf, len, set_max_age);
176 static INTERNAL_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
178 static ssize_t show_ageing_time(DEVICE_PARAMS, char *buf)
181 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
182 return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time));
184 return sprintf(buf, "%d\n", 0);
188 static void set_ageing_time(struct datapath *dp, unsigned long val)
191 br->ageing_time = clock_t_to_jiffies(val);
193 printk("%s: xxx attempt to set_ageing_time()\n", dp_name(dp));
197 static ssize_t store_ageing_time(DEVICE_PARAMS,
198 const char *buf, size_t len)
200 return store_bridge_parm(DEVICE_ARGS, buf, len, set_ageing_time);
202 static INTERNAL_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
205 static ssize_t show_stp_state(DEVICE_PARAMS, char *buf)
208 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
209 return sprintf(buf, "%d\n", br->stp_enabled);
211 return sprintf(buf, "%d\n", 0);
216 static ssize_t store_stp_state(DEVICE_PARAMS,
220 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
225 if (!capable(CAP_NET_ADMIN))
228 val = simple_strtoul(buf, &endp, 0);
233 br_stp_set_enabled(br, val);
236 printk("%s: xxx attempt to set_stp_state()\n", dp_name(dp));
241 static INTERNAL_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
244 static ssize_t show_priority(DEVICE_PARAMS, char *buf)
247 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
248 return sprintf(buf, "%d\n",
249 (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]);
251 return sprintf(buf, "%d\n", 0);
255 static void set_priority(struct datapath *dp, unsigned long val)
258 br_stp_set_bridge_priority(br, (u16) val);
260 printk("%s: xxx attempt to set_priority()\n", dp_name(dp));
264 static ssize_t store_priority(DEVICE_PARAMS,
265 const char *buf, size_t len)
267 return store_bridge_parm(DEVICE_ARGS, buf, len, set_priority);
269 static INTERNAL_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority);
271 static ssize_t show_root_id(DEVICE_PARAMS, char *buf)
274 return br_show_bridge_id(buf, &to_bridge(d)->designated_root);
276 return sprintf(buf, "0000.010203040506\n");
279 static INTERNAL_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
281 static ssize_t show_bridge_id(DEVICE_PARAMS, char *buf)
283 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
284 const unsigned char *addr = vport_get_addr(dp->ports[ODPP_LOCAL]->vport);
286 /* xxx Do we need a lock of some sort? */
287 return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
288 0, 0, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
290 static INTERNAL_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
292 static ssize_t show_root_port(DEVICE_PARAMS, char *buf)
295 return sprintf(buf, "%d\n", to_bridge(d)->root_port);
297 return sprintf(buf, "%d\n", 0);
300 static INTERNAL_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
302 static ssize_t show_root_path_cost(DEVICE_PARAMS, char *buf)
305 return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost);
307 return sprintf(buf, "%d\n", 0);
310 static INTERNAL_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
312 static ssize_t show_topology_change(DEVICE_PARAMS, char *buf)
315 return sprintf(buf, "%d\n", to_bridge(d)->topology_change);
317 return sprintf(buf, "%d\n", 0);
320 static INTERNAL_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
322 static ssize_t show_topology_change_detected(DEVICE_PARAMS, char *buf)
325 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
326 return sprintf(buf, "%d\n", br->topology_change_detected);
328 return sprintf(buf, "%d\n", 0);
331 static INTERNAL_DEVICE_ATTR(topology_change_detected, S_IRUGO,
332 show_topology_change_detected, NULL);
334 static ssize_t show_hello_timer(DEVICE_PARAMS, char *buf)
337 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
338 return sprintf(buf, "%ld\n", br_timer_value(&br->hello_timer));
340 return sprintf(buf, "%d\n", 0);
343 static INTERNAL_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
345 static ssize_t show_tcn_timer(DEVICE_PARAMS, char *buf)
348 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
349 return sprintf(buf, "%ld\n", br_timer_value(&br->tcn_timer));
351 return sprintf(buf, "%d\n", 0);
354 static INTERNAL_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
356 static ssize_t show_topology_change_timer(DEVICE_PARAMS, char *buf)
359 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
360 return sprintf(buf, "%ld\n", br_timer_value(&br->topology_change_timer));
362 return sprintf(buf, "%d\n", 0);
365 static INTERNAL_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
368 static ssize_t show_gc_timer(DEVICE_PARAMS, char *buf)
371 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
372 return sprintf(buf, "%ld\n", br_timer_value(&br->gc_timer));
374 return sprintf(buf, "%d\n", 0);
377 static INTERNAL_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
379 static ssize_t show_group_addr(DEVICE_PARAMS, char *buf)
382 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
383 return sprintf(buf, "%x:%x:%x:%x:%x:%x\n",
384 br->group_addr[0], br->group_addr[1],
385 br->group_addr[2], br->group_addr[3],
386 br->group_addr[4], br->group_addr[5]);
388 return sprintf(buf, "00:01:02:03:04:05\n");
392 static ssize_t store_group_addr(DEVICE_PARAMS,
393 const char *buf, size_t len)
395 struct datapath *dp = sysfs_get_dp(to_net_dev(d));
397 unsigned new_addr[6];
400 if (!capable(CAP_NET_ADMIN))
403 if (sscanf(buf, "%x:%x:%x:%x:%x:%x",
404 &new_addr[0], &new_addr[1], &new_addr[2],
405 &new_addr[3], &new_addr[4], &new_addr[5]) != 6)
408 /* Must be 01:80:c2:00:00:0X */
409 for (i = 0; i < 5; i++)
410 if (new_addr[i] != br_group_address[i])
413 if (new_addr[5] & ~0xf)
416 if (new_addr[5] == 1 /* 802.3x Pause address */
417 || new_addr[5] == 2 /* 802.3ad Slow protocols */
418 || new_addr[5] == 3) /* 802.1X PAE address */
421 spin_lock_bh(&br->lock);
422 for (i = 0; i < 6; i++)
423 br->group_addr[i] = new_addr[i];
424 spin_unlock_bh(&br->lock);
426 printk("%s: xxx attempt to store_group_addr()\n", dp_name(dp));
431 static INTERNAL_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
432 show_group_addr, store_group_addr);
434 static struct attribute *bridge_attrs[] = {
435 &DEV_ATTR(forward_delay).attr,
436 &DEV_ATTR(hello_time).attr,
437 &DEV_ATTR(max_age).attr,
438 &DEV_ATTR(ageing_time).attr,
439 &DEV_ATTR(stp_state).attr,
440 &DEV_ATTR(priority).attr,
441 &DEV_ATTR(bridge_id).attr,
442 &DEV_ATTR(root_id).attr,
443 &DEV_ATTR(root_path_cost).attr,
444 &DEV_ATTR(root_port).attr,
445 &DEV_ATTR(topology_change).attr,
446 &DEV_ATTR(topology_change_detected).attr,
447 &DEV_ATTR(hello_timer).attr,
448 &DEV_ATTR(tcn_timer).attr,
449 &DEV_ATTR(topology_change_timer).attr,
450 &DEV_ATTR(gc_timer).attr,
451 &DEV_ATTR(group_addr).attr,
455 static struct attribute_group bridge_group = {
456 .name = SYSFS_BRIDGE_ATTR, /* "bridge" */
457 .attrs = bridge_attrs,
461 * Add entries in sysfs onto the existing network class device
463 * Adds a attribute group "bridge" containing tuning parameters.
464 * Sub directory to hold links to interfaces.
466 * Note: the ifobj exists only to be a subdirectory
467 * to hold links. The ifobj exists in the same data structure
468 * as its parent the bridge so reference counting works.
470 int dp_sysfs_add_dp(struct datapath *dp)
472 struct kobject *kobj = vport_get_kobj(dp->ports[ODPP_LOCAL]->vport);
475 /* Create /sys/class/net/<devname>/bridge directory. */
476 err = sysfs_create_group(kobj, &bridge_group);
478 pr_info("%s: can't create group %s/%s\n",
479 __func__, dp_name(dp), bridge_group.name);
483 /* Create /sys/class/net/<devname>/brif directory. */
484 err = kobject_add(&dp->ifobj, kobj, SYSFS_BRIDGE_PORT_SUBDIR);
486 pr_info("%s: can't add kobject (directory) %s/%s\n",
487 __FUNCTION__, dp_name(dp), kobject_name(&dp->ifobj));
490 kobject_uevent(&dp->ifobj, KOBJ_ADD);
494 sysfs_remove_group(kobj, &bridge_group);
499 int dp_sysfs_del_dp(struct datapath *dp)
501 struct kobject *kobj = vport_get_kobj(dp->ports[ODPP_LOCAL]->vport);
503 kobject_del(&dp->ifobj);
504 sysfs_remove_group(kobj, &bridge_group);
508 #else /* !CONFIG_SYSFS */
509 int dp_sysfs_add_dp(struct datapath *dp) { return 0; }
510 int dp_sysfs_del_dp(struct datapath *dp) { return 0; }
511 int dp_sysfs_add_if(struct dp_port *p) { return 0; }
512 int dp_sysfs_del_if(struct dp_port *p) { return 0; }
513 #endif /* !CONFIG_SYSFS */