Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9771b8cc authored by Justin.Lee1@Dell.com's avatar Justin.Lee1@Dell.com Committed by David S. Miller
Browse files

net/ncsi: Extend NC-SI Netlink interface to allow user space to send NC-SI command



The new command (NCSI_CMD_SEND_CMD) is added to allow user space application
to send NC-SI command to the network card.
Also, add a new attribute (NCSI_ATTR_DATA) for transferring request and response.

The work flow is as below.

Request:
User space application
	-> Netlink interface (msg)
	-> new Netlink handler - ncsi_send_cmd_nl()
	-> ncsi_xmit_cmd()

Response:
Response received - ncsi_rcv_rsp()
	-> internal response handler - ncsi_rsp_handler_xxx()
	-> ncsi_rsp_handler_netlink()
	-> ncsi_send_netlink_rsp ()
	-> Netlink interface (msg)
	-> user space application

Command timeout - ncsi_request_timeout()
	-> ncsi_send_netlink_timeout ()
	-> Netlink interface (msg with zero data length)
	-> user space application

Error:
Error detected
	-> ncsi_send_netlink_err ()
	-> Netlink interface (err msg)
	-> user space application

Signed-off-by: default avatarJustin Lee <justin.lee1@dell.com>
Reviewed-by: default avatarSamuel Mendoza-Jonas <sam@mendozajonas.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6384e483
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@
 *	optionally the preferred NCSI_ATTR_CHANNEL_ID.
 * @NCSI_CMD_CLEAR_INTERFACE: clear any preferred package/channel combination.
 *	Requires NCSI_ATTR_IFINDEX.
 * @NCSI_CMD_SEND_CMD: send NC-SI command to network card.
 *	Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID
 *	and NCSI_ATTR_CHANNEL_ID.
 * @NCSI_CMD_MAX: highest command number
 */
