738 lines · 18.5 KB · C
RawDownload
/* * net/core/devlink.c - Network physical/parent device Netlink interface * * Heavily inspired by net/wireless/ * Copyright (c) 2016 Mellanox Technologies. All rights reserved. * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include <linux/kernel.h>#include <linux/module.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/gfp.h>#include <linux/device.h>#include <linux/list.h>#include <linux/netdevice.h>#include <rdma/ib_verbs.h>#include <net/netlink.h>#include <net/genetlink.h>#include <net/rtnetlink.h>#include <net/net_namespace.h>#include <net/sock.h>#include <net/devlink.h> static LIST_HEAD(devlink_list); /* devlink_mutex * * An overall lock guarding every operation coming from userspace. * It also guards devlink devices list and it is taken when * driver registers/unregisters it. */static DEFINE_MUTEX(devlink_mutex); /* devlink_port_mutex * * Shared lock to guard lists of ports in all devlink devices. */static DEFINE_MUTEX(devlink_port_mutex); static struct net *devlink_net(const struct devlink *devlink){	return read_pnet(&devlink->_net);} static void devlink_net_set(struct devlink *devlink, struct net *net){	write_pnet(&devlink->_net, net);} static struct devlink *devlink_get_from_attrs(struct net *net,					      struct nlattr **attrs){	struct devlink *devlink;	char *busname;	char *devname; 	if (!attrs[DEVLINK_ATTR_BUS_NAME] || !attrs[DEVLINK_ATTR_DEV_NAME])		return ERR_PTR(-EINVAL); 	busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]);	devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]); 	list_for_each_entry(devlink, &devlink_list, list) {		if (strcmp(devlink->dev->bus->name, busname) == 0 &&		    strcmp(dev_name(devlink->dev), devname) == 0 &&		    net_eq(devlink_net(devlink), net))			return devlink;	} 	return ERR_PTR(-ENODEV);} static struct devlink *devlink_get_from_info(struct genl_info *info){	return devlink_get_from_attrs(genl_info_net(info), info->attrs);} static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,						      int port_index){	struct devlink_port *devlink_port; 	list_for_each_entry(devlink_port, &devlink->port_list, list) {		if (devlink_port->index == port_index)			return devlink_port;	}	return NULL;} static bool devlink_port_index_exists(struct devlink *devlink, int port_index){	return devlink_port_get_by_index(devlink, port_index);} static struct devlink_port *devlink_port_get_from_attrs(struct devlink *devlink,							struct nlattr **attrs){	if (attrs[DEVLINK_ATTR_PORT_INDEX]) {		u32 port_index = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);		struct devlink_port *devlink_port; 		devlink_port = devlink_port_get_by_index(devlink, port_index);		if (!devlink_port)			return ERR_PTR(-ENODEV);		return devlink_port;	}	return ERR_PTR(-EINVAL);} static struct devlink_port *devlink_port_get_from_info(struct devlink *devlink,						       struct genl_info *info){	return devlink_port_get_from_attrs(devlink, info->attrs);} #define DEVLINK_NL_FLAG_NEED_PORT	BIT(0) static int devlink_nl_pre_doit(const struct genl_ops *ops,			       struct sk_buff *skb, struct genl_info *info){	struct devlink *devlink; 	mutex_lock(&devlink_mutex);	devlink = devlink_get_from_info(info);	if (IS_ERR(devlink)) {		mutex_unlock(&devlink_mutex);		return PTR_ERR(devlink);	}	info->user_ptr[0] = devlink;	if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) {		struct devlink_port *devlink_port; 		mutex_lock(&devlink_port_mutex);		devlink_port = devlink_port_get_from_info(devlink, info);		if (IS_ERR(devlink_port)) {			mutex_unlock(&devlink_port_mutex);			mutex_unlock(&devlink_mutex);			return PTR_ERR(devlink_port);		}		info->user_ptr[1] = devlink_port;	}	return 0;} static void devlink_nl_post_doit(const struct genl_ops *ops,				 struct sk_buff *skb, struct genl_info *info){	if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT)		mutex_unlock(&devlink_port_mutex);	mutex_unlock(&devlink_mutex);} static struct genl_family devlink_nl_family = {	.id		= GENL_ID_GENERATE,	.name		= DEVLINK_GENL_NAME,	.version	= DEVLINK_GENL_VERSION,	.maxattr	= DEVLINK_ATTR_MAX,	.netnsok	= true,	.pre_doit	= devlink_nl_pre_doit,	.post_doit	= devlink_nl_post_doit,}; enum devlink_multicast_groups {	DEVLINK_MCGRP_CONFIG,}; static const struct genl_multicast_group devlink_nl_mcgrps[] = {	[DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },}; static int devlink_nl_put_handle(struct sk_buff *msg, struct devlink *devlink){	if (nla_put_string(msg, DEVLINK_ATTR_BUS_NAME, devlink->dev->bus->name))		return -EMSGSIZE;	if (nla_put_string(msg, DEVLINK_ATTR_DEV_NAME, dev_name(devlink->dev)))		return -EMSGSIZE;	return 0;} static int devlink_nl_fill(struct sk_buff *msg, struct devlink *devlink,			   enum devlink_command cmd, u32 portid,			   u32 seq, int flags){	void *hdr; 	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);	if (!hdr)		return -EMSGSIZE; 	if (devlink_nl_put_handle(msg, devlink))		goto nla_put_failure; 	genlmsg_end(msg, hdr);	return 0; nla_put_failure:	genlmsg_cancel(msg, hdr);	return -EMSGSIZE;} static void devlink_notify(struct devlink *devlink, enum devlink_command cmd){	struct sk_buff *msg;	int err; 	WARN_ON(cmd != DEVLINK_CMD_NEW && cmd != DEVLINK_CMD_DEL); 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return; 	err = devlink_nl_fill(msg, devlink, cmd, 0, 0, 0);	if (err) {		nlmsg_free(msg);		return;	} 	genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),				msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);} static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink,				struct devlink_port *devlink_port,				enum devlink_command cmd, u32 portid,				u32 seq, int flags){	void *hdr; 	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);	if (!hdr)		return -EMSGSIZE; 	if (devlink_nl_put_handle(msg, devlink))		goto nla_put_failure;	if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))		goto nla_put_failure;	if (nla_put_u16(msg, DEVLINK_ATTR_PORT_TYPE, devlink_port->type))		goto nla_put_failure;	if (devlink_port->desired_type != DEVLINK_PORT_TYPE_NOTSET &&	    nla_put_u16(msg, DEVLINK_ATTR_PORT_DESIRED_TYPE,			devlink_port->desired_type))		goto nla_put_failure;	if (devlink_port->type == DEVLINK_PORT_TYPE_ETH) {		struct net_device *netdev = devlink_port->type_dev; 		if (netdev &&		    (nla_put_u32(msg, DEVLINK_ATTR_PORT_NETDEV_IFINDEX,				 netdev->ifindex) ||		     nla_put_string(msg, DEVLINK_ATTR_PORT_NETDEV_NAME,				    netdev->name)))			goto nla_put_failure;	}	if (devlink_port->type == DEVLINK_PORT_TYPE_IB) {		struct ib_device *ibdev = devlink_port->type_dev; 		if (ibdev &&		    nla_put_string(msg, DEVLINK_ATTR_PORT_IBDEV_NAME,				   ibdev->name))			goto nla_put_failure;	}	if (devlink_port->split &&	    nla_put_u32(msg, DEVLINK_ATTR_PORT_SPLIT_GROUP,			devlink_port->split_group))		goto nla_put_failure; 	genlmsg_end(msg, hdr);	return 0; nla_put_failure:	genlmsg_cancel(msg, hdr);	return -EMSGSIZE;} static void devlink_port_notify(struct devlink_port *devlink_port,				enum devlink_command cmd){	struct devlink *devlink = devlink_port->devlink;	struct sk_buff *msg;	int err; 	if (!devlink_port->registered)		return; 	WARN_ON(cmd != DEVLINK_CMD_PORT_NEW && cmd != DEVLINK_CMD_PORT_DEL); 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return; 	err = devlink_nl_port_fill(msg, devlink, devlink_port, cmd, 0, 0, 0);	if (err) {		nlmsg_free(msg);		return;	} 	genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),				msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);} static int devlink_nl_cmd_get_doit(struct sk_buff *skb, struct genl_info *info){	struct devlink *devlink = info->user_ptr[0];	struct sk_buff *msg;	int err; 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return -ENOMEM; 	err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,			      info->snd_portid, info->snd_seq, 0);	if (err) {		nlmsg_free(msg);		return err;	} 	return genlmsg_reply(msg, info);} static int devlink_nl_cmd_get_dumpit(struct sk_buff *msg,				     struct netlink_callback *cb){	struct devlink *devlink;	int start = cb->args[0];	int idx = 0;	int err; 	mutex_lock(&devlink_mutex);	list_for_each_entry(devlink, &devlink_list, list) {		if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))			continue;		if (idx < start) {			idx++;			continue;		}		err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,				      NETLINK_CB(cb->skb).portid,				      cb->nlh->nlmsg_seq, NLM_F_MULTI);		if (err)			goto out;		idx++;	}out:	mutex_unlock(&devlink_mutex); 	cb->args[0] = idx;	return msg->len;} static int devlink_nl_cmd_port_get_doit(struct sk_buff *skb,					struct genl_info *info){	struct devlink *devlink = info->user_ptr[0];	struct devlink_port *devlink_port = info->user_ptr[1];	struct sk_buff *msg;	int err; 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return -ENOMEM; 	err = devlink_nl_port_fill(msg, devlink, devlink_port,				   DEVLINK_CMD_PORT_NEW,				   info->snd_portid, info->snd_seq, 0);	if (err) {		nlmsg_free(msg);		return err;	} 	return genlmsg_reply(msg, info);} static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg,					  struct netlink_callback *cb){	struct devlink *devlink;	struct devlink_port *devlink_port;	int start = cb->args[0];	int idx = 0;	int err; 	mutex_lock(&devlink_mutex);	mutex_lock(&devlink_port_mutex);	list_for_each_entry(devlink, &devlink_list, list) {		if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))			continue;		list_for_each_entry(devlink_port, &devlink->port_list, list) {			if (idx < start) {				idx++;				continue;			}			err = devlink_nl_port_fill(msg, devlink, devlink_port,						   DEVLINK_CMD_NEW,						   NETLINK_CB(cb->skb).portid,						   cb->nlh->nlmsg_seq,						   NLM_F_MULTI);			if (err)				goto out;			idx++;		}	}out:	mutex_unlock(&devlink_port_mutex);	mutex_unlock(&devlink_mutex); 	cb->args[0] = idx;	return msg->len;} static int devlink_port_type_set(struct devlink *devlink,				 struct devlink_port *devlink_port,				 enum devlink_port_type port_type) {	int err; 	if (devlink->ops && devlink->ops->port_type_set) {		if (port_type == DEVLINK_PORT_TYPE_NOTSET)			return -EINVAL;		err = devlink->ops->port_type_set(devlink_port, port_type);		if (err)			return err;		devlink_port->desired_type = port_type;		devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);		return 0;	}	return -EOPNOTSUPP;} static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb,					struct genl_info *info){	struct devlink *devlink = info->user_ptr[0];	struct devlink_port *devlink_port = info->user_ptr[1];	int err; 	if (info->attrs[DEVLINK_ATTR_PORT_TYPE]) {		enum devlink_port_type port_type; 		port_type = nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_TYPE]);		err = devlink_port_type_set(devlink, devlink_port, port_type);		if (err)			return err;	}	return 0;} static int devlink_port_split(struct devlink *devlink,			      u32 port_index, u32 count) {	if (devlink->ops && devlink->ops->port_split)		return devlink->ops->port_split(devlink, port_index, count);	return -EOPNOTSUPP;} static int devlink_nl_cmd_port_split_doit(struct sk_buff *skb,					  struct genl_info *info){	struct devlink *devlink = info->user_ptr[0];	u32 port_index;	u32 count; 	if (!info->attrs[DEVLINK_ATTR_PORT_INDEX] ||	    !info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT])		return -EINVAL; 	port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);	count = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT]);	return devlink_port_split(devlink, port_index, count);} static int devlink_port_unsplit(struct devlink *devlink, u32 port_index) {	if (devlink->ops && devlink->ops->port_unsplit)		return devlink->ops->port_unsplit(devlink, port_index);	return -EOPNOTSUPP;} static int devlink_nl_cmd_port_unsplit_doit(struct sk_buff *skb,					    struct genl_info *info){	struct devlink *devlink = info->user_ptr[0];	u32 port_index; 	if (!info->attrs[DEVLINK_ATTR_PORT_INDEX])		return -EINVAL; 	port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);	return devlink_port_unsplit(devlink, port_index);} static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {

Lines 1–500 of 738.