18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	Sysfs attributes of bridge ports
48c2ecf20Sopenharmony_ci *	Linux ethernet bridge
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *	Authors:
78c2ecf20Sopenharmony_ci *	Stephen Hemminger		<shemminger@osdl.org>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/capability.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
138c2ecf20Sopenharmony_ci#include <linux/if_bridge.h>
148c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
158c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
168c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "br_private.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct brport_attribute {
218c2ecf20Sopenharmony_ci	struct attribute	attr;
228c2ecf20Sopenharmony_ci	ssize_t (*show)(struct net_bridge_port *, char *);
238c2ecf20Sopenharmony_ci	int (*store)(struct net_bridge_port *, unsigned long);
248c2ecf20Sopenharmony_ci	int (*store_raw)(struct net_bridge_port *, char *);
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define BRPORT_ATTR_RAW(_name, _mode, _show, _store)			\
288c2ecf20Sopenharmony_ciconst struct brport_attribute brport_attr_##_name = {			\
298c2ecf20Sopenharmony_ci	.attr		= {.name = __stringify(_name),			\
308c2ecf20Sopenharmony_ci			   .mode = _mode },				\
318c2ecf20Sopenharmony_ci	.show		= _show,					\
328c2ecf20Sopenharmony_ci	.store_raw	= _store,					\
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define BRPORT_ATTR(_name, _mode, _show, _store)		\
368c2ecf20Sopenharmony_ciconst struct brport_attribute brport_attr_##_name = { 	        \
378c2ecf20Sopenharmony_ci	.attr = {.name = __stringify(_name), 			\
388c2ecf20Sopenharmony_ci		 .mode = _mode },				\
398c2ecf20Sopenharmony_ci	.show	= _show,					\
408c2ecf20Sopenharmony_ci	.store	= _store,					\
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define BRPORT_ATTR_FLAG(_name, _mask)				\
448c2ecf20Sopenharmony_cistatic ssize_t show_##_name(struct net_bridge_port *p, char *buf) \
458c2ecf20Sopenharmony_ci{								\
468c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", !!(p->flags & _mask));	\
478c2ecf20Sopenharmony_ci}								\
488c2ecf20Sopenharmony_cistatic int store_##_name(struct net_bridge_port *p, unsigned long v) \
498c2ecf20Sopenharmony_ci{								\
508c2ecf20Sopenharmony_ci	return store_flag(p, v, _mask);				\
518c2ecf20Sopenharmony_ci}								\
528c2ecf20Sopenharmony_cistatic BRPORT_ATTR(_name, 0644,					\
538c2ecf20Sopenharmony_ci		   show_##_name, store_##_name)
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int store_flag(struct net_bridge_port *p, unsigned long v,
568c2ecf20Sopenharmony_ci		      unsigned long mask)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	unsigned long flags = p->flags;
598c2ecf20Sopenharmony_ci	int err;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (v)
628c2ecf20Sopenharmony_ci		flags |= mask;
638c2ecf20Sopenharmony_ci	else
648c2ecf20Sopenharmony_ci		flags &= ~mask;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (flags != p->flags) {
678c2ecf20Sopenharmony_ci		err = br_switchdev_set_port_flag(p, flags, mask);
688c2ecf20Sopenharmony_ci		if (err)
698c2ecf20Sopenharmony_ci			return err;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		p->flags = flags;
728c2ecf20Sopenharmony_ci		br_port_flags_change(p, mask);
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", p->path_cost);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic BRPORT_ATTR(path_cost, 0644,
838c2ecf20Sopenharmony_ci		   show_path_cost, br_stp_set_path_cost);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic ssize_t show_priority(struct net_bridge_port *p, char *buf)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", p->priority);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic BRPORT_ATTR(priority, 0644,
918c2ecf20Sopenharmony_ci			 show_priority, br_stp_set_port_priority);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic ssize_t show_designated_root(struct net_bridge_port *p, char *buf)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	return br_show_bridge_id(buf, &p->designated_root);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_cistatic BRPORT_ATTR(designated_root, 0444, show_designated_root, NULL);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic ssize_t show_designated_bridge(struct net_bridge_port *p, char *buf)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	return br_show_bridge_id(buf, &p->designated_bridge);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_cistatic BRPORT_ATTR(designated_bridge, 0444, show_designated_bridge, NULL);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic ssize_t show_designated_port(struct net_bridge_port *p, char *buf)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", p->designated_port);
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_cistatic BRPORT_ATTR(designated_port, 0444, show_designated_port, NULL);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic ssize_t show_designated_cost(struct net_bridge_port *p, char *buf)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", p->designated_cost);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_cistatic BRPORT_ATTR(designated_cost, 0444, show_designated_cost, NULL);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic ssize_t show_port_id(struct net_bridge_port *p, char *buf)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%x\n", p->port_id);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_cistatic BRPORT_ATTR(port_id, 0444, show_port_id, NULL);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic ssize_t show_port_no(struct net_bridge_port *p, char *buf)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%x\n", p->port_no);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic BRPORT_ATTR(port_no, 0444, show_port_no, NULL);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic ssize_t show_change_ack(struct net_bridge_port *p, char *buf)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", p->topology_change_ack);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_cistatic BRPORT_ATTR(change_ack, 0444, show_change_ack, NULL);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic ssize_t show_config_pending(struct net_bridge_port *p, char *buf)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", p->config_pending);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_cistatic BRPORT_ATTR(config_pending, 0444, show_config_pending, NULL);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic ssize_t show_port_state(struct net_bridge_port *p, char *buf)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", p->state);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_cistatic BRPORT_ATTR(state, 0444, show_port_state, NULL);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic ssize_t show_message_age_timer(struct net_bridge_port *p,
1498c2ecf20Sopenharmony_ci					    char *buf)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	return sprintf(buf, "%ld\n", br_timer_value(&p->message_age_timer));
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_cistatic BRPORT_ATTR(message_age_timer, 0444, show_message_age_timer, NULL);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic ssize_t show_forward_delay_timer(struct net_bridge_port *p,
1568c2ecf20Sopenharmony_ci					    char *buf)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	return sprintf(buf, "%ld\n", br_timer_value(&p->forward_delay_timer));
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_cistatic BRPORT_ATTR(forward_delay_timer, 0444, show_forward_delay_timer, NULL);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic ssize_t show_hold_timer(struct net_bridge_port *p,
1638c2ecf20Sopenharmony_ci					    char *buf)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	return sprintf(buf, "%ld\n", br_timer_value(&p->hold_timer));
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_cistatic BRPORT_ATTR(hold_timer, 0444, show_hold_timer, NULL);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int store_flush(struct net_bridge_port *p, unsigned long v)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	br_fdb_delete_by_port(p->br, p, 0, 0); // Don't delete local entry
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_cistatic BRPORT_ATTR(flush, 0200, NULL, store_flush);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic ssize_t show_group_fwd_mask(struct net_bridge_port *p, char *buf)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	return sprintf(buf, "%#x\n", p->group_fwd_mask);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int store_group_fwd_mask(struct net_bridge_port *p,
1828c2ecf20Sopenharmony_ci				unsigned long v)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	if (v & BR_GROUPFWD_MACPAUSE)
1858c2ecf20Sopenharmony_ci		return -EINVAL;
1868c2ecf20Sopenharmony_ci	p->group_fwd_mask = v;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return 0;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_cistatic BRPORT_ATTR(group_fwd_mask, 0644, show_group_fwd_mask,
1918c2ecf20Sopenharmony_ci		   store_group_fwd_mask);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic ssize_t show_backup_port(struct net_bridge_port *p, char *buf)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct net_bridge_port *backup_p;
1968c2ecf20Sopenharmony_ci	int ret = 0;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	rcu_read_lock();
1998c2ecf20Sopenharmony_ci	backup_p = rcu_dereference(p->backup_port);
2008c2ecf20Sopenharmony_ci	if (backup_p)
2018c2ecf20Sopenharmony_ci		ret = sprintf(buf, "%s\n", backup_p->dev->name);
2028c2ecf20Sopenharmony_ci	rcu_read_unlock();
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return ret;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic int store_backup_port(struct net_bridge_port *p, char *buf)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct net_device *backup_dev = NULL;
2108c2ecf20Sopenharmony_ci	char *nl = strchr(buf, '\n');
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (nl)
2138c2ecf20Sopenharmony_ci		*nl = '\0';
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (strlen(buf) > 0) {
2168c2ecf20Sopenharmony_ci		backup_dev = __dev_get_by_name(dev_net(p->dev), buf);
2178c2ecf20Sopenharmony_ci		if (!backup_dev)
2188c2ecf20Sopenharmony_ci			return -ENOENT;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return nbp_backup_change(p, backup_dev);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_cistatic BRPORT_ATTR_RAW(backup_port, 0644, show_backup_port, store_backup_port);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE);
2268c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD);
2278c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK);
2288c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(learning, BR_LEARNING);
2298c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD);
2308c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP);
2318c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(proxyarp_wifi, BR_PROXYARP_WIFI);
2328c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(multicast_flood, BR_MCAST_FLOOD);
2338c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD);
2348c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
2358c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(isolated, BR_ISOLATED);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
2388c2ecf20Sopenharmony_cistatic ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", p->multicast_router);
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int store_multicast_router(struct net_bridge_port *p,
2448c2ecf20Sopenharmony_ci				      unsigned long v)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	return br_multicast_set_port_router(p, v);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_cistatic BRPORT_ATTR(multicast_router, 0644, show_multicast_router,
2498c2ecf20Sopenharmony_ci		   store_multicast_router);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE);
2528c2ecf20Sopenharmony_ciBRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST);
2538c2ecf20Sopenharmony_ci#endif
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic const struct brport_attribute *brport_attrs[] = {
2568c2ecf20Sopenharmony_ci	&brport_attr_path_cost,
2578c2ecf20Sopenharmony_ci	&brport_attr_priority,
2588c2ecf20Sopenharmony_ci	&brport_attr_port_id,
2598c2ecf20Sopenharmony_ci	&brport_attr_port_no,
2608c2ecf20Sopenharmony_ci	&brport_attr_designated_root,
2618c2ecf20Sopenharmony_ci	&brport_attr_designated_bridge,
2628c2ecf20Sopenharmony_ci	&brport_attr_designated_port,
2638c2ecf20Sopenharmony_ci	&brport_attr_designated_cost,
2648c2ecf20Sopenharmony_ci	&brport_attr_state,
2658c2ecf20Sopenharmony_ci	&brport_attr_change_ack,
2668c2ecf20Sopenharmony_ci	&brport_attr_config_pending,
2678c2ecf20Sopenharmony_ci	&brport_attr_message_age_timer,
2688c2ecf20Sopenharmony_ci	&brport_attr_forward_delay_timer,
2698c2ecf20Sopenharmony_ci	&brport_attr_hold_timer,
2708c2ecf20Sopenharmony_ci	&brport_attr_flush,
2718c2ecf20Sopenharmony_ci	&brport_attr_hairpin_mode,
2728c2ecf20Sopenharmony_ci	&brport_attr_bpdu_guard,
2738c2ecf20Sopenharmony_ci	&brport_attr_root_block,
2748c2ecf20Sopenharmony_ci	&brport_attr_learning,
2758c2ecf20Sopenharmony_ci	&brport_attr_unicast_flood,
2768c2ecf20Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
2778c2ecf20Sopenharmony_ci	&brport_attr_multicast_router,
2788c2ecf20Sopenharmony_ci	&brport_attr_multicast_fast_leave,
2798c2ecf20Sopenharmony_ci	&brport_attr_multicast_to_unicast,
2808c2ecf20Sopenharmony_ci#endif
2818c2ecf20Sopenharmony_ci	&brport_attr_proxyarp,
2828c2ecf20Sopenharmony_ci	&brport_attr_proxyarp_wifi,
2838c2ecf20Sopenharmony_ci	&brport_attr_multicast_flood,
2848c2ecf20Sopenharmony_ci	&brport_attr_broadcast_flood,
2858c2ecf20Sopenharmony_ci	&brport_attr_group_fwd_mask,
2868c2ecf20Sopenharmony_ci	&brport_attr_neigh_suppress,
2878c2ecf20Sopenharmony_ci	&brport_attr_isolated,
2888c2ecf20Sopenharmony_ci	&brport_attr_backup_port,
2898c2ecf20Sopenharmony_ci	NULL
2908c2ecf20Sopenharmony_ci};
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci#define to_brport_attr(_at) container_of(_at, struct brport_attribute, attr)
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic ssize_t brport_show(struct kobject *kobj,
2958c2ecf20Sopenharmony_ci			   struct attribute *attr, char *buf)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct brport_attribute *brport_attr = to_brport_attr(attr);
2988c2ecf20Sopenharmony_ci	struct net_bridge_port *p = kobj_to_brport(kobj);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (!brport_attr->show)
3018c2ecf20Sopenharmony_ci		return -EINVAL;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return brport_attr->show(p, buf);
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic ssize_t brport_store(struct kobject *kobj,
3078c2ecf20Sopenharmony_ci			    struct attribute *attr,
3088c2ecf20Sopenharmony_ci			    const char *buf, size_t count)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct brport_attribute *brport_attr = to_brport_attr(attr);
3118c2ecf20Sopenharmony_ci	struct net_bridge_port *p = kobj_to_brport(kobj);
3128c2ecf20Sopenharmony_ci	ssize_t ret = -EINVAL;
3138c2ecf20Sopenharmony_ci	unsigned long val;
3148c2ecf20Sopenharmony_ci	char *endp;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (!ns_capable(dev_net(p->dev)->user_ns, CAP_NET_ADMIN))
3178c2ecf20Sopenharmony_ci		return -EPERM;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (!rtnl_trylock())
3208c2ecf20Sopenharmony_ci		return restart_syscall();
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (brport_attr->store_raw) {
3238c2ecf20Sopenharmony_ci		char *buf_copy;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		buf_copy = kstrndup(buf, count, GFP_KERNEL);
3268c2ecf20Sopenharmony_ci		if (!buf_copy) {
3278c2ecf20Sopenharmony_ci			ret = -ENOMEM;
3288c2ecf20Sopenharmony_ci			goto out_unlock;
3298c2ecf20Sopenharmony_ci		}
3308c2ecf20Sopenharmony_ci		spin_lock_bh(&p->br->lock);
3318c2ecf20Sopenharmony_ci		ret = brport_attr->store_raw(p, buf_copy);
3328c2ecf20Sopenharmony_ci		spin_unlock_bh(&p->br->lock);
3338c2ecf20Sopenharmony_ci		kfree(buf_copy);
3348c2ecf20Sopenharmony_ci	} else if (brport_attr->store) {
3358c2ecf20Sopenharmony_ci		val = simple_strtoul(buf, &endp, 0);
3368c2ecf20Sopenharmony_ci		if (endp == buf)
3378c2ecf20Sopenharmony_ci			goto out_unlock;
3388c2ecf20Sopenharmony_ci		spin_lock_bh(&p->br->lock);
3398c2ecf20Sopenharmony_ci		ret = brport_attr->store(p, val);
3408c2ecf20Sopenharmony_ci		spin_unlock_bh(&p->br->lock);
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (!ret) {
3448c2ecf20Sopenharmony_ci		br_ifinfo_notify(RTM_NEWLINK, NULL, p);
3458c2ecf20Sopenharmony_ci		ret = count;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ciout_unlock:
3488c2ecf20Sopenharmony_ci	rtnl_unlock();
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return ret;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ciconst struct sysfs_ops brport_sysfs_ops = {
3548c2ecf20Sopenharmony_ci	.show = brport_show,
3558c2ecf20Sopenharmony_ci	.store = brport_store,
3568c2ecf20Sopenharmony_ci};
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci/*
3598c2ecf20Sopenharmony_ci * Add sysfs entries to ethernet device added to a bridge.
3608c2ecf20Sopenharmony_ci * Creates a brport subdirectory with bridge attributes.
3618c2ecf20Sopenharmony_ci * Puts symlink in bridge's brif subdirectory
3628c2ecf20Sopenharmony_ci */
3638c2ecf20Sopenharmony_ciint br_sysfs_addif(struct net_bridge_port *p)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct net_bridge *br = p->br;
3668c2ecf20Sopenharmony_ci	const struct brport_attribute **a;
3678c2ecf20Sopenharmony_ci	int err;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	err = sysfs_create_link(&p->kobj, &br->dev->dev.kobj,
3708c2ecf20Sopenharmony_ci				SYSFS_BRIDGE_PORT_LINK);
3718c2ecf20Sopenharmony_ci	if (err)
3728c2ecf20Sopenharmony_ci		return err;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	for (a = brport_attrs; *a; ++a) {
3758c2ecf20Sopenharmony_ci		err = sysfs_create_file(&p->kobj, &((*a)->attr));
3768c2ecf20Sopenharmony_ci		if (err)
3778c2ecf20Sopenharmony_ci			return err;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	strlcpy(p->sysfs_name, p->dev->name, IFNAMSIZ);
3818c2ecf20Sopenharmony_ci	return sysfs_create_link(br->ifobj, &p->kobj, p->sysfs_name);
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci/* Rename bridge's brif symlink */
3858c2ecf20Sopenharmony_ciint br_sysfs_renameif(struct net_bridge_port *p)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct net_bridge *br = p->br;
3888c2ecf20Sopenharmony_ci	int err;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* If a rename fails, the rollback will cause another
3918c2ecf20Sopenharmony_ci	 * rename call with the existing name.
3928c2ecf20Sopenharmony_ci	 */
3938c2ecf20Sopenharmony_ci	if (!strncmp(p->sysfs_name, p->dev->name, IFNAMSIZ))
3948c2ecf20Sopenharmony_ci		return 0;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	err = sysfs_rename_link(br->ifobj, &p->kobj,
3978c2ecf20Sopenharmony_ci				p->sysfs_name, p->dev->name);
3988c2ecf20Sopenharmony_ci	if (err)
3998c2ecf20Sopenharmony_ci		netdev_notice(br->dev, "unable to rename link %s to %s",
4008c2ecf20Sopenharmony_ci			      p->sysfs_name, p->dev->name);
4018c2ecf20Sopenharmony_ci	else
4028c2ecf20Sopenharmony_ci		strlcpy(p->sysfs_name, p->dev->name, IFNAMSIZ);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return err;
4058c2ecf20Sopenharmony_ci}
406