enum ncsi_nl_commands {
@@ -30,6 +33,7 @@ enum ncsi_nl_commands {
	NCSI_CMD_PKG_INFO,
	NCSI_CMD_SET_INTERFACE,
	NCSI_CMD_CLEAR_INTERFACE,
	NCSI_CMD_SEND_CMD,

	__NCSI_CMD_AFTER_LAST,
	NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1
@@ -43,6 +47,7 @@ enum ncsi_nl_commands {
 * @NCSI_ATTR_PACKAGE_LIST: nested array of NCSI_PKG_ATTR attributes
 * @NCSI_ATTR_PACKAGE_ID: package ID
 * @NCSI_ATTR_CHANNEL_ID: channel ID
 * @NCSI_ATTR_DATA: command payload
 * @NCSI_ATTR_MAX: highest attribute number
 */
enum ncsi_nl_attrs {
@@ -51,6 +56,7 @@ enum ncsi_nl_attrs {
	NCSI_ATTR_PACKAGE_LIST,
	NCSI_ATTR_PACKAGE_ID,
	NCSI_ATTR_CHANNEL_ID,
	NCSI_ATTR_DATA,

	__NCSI_ATTR_AFTER_LAST,
	NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1
+7 −0
Original line number Diff line number Diff line
@@ -175,6 +175,8 @@ struct ncsi_package;
#define NCSI_RESERVED_CHANNEL	0x1f
#define NCSI_CHANNEL_INDEX(c)	((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1))
#define NCSI_TO_CHANNEL(p, c)	(((p) << NCSI_PACKAGE_SHIFT) | (c))
#define NCSI_MAX_PACKAGE	8
#define NCSI_MAX_CHANNEL	32

struct ncsi_channel {
	unsigned char               id;
@@ -220,11 +222,15 @@ struct ncsi_request {
	bool                 used;    /* Request that has been assigned  */
	unsigned int         flags;   /* NCSI request property           */
#define NCSI_REQ_FLAG_EVENT_DRIVEN	1
#define NCSI_REQ_FLAG_NETLINK_DRIVEN	2
	struct ncsi_dev_priv *ndp;    /* Associated NCSI device          */
	struct sk_buff       *cmd;    /* Associated NCSI command packet  */
	struct sk_buff       *rsp;    /* Associated NCSI response packet */
	struct timer_list    timer;   /* Timer on waiting for response   */
	bool                 enabled; /* Time has been enabled or not    */
	u32                  snd_seq;     /* netlink sending sequence number */
	u32                  snd_portid;  /* netlink portid of sender        */
	struct nlmsghdr      nlhdr;       /* netlink message header          */
};

enum {
@@ -310,6 +316,7 @@ struct ncsi_cmd_arg {
		unsigned int   dwords[4];
	};
	unsigned char        *data;       /* NCSI OEM data                 */
	struct genl_info     *info;       /* Netlink information           */
};

extern struct list_head ncsi_dev_list;
+8 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <net/ncsi.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/genetlink.h>

#include "internal.h"
#include "ncsi-pkt.h"
@@ -346,6 +347,13 @@ int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
	if (!nr)
		return -ENOMEM;

	/* track netlink information */
	if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
		nr->snd_seq = nca->info->snd_seq;
		nr->snd_portid = nca->info->snd_portid;
		nr->nlhdr = *nca->info->nlhdr;
	}

	/* Prepare the packet */
	nca->id = nr->id;
	ret = nch->handler(nr->cmd, nca);
+16 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <net/addrconf.h>
#include <net/ipv6.h>
#include <net/if_inet6.h>
#include <net/genetlink.h>

#include "internal.h"
#include "ncsi-pkt.h"
@@ -406,6 +407,9 @@ static void ncsi_request_timeout(struct timer_list *t)
{
	struct ncsi_request *nr = from_timer(nr, t, timer);
	struct ncsi_dev_priv *ndp = nr->ndp;
	struct ncsi_cmd_pkt *cmd;
	struct ncsi_package *np;
	struct ncsi_channel *nc;
	unsigned long flags;

	/* If the request already had associated response,
@@ -419,6 +423,18 @@ static void ncsi_request_timeout(struct timer_list *t)
	}
	spin_unlock_irqrestore(&ndp->lock, flags);

	if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
		if (nr->cmd) {
			/* Find the package */
			cmd = (struct ncsi_cmd_pkt *)
			      skb_network_header(nr->cmd);
			ncsi_find_package_and_channel(ndp,
						      cmd->cmd.common.channel,
						      &np, &nc);
			ncsi_send_netlink_timeout(nr, np, nc);
		}
	}

	/* Release the request */
	ncsi_free_request(nr);
}
+204 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <uapi/linux/ncsi.h>

#include "internal.h"
#include "ncsi-pkt.h"
#include "ncsi-netlink.h"

static struct genl_family ncsi_genl_family;
@@ -28,6 +29,7 @@ static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
	[NCSI_ATTR_PACKAGE_LIST] =	{ .type = NLA_NESTED },
	[NCSI_ATTR_PACKAGE_ID] =	{ .type = NLA_U32 },
	[NCSI_ATTR_CHANNEL_ID] =	{ .type = NLA_U32 },
	[NCSI_ATTR_DATA] =		{ .type = NLA_BINARY, .len = 2048 },
};

static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
@@ -365,6 +367,202 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
	return 0;
}

static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info)
{
	struct ncsi_dev_priv *ndp;
	struct ncsi_pkt_hdr *hdr;
	struct ncsi_cmd_arg nca;
	unsigned char *data;
	u32 package_id;
	u32 channel_id;
	int len, ret;

	if (!info || !info->attrs) {
		ret = -EINVAL;
		goto out;
	}

	if (!info->attrs[NCSI_ATTR_IFINDEX]) {
		ret = -EINVAL;
		goto out;
	}

	if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) {
		ret = -EINVAL;
		goto out;
	}

	if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
		ret = -EINVAL;
		goto out;
	}

	if (!info->attrs[NCSI_ATTR_DATA]) {
		ret = -EINVAL;
		goto out;
	}

	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
	if (!ndp) {
		ret = -ENODEV;
		goto out;
	}

	package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
	channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);

	if (package_id >= NCSI_MAX_PACKAGE || channel_id >= NCSI_MAX_CHANNEL) {
		ret = -ERANGE;
		goto out_netlink;
	}

	len = nla_len(info->attrs[NCSI_ATTR_DATA]);
	if (len < sizeof(struct ncsi_pkt_hdr)) {
		netdev_info(ndp->ndev.dev, "NCSI: no command to send %u\n",
			    package_id);
		ret = -EINVAL;
		goto out_netlink;
	} else {
		data = (unsigned char *)nla_data(info->attrs[NCSI_ATTR_DATA]);
	}

	hdr = (struct ncsi_pkt_hdr *)data;

	nca.ndp = ndp;
	nca.package = (unsigned char)package_id;
	nca.channel = (unsigned char)channel_id;
	nca.type = hdr->type;
	nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN;
	nca.info = info;
	nca.payload = ntohs(hdr->length);
	nca.data = data + sizeof(*hdr);

	ret = ncsi_xmit_cmd(&nca);
