|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* Copyright 2022 NXP | 
|  | */ | 
|  | #include <linux/netdevice.h> | 
|  | #include <net/rtnetlink.h> | 
|  |  | 
|  | #include "netlink.h" | 
|  | #include "user.h" | 
|  |  | 
|  | static const struct nla_policy dsa_policy[IFLA_DSA_MAX + 1] = { | 
|  | [IFLA_DSA_CONDUIT]	= { .type = NLA_U32 }, | 
|  | }; | 
|  |  | 
|  | static int dsa_changelink(struct net_device *dev, struct nlattr *tb[], | 
|  | struct nlattr *data[], | 
|  | struct netlink_ext_ack *extack) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | if (!data) | 
|  | return 0; | 
|  |  | 
|  | if (data[IFLA_DSA_CONDUIT]) { | 
|  | u32 ifindex = nla_get_u32(data[IFLA_DSA_CONDUIT]); | 
|  | struct net_device *conduit; | 
|  |  | 
|  | conduit = __dev_get_by_index(dev_net(dev), ifindex); | 
|  | if (!conduit) | 
|  | return -EINVAL; | 
|  |  | 
|  | err = dsa_user_change_conduit(dev, conduit, extack); | 
|  | if (err) | 
|  | return err; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static size_t dsa_get_size(const struct net_device *dev) | 
|  | { | 
|  | return nla_total_size(sizeof(u32)) +	/* IFLA_DSA_CONDUIT  */ | 
|  | 0; | 
|  | } | 
|  |  | 
|  | static int dsa_fill_info(struct sk_buff *skb, const struct net_device *dev) | 
|  | { | 
|  | struct net_device *conduit = dsa_user_to_conduit(dev); | 
|  |  | 
|  | if (nla_put_u32(skb, IFLA_DSA_CONDUIT, conduit->ifindex)) | 
|  | return -EMSGSIZE; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | struct rtnl_link_ops dsa_link_ops __read_mostly = { | 
|  | .kind			= "dsa", | 
|  | .priv_size		= sizeof(struct dsa_port), | 
|  | .maxtype		= IFLA_DSA_MAX, | 
|  | .policy			= dsa_policy, | 
|  | .changelink		= dsa_changelink, | 
|  | .get_size		= dsa_get_size, | 
|  | .fill_info		= dsa_fill_info, | 
|  | .netns_refund		= true, | 
|  | }; |