out_netlink:
	if (ret != 0) {
		netdev_err(ndp->ndev.dev,
			   "NCSI: Error %d sending command\n",
			   ret);
		ncsi_send_netlink_err(ndp->ndev.dev,
				      info->snd_seq,
				      info->snd_portid,
				      info->nlhdr,
				      ret);
	}
out:
	return ret;
}

int ncsi_send_netlink_rsp(struct ncsi_request *nr,
			  struct ncsi_package *np,
			  struct ncsi_channel *nc)
{
	struct sk_buff *skb;
	struct net *net;
	void *hdr;
	int rc;

	net = dev_net(nr->rsp->dev);

	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
	if (!skb)
		return -ENOMEM;

	hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq,
			  &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD);
	if (!hdr) {
		kfree_skb(skb);
		return -EMSGSIZE;
	}

	nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->rsp->dev->ifindex);
	if (np)
		nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id);
	if (nc)
		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id);
	else
		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL);

	rc = nla_put(skb, NCSI_ATTR_DATA, nr->rsp->len, (void *)nr->rsp->data);
	if (rc)
		goto err;

	genlmsg_end(skb, hdr);
	return genlmsg_unicast(net, skb, nr->snd_portid);

err:
	kfree_skb(skb);
	return rc;
}

int ncsi_send_netlink_timeout(struct ncsi_request *nr,
			      struct ncsi_package *np,
			      struct ncsi_channel *nc)
{
	struct sk_buff *skb;
	struct net *net;
	void *hdr;

	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
	if (!skb)
		return -ENOMEM;

	hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq,
			  &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD);
	if (!hdr) {
		kfree_skb(skb);
		return -EMSGSIZE;
	}

	net = dev_net(nr->cmd->dev);

	nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->cmd->dev->ifindex);

	if (np)
		nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id);
	else
		nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID,
			    NCSI_PACKAGE_INDEX((((struct ncsi_pkt_hdr *)
						 nr->cmd->data)->channel)));

	if (nc)
		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id);
	else
		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL);

	genlmsg_end(skb, hdr);
	return genlmsg_unicast(net, skb, nr->snd_portid);
}

int ncsi_send_netlink_err(struct net_device *dev,
			  u32 snd_seq,
			  u32 snd_portid,
			  struct nlmsghdr *nlhdr,
			  int err)
{
	struct nlmsghdr *nlh;
	struct nlmsgerr *nle;
	struct sk_buff *skb;
	struct net *net;

	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
	if (!skb)
		return -ENOMEM;

	net = dev_net(dev);

	nlh = nlmsg_put(skb, snd_portid, snd_seq,
			NLMSG_ERROR, sizeof(*nle), 0);
	nle = (struct nlmsgerr *)nlmsg_data(nlh);
	nle->error = err;
	memcpy(&nle->msg, nlhdr, sizeof(*nlh));

	nlmsg_end(skb, nlh);

	return nlmsg_unicast(net->genl_sock, skb, snd_portid);
}

static const struct genl_ops ncsi_ops[] = {
	{
		.cmd = NCSI_CMD_PKG_INFO,
@@ -385,6 +583,12 @@ static const struct genl_ops ncsi_ops[] = {
		.doit = ncsi_clear_interface_nl,
		.flags = GENL_ADMIN_PERM,
	},
	{
		.cmd = NCSI_CMD_SEND_CMD,
		.policy = ncsi_genl_policy,
		.doit = ncsi_send_cmd_nl,
		.flags = GENL_ADMIN_PERM,
	},
};

static struct genl_family ncsi_genl_family __ro_after_init = {
Loading