2019-05-27 09:55:01 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2018-03-05 03:39:05 +03:00
|
|
|
/*
|
|
|
|
* Copyright Samuel Mendoza-Jonas, IBM Corporation 2018.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <net/genetlink.h>
|
|
|
|
#include <net/ncsi.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <net/sock.h>
|
|
|
|
#include <uapi/linux/ncsi.h>
|
|
|
|
|
|
|
|
#include "internal.h"
|
2018-10-11 21:07:37 +03:00
|
|
|
#include "ncsi-pkt.h"
|
2018-03-05 03:39:05 +03:00
|
|
|
#include "ncsi-netlink.h"
|
|
|
|
|
|
|
|
static struct genl_family ncsi_genl_family;
|
|
|
|
|
|
|
|
static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
|
|
|
|
[NCSI_ATTR_IFINDEX] = { .type = NLA_U32 },
|
|
|
|
[NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED },
|
|
|
|
[NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 },
|
|
|
|
[NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 },
|
2018-10-11 21:07:37 +03:00
|
|
|
[NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 },
|
2018-11-16 07:51:59 +03:00
|
|
|
[NCSI_ATTR_MULTI_FLAG] = { .type = NLA_FLAG },
|
|
|
|
[NCSI_ATTR_PACKAGE_MASK] = { .type = NLA_U32 },
|
|
|
|
[NCSI_ATTR_CHANNEL_MASK] = { .type = NLA_U32 },
|
2018-03-05 03:39:05 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct ncsi_dev *nd;
|
|
|
|
struct ncsi_dev;
|
|
|
|
|
|
|
|
if (!net)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
dev = dev_get_by_index(net, ifindex);
|
|
|
|
if (!dev) {
|
|
|
|
pr_err("NCSI netlink: No device for ifindex %u\n", ifindex);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd = ncsi_find_dev(dev);
|
|
|
|
ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
|
|
|
|
|
|
|
|
dev_put(dev);
|
|
|
|
return ndp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ncsi_write_channel_info(struct sk_buff *skb,
|
|
|
|
struct ncsi_dev_priv *ndp,
|
|
|
|
struct ncsi_channel *nc)
|
|
|
|
{
|
2018-04-17 07:23:23 +03:00
|
|
|
struct ncsi_channel_vlan_filter *ncf;
|
2018-03-05 03:39:05 +03:00
|
|
|
struct ncsi_channel_mode *m;
|
2018-04-17 07:23:23 +03:00
|
|
|
struct nlattr *vid_nest;
|
2018-03-05 03:39:05 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id);
|
|
|
|
m = &nc->modes[NCSI_MODE_LINK];
|
|
|
|
nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]);
|
|
|
|
if (nc->state == NCSI_CHANNEL_ACTIVE)
|
|
|
|
nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE);
|
2018-11-16 07:51:59 +03:00
|
|
|
if (nc == nc->package->preferred_channel)
|
2018-03-05 03:39:05 +03:00
|
|
|
nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
|
|
|
|
|
net/ncsi: Fix netlink major/minor version numbers
[ Upstream commit 3084b58bfd0b9e4b5e034f31f31b42977db35f12 ]
The netlink interface for major and minor version numbers doesn't actually
return the major and minor version numbers.
It reports a u32 that contains the (major, minor, update, alpha1)
components as the major version number, and then alpha2 as the minor
version number.
For whatever reason, the u32 byte order was reversed (ntohl): maybe it was
assumed that the encoded value was a single big-endian u32, and alpha2 was
the minor version.
The correct way to get the supported NC-SI version from the network
controller is to parse the Get Version ID response as described in 8.4.44
of the NC-SI spec[1].
Get Version ID Response Packet Format
Bits
+--------+--------+--------+--------+
Bytes | 31..24 | 23..16 | 15..8 | 7..0 |
+-------+--------+--------+--------+--------+
| 0..15 | NC-SI Header |
+-------+--------+--------+--------+--------+
| 16..19| Response code | Reason code |
+-------+--------+--------+--------+--------+
|20..23 | Major | Minor | Update | Alpha1 |
+-------+--------+--------+--------+--------+
|24..27 | reserved | Alpha2 |
+-------+--------+--------+--------+--------+
| .... other stuff .... |
The major, minor, and update fields are all binary-coded decimal (BCD)
encoded [2]. The spec provides examples below the Get Version ID response
format in section 8.4.44.1, but for practical purposes, this is an example
from a live network card:
root@bmc:~# ncsi-util 0x15
NC-SI Command Response:
cmd: GET_VERSION_ID(0x15)
Response: COMMAND_COMPLETED(0x0000) Reason: NO_ERROR(0x0000)
Payload length = 40
20: 0xf1 0xf1 0xf0 0x00 <<<<<<<<< (major, minor, update, alpha1)
24: 0x00 0x00 0x00 0x00 <<<<<<<<< (_, _, _, alpha2)
28: 0x6d 0x6c 0x78 0x30
32: 0x2e 0x31 0x00 0x00
36: 0x00 0x00 0x00 0x00
40: 0x16 0x1d 0x07 0xd2
44: 0x10 0x1d 0x15 0xb3
48: 0x00 0x17 0x15 0xb3
52: 0x00 0x00 0x81 0x19
This should be parsed as "1.1.0".
"f" in the upper-nibble means to ignore it, contributing zero.
If both nibbles are "f", I think the whole field is supposed to be ignored.
Major and minor are "required", meaning they're not supposed to be "ff",
but the update field is "optional" so I think it can be ff. I think the
simplest thing to do is just set the major and minor to zero instead of
juggling some conditional logic or something.
bcd2bin() from "include/linux/bcd.h" seems to assume both nibbles are 0-9,
so I've provided a custom BCD decoding function.
Alpha1 and alpha2 are ISO/IEC 8859-1 encoded, which just means ASCII
characters as far as I can tell, although the full encoding table for
non-alphabetic characters is slightly different (I think).
I imagine the alpha fields are just supposed to be alphabetic characters,
but I haven't seen any network cards actually report a non-zero value for
either.
If people wrote software against this netlink behavior, and were parsing
the major and minor versions themselves from the u32, then this would
definitely break their code.
[1] https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.0.0.pdf
[2] https://en.wikipedia.org/wiki/Binary-coded_decimal
[2] https://en.wikipedia.org/wiki/ISO/IEC_8859-1
Signed-off-by: Peter Delevoryas <peter@pjd.dev>
Fixes: 138635cc27c9 ("net/ncsi: NCSI response packet handler")
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2023-11-14 19:07:34 +03:00
|
|
|
nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.major);
|
|
|
|
nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.minor);
|
2018-03-05 03:39:05 +03:00
|
|
|
nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name);
|
|
|
|
|
2019-04-26 12:13:06 +03:00
|
|
|
vid_nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR_VLAN_LIST);
|
2018-03-05 03:39:05 +03:00
|
|
|
if (!vid_nest)
|
|
|
|
return -ENOMEM;
|
2018-04-17 07:23:23 +03:00
|
|
|
ncf = &nc->vlan_filter;
|
2018-03-05 03:39:05 +03:00
|
|
|
i = -1;
|
2018-04-17 07:23:23 +03:00
|
|
|
while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids,
|
|
|
|
i + 1)) < ncf->n_vids) {
|
|
|
|
if (ncf->vids[i])
|
2018-03-05 03:39:05 +03:00
|
|
|
nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID,
|
2018-04-17 07:23:23 +03:00
|
|
|
ncf->vids[i]);
|
2018-03-05 03:39:05 +03:00
|
|
|
}
|
|
|
|
nla_nest_end(skb, vid_nest);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ncsi_write_package_info(struct sk_buff *skb,
|
|
|
|
struct ncsi_dev_priv *ndp, unsigned int id)
|
|
|
|
{
|
|
|
|
struct nlattr *pnest, *cnest, *nest;
|
|
|
|
struct ncsi_package *np;
|
|
|
|
struct ncsi_channel *nc;
|
|
|
|
bool found;
|
|
|
|
int rc;
|
|
|
|
|
2018-08-22 07:57:44 +03:00
|
|
|
if (id > ndp->package_num - 1) {
|
2018-03-05 03:39:05 +03:00
|
|
|
netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
found = false;
|
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np) {
|
|
|
|
if (np->id != id)
|
|
|
|
continue;
|
2019-04-26 12:13:06 +03:00
|
|
|
pnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR);
|
2018-03-05 03:39:05 +03:00
|
|
|
if (!pnest)
|
|
|
|
return -ENOMEM;
|
2021-12-29 06:21:18 +03:00
|
|
|
rc = nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
|
|
|
|
if (rc) {
|
|
|
|
nla_nest_cancel(skb, pnest);
|
|
|
|
return rc;
|
|
|
|
}
|
2018-11-16 07:51:59 +03:00
|
|
|
if ((0x1 << np->id) == ndp->package_whitelist)
|
2018-03-05 03:39:05 +03:00
|
|
|
nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
|
2019-04-26 12:13:06 +03:00
|
|
|
cnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
|
2018-03-05 03:39:05 +03:00
|
|
|
if (!cnest) {
|
|
|
|
nla_nest_cancel(skb, pnest);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
2019-04-26 12:13:06 +03:00
|
|
|
nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR);
|
2018-03-05 03:39:05 +03:00
|
|
|
if (!nest) {
|
|
|
|
nla_nest_cancel(skb, cnest);
|
|
|
|
nla_nest_cancel(skb, pnest);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
rc = ncsi_write_channel_info(skb, ndp, nc);
|
|
|
|
if (rc) {
|
|
|
|
nla_nest_cancel(skb, nest);
|
|
|
|
nla_nest_cancel(skb, cnest);
|
|
|
|
nla_nest_cancel(skb, pnest);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
nla_nest_end(skb, nest);
|
|
|
|
}
|
|
|
|
nla_nest_end(skb, cnest);
|
|
|
|
nla_nest_end(skb, pnest);
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp;
|
|
|
|
unsigned int package_id;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct nlattr *attr;
|
|
|
|
void *hdr;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!info || !info->attrs)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NCSI_ATTR_IFINDEX])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ndp = ndp_from_ifindex(genl_info_net(info),
|
|
|
|
nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
|
|
|
|
if (!ndp)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!skb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
|
|
|
|
&ncsi_genl_family, 0, NCSI_CMD_PKG_INFO);
|
|
|
|
if (!hdr) {
|
2018-03-08 12:36:04 +03:00
|
|
|
kfree_skb(skb);
|
2018-03-05 03:39:05 +03:00
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
|
|
|
|
|
2019-04-26 12:13:06 +03:00
|
|
|
attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST);
|
2018-03-26 14:27:12 +03:00
|
|
|
if (!attr) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
2018-03-05 03:39:05 +03:00
|
|
|
rc = ncsi_write_package_info(skb, ndp, package_id);
|
|
|
|
|
|
|
|
if (rc) {
|
|
|
|
nla_nest_cancel(skb, attr);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(skb, attr);
|
|
|
|
|
|
|
|
genlmsg_end(skb, hdr);
|
|
|
|
return genlmsg_reply(skb, info);
|
|
|
|
|
|
|
|
err:
|
2018-03-08 12:36:04 +03:00
|
|
|
kfree_skb(skb);
|
2018-03-05 03:39:05 +03:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ncsi_pkg_info_all_nl(struct sk_buff *skb,
|
|
|
|
struct netlink_callback *cb)
|
|
|
|
{
|
2018-05-31 07:10:04 +03:00
|
|
|
struct nlattr *attrs[NCSI_ATTR_MAX + 1];
|
2018-03-05 03:39:05 +03:00
|
|
|
struct ncsi_package *np, *package;
|
|
|
|
struct ncsi_dev_priv *ndp;
|
|
|
|
unsigned int package_id;
|
|
|
|
struct nlattr *attr;
|
|
|
|
void *hdr;
|
|
|
|
int rc;
|
|
|
|
|
netlink: make validation more configurable for future strictness
We currently have two levels of strict validation:
1) liberal (default)
- undefined (type >= max) & NLA_UNSPEC attributes accepted
- attribute length >= expected accepted
- garbage at end of message accepted
2) strict (opt-in)
- NLA_UNSPEC attributes accepted
- attribute length >= expected accepted
Split out parsing strictness into four different options:
* TRAILING - check that there's no trailing data after parsing
attributes (in message or nested)
* MAXTYPE - reject attrs > max known type
* UNSPEC - reject attributes with NLA_UNSPEC policy entries
* STRICT_ATTRS - strictly validate attribute size
The default for future things should be *everything*.
The current *_strict() is a combination of TRAILING and MAXTYPE,
and is renamed to _deprecated_strict().
The current regular parsing has none of this, and is renamed to
*_parse_deprecated().
Additionally it allows us to selectively set one of the new flags
even on old policies. Notably, the UNSPEC flag could be useful in
this case, since it can be arranged (by filling in the policy) to
not be an incompatible userspace ABI change, but would then going
forward prevent forgetting attribute entries. Similar can apply
to the POLICY flag.
We end up with the following renames:
* nla_parse -> nla_parse_deprecated
* nla_parse_strict -> nla_parse_deprecated_strict
* nlmsg_parse -> nlmsg_parse_deprecated
* nlmsg_parse_strict -> nlmsg_parse_deprecated_strict
* nla_parse_nested -> nla_parse_nested_deprecated
* nla_validate_nested -> nla_validate_nested_deprecated
Using spatch, of course:
@@
expression TB, MAX, HEAD, LEN, POL, EXT;
@@
-nla_parse(TB, MAX, HEAD, LEN, POL, EXT)
+nla_parse_deprecated(TB, MAX, HEAD, LEN, POL, EXT)
@@
expression NLH, HDRLEN, TB, MAX, POL, EXT;
@@
-nlmsg_parse(NLH, HDRLEN, TB, MAX, POL, EXT)
+nlmsg_parse_deprecated(NLH, HDRLEN, TB, MAX, POL, EXT)
@@
expression NLH, HDRLEN, TB, MAX, POL, EXT;
@@
-nlmsg_parse_strict(NLH, HDRLEN, TB, MAX, POL, EXT)
+nlmsg_parse_deprecated_strict(NLH, HDRLEN, TB, MAX, POL, EXT)
@@
expression TB, MAX, NLA, POL, EXT;
@@
-nla_parse_nested(TB, MAX, NLA, POL, EXT)
+nla_parse_nested_deprecated(TB, MAX, NLA, POL, EXT)
@@
expression START, MAX, POL, EXT;
@@
-nla_validate_nested(START, MAX, POL, EXT)
+nla_validate_nested_deprecated(START, MAX, POL, EXT)
@@
expression NLH, HDRLEN, MAX, POL, EXT;
@@
-nlmsg_validate(NLH, HDRLEN, MAX, POL, EXT)
+nlmsg_validate_deprecated(NLH, HDRLEN, MAX, POL, EXT)
For this patch, don't actually add the strict, non-renamed versions
yet so that it breaks compile if I get it wrong.
Also, while at it, make nla_validate and nla_parse go down to a
common __nla_validate_parse() function to avoid code duplication.
Ultimately, this allows us to have very strict validation for every
new caller of nla_parse()/nlmsg_parse() etc as re-introduced in the
next patch, while existing things will continue to work as is.
In effect then, this adds fully strict validation for any new command.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-04-26 15:07:28 +03:00
|
|
|
rc = genlmsg_parse_deprecated(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX,
|
|
|
|
ncsi_genl_policy, NULL);
|
2018-03-05 03:39:05 +03:00
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (!attrs[NCSI_ATTR_IFINDEX])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)),
|
|
|
|
nla_get_u32(attrs[NCSI_ATTR_IFINDEX]));
|
|
|
|
|
|
|
|
if (!ndp)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
package_id = cb->args[0];
|
|
|
|
package = NULL;
|
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np)
|
|
|
|
if (np->id == package_id)
|
|
|
|
package = np;
|
|
|
|
|
|
|
|
if (!package)
|
|
|
|
return 0; /* done */
|
|
|
|
|
|
|
|
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
|
2018-08-22 07:57:44 +03:00
|
|
|
&ncsi_genl_family, NLM_F_MULTI, NCSI_CMD_PKG_INFO);
|
2018-03-05 03:39:05 +03:00
|
|
|
if (!hdr) {
|
|
|
|
rc = -EMSGSIZE;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2019-04-26 12:13:06 +03:00
|
|
|
attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST);
|
2019-03-15 09:14:33 +03:00
|
|
|
if (!attr) {
|
|
|
|
rc = -EMSGSIZE;
|
|
|
|
goto err;
|
|
|
|
}
|
2018-03-05 03:39:05 +03:00
|
|
|
rc = ncsi_write_package_info(skb, ndp, package->id);
|
|
|
|
if (rc) {
|
|
|
|
nla_nest_cancel(skb, attr);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(skb, attr);
|
|
|
|
genlmsg_end(skb, hdr);
|
|
|
|
|
|
|
|
cb->args[0] = package_id + 1;
|
|
|
|
|
|
|
|
return skb->len;
|
|
|
|
err:
|
|
|
|
genlmsg_cancel(skb, hdr);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct ncsi_package *np, *package;
|
|
|
|
struct ncsi_channel *nc, *channel;
|
|
|
|
u32 package_id, channel_id;
|
|
|
|
struct ncsi_dev_priv *ndp;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (!info || !info->attrs)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NCSI_ATTR_IFINDEX])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
|
|
|
|
nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
|
|
|
|
if (!ndp)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
|
|
|
|
package = NULL;
|
|
|
|
|
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np)
|
|
|
|
if (np->id == package_id)
|
|
|
|
package = np;
|
|
|
|
if (!package) {
|
|
|
|
/* The user has set a package that does not exist */
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
channel = NULL;
|
2018-11-16 07:51:59 +03:00
|
|
|
if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
|
2018-03-05 03:39:05 +03:00
|
|
|
channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
|
|
|
|
NCSI_FOR_EACH_CHANNEL(package, nc)
|
2018-11-16 07:51:59 +03:00
|
|
|
if (nc->id == channel_id) {
|
2018-03-05 03:39:05 +03:00
|
|
|
channel = nc;
|
2018-11-16 07:51:59 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!channel) {
|
|
|
|
netdev_info(ndp->ndev.dev,
|
|
|
|
"NCSI: Channel %u does not exist!\n",
|
|
|
|
channel_id);
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
2018-03-05 03:39:05 +03:00
|
|
|
}
|
|
|
|
|
2018-11-16 07:51:59 +03:00
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
ndp->package_whitelist = 0x1 << package->id;
|
|
|
|
ndp->multi_package = false;
|
2018-03-05 03:39:05 +03:00
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
2018-11-16 07:51:59 +03:00
|
|
|
spin_lock_irqsave(&package->lock, flags);
|
|
|
|
package->multi_channel = false;
|
|
|
|
if (channel) {
|
|
|
|
package->channel_whitelist = 0x1 << channel->id;
|
|
|
|
package->preferred_channel = channel;
|
|
|
|
} else {
|
|
|
|
/* Allow any channel */
|
|
|
|
package->channel_whitelist = UINT_MAX;
|
|
|
|
package->preferred_channel = NULL;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&package->lock, flags);
|
|
|
|
|
|
|
|
if (channel)
|
|
|
|
netdev_info(ndp->ndev.dev,
|
|
|
|
"Set package 0x%x, channel 0x%x as preferred\n",
|
|
|
|
package_id, channel_id);
|
|
|
|
else
|
|
|
|
netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n",
|
|
|
|
package_id);
|
2018-03-05 03:39:05 +03:00
|
|
|
|
2018-11-16 07:51:58 +03:00
|
|
|
/* Update channel configuration */
|
|
|
|
if (!(ndp->flags & NCSI_DEV_RESET))
|
|
|
|
ncsi_reset_dev(&ndp->ndev);
|
2018-03-05 03:39:05 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp;
|
2018-11-16 07:51:59 +03:00
|
|
|
struct ncsi_package *np;
|
2018-03-05 03:39:05 +03:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (!info || !info->attrs)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NCSI_ATTR_IFINDEX])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
|
|
|
|
nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
|
|
|
|
if (!ndp)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2018-11-16 07:51:59 +03:00
|
|
|
/* Reset any whitelists and disable multi mode */
|
2018-03-05 03:39:05 +03:00
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
2018-11-16 07:51:59 +03:00
|
|
|
ndp->package_whitelist = UINT_MAX;
|
|
|
|
ndp->multi_package = false;
|
2018-03-05 03:39:05 +03:00
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
2018-11-16 07:51:59 +03:00
|
|
|
|
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np) {
|
|
|
|
spin_lock_irqsave(&np->lock, flags);
|
|
|
|
np->multi_channel = false;
|
|
|
|
np->channel_whitelist = UINT_MAX;
|
|
|
|
np->preferred_channel = NULL;
|
|
|
|
spin_unlock_irqrestore(&np->lock, flags);
|
|
|
|
}
|
2018-03-05 03:39:05 +03:00
|
|
|
netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
|
|
|
|
|
2018-11-16 07:51:58 +03:00
|
|
|
/* Update channel configuration */
|
|
|
|
if (!(ndp->flags & NCSI_DEV_RESET))
|
|
|
|
ncsi_reset_dev(&ndp->ndev);
|
2018-03-05 03:39:05 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-11 21:07:37 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-11-16 07:51:59 +03:00
|
|
|
static int ncsi_set_package_mask_nl(struct sk_buff *msg,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct ncsi_dev_priv *ndp;
|
|
|
|
unsigned long flags;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!info || !info->attrs)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NCSI_ATTR_IFINDEX])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NCSI_ATTR_PACKAGE_MASK])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
|
|
|
|
nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
|
|
|
|
if (!ndp)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
|
|
if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
|
|
|
|
if (ndp->flags & NCSI_DEV_HWA) {
|
|
|
|
ndp->multi_package = true;
|
|
|
|
rc = 0;
|
|
|
|
} else {
|
|
|
|
netdev_err(ndp->ndev.dev,
|
|
|
|
"NCSI: Can't use multiple packages without HWA\n");
|
|
|
|
rc = -EPERM;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ndp->multi_package = false;
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!rc)
|
|
|
|
ndp->package_whitelist =
|
|
|
|
nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]);
|
|
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
|
|
|
|
|
|
if (!rc) {
|
|
|
|
/* Update channel configuration */
|
|
|
|
if (!(ndp->flags & NCSI_DEV_RESET))
|
|
|
|
ncsi_reset_dev(&ndp->ndev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ncsi_set_channel_mask_nl(struct sk_buff *msg,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct ncsi_package *np, *package;
|
|
|
|
struct ncsi_channel *nc, *channel;
|
|
|
|
u32 package_id, channel_id;
|
|
|
|
struct ncsi_dev_priv *ndp;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (!info || !info->attrs)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NCSI_ATTR_IFINDEX])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NCSI_ATTR_CHANNEL_MASK])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
|
|
|
|
nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
|
|
|
|
if (!ndp)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
|
|
|
|
package = NULL;
|
|
|
|
NCSI_FOR_EACH_PACKAGE(ndp, np)
|
|
|
|
if (np->id == package_id) {
|
|
|
|
package = np;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!package)
|
|
|
|
return -ERANGE;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&package->lock, flags);
|
|
|
|
|
|
|
|
channel = NULL;
|
|
|
|
if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
|
|
|
|
channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
|
|
|
|
NCSI_FOR_EACH_CHANNEL(np, nc)
|
|
|
|
if (nc->id == channel_id) {
|
|
|
|
channel = nc;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!channel) {
|
|
|
|
spin_unlock_irqrestore(&package->lock, flags);
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
netdev_dbg(ndp->ndev.dev,
|
|
|
|
"NCSI: Channel %u set as preferred channel\n",
|
|
|
|
channel->id);
|
|
|
|
}
|
|
|
|
|
|
|
|
package->channel_whitelist =
|
|
|
|
nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]);
|
|
|
|
if (package->channel_whitelist == 0)
|
|
|
|
netdev_dbg(ndp->ndev.dev,
|
|
|
|
"NCSI: Package %u set to all channels disabled\n",
|
|
|
|
package->id);
|
|
|
|
|
|
|
|
package->preferred_channel = channel;
|
|
|
|
|
|
|
|
if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
|
|
|
|
package->multi_channel = true;
|
|
|
|
netdev_info(ndp->ndev.dev,
|
|
|
|
"NCSI: Multi-channel enabled on package %u\n",
|
|
|
|
package_id);
|
|
|
|
} else {
|
|
|
|
package->multi_channel = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&package->lock, flags);
|
|
|
|
|
|
|
|
/* Update channel configuration */
|
|
|
|
if (!(ndp->flags & NCSI_DEV_RESET))
|
|
|
|
ncsi_reset_dev(&ndp->ndev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-03 00:49:54 +03:00
|
|
|
static const struct genl_small_ops ncsi_ops[] = {
|
2018-03-05 03:39:05 +03:00
|
|
|
{
|
|
|
|
.cmd = NCSI_CMD_PKG_INFO,
|
2019-04-26 15:07:31 +03:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2018-03-05 03:39:05 +03:00
|
|
|
.doit = ncsi_pkg_info_nl,
|
|
|
|
.dumpit = ncsi_pkg_info_all_nl,
|
|
|
|
.flags = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NCSI_CMD_SET_INTERFACE,
|
2019-04-26 15:07:31 +03:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2018-03-05 03:39:05 +03:00
|
|
|
.doit = ncsi_set_interface_nl,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NCSI_CMD_CLEAR_INTERFACE,
|
2019-04-26 15:07:31 +03:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2018-03-05 03:39:05 +03:00
|
|
|
.doit = ncsi_clear_interface_nl,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2018-10-11 21:07:37 +03:00
|
|
|
{
|
|
|
|
.cmd = NCSI_CMD_SEND_CMD,
|
2019-04-26 15:07:31 +03:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2018-10-11 21:07:37 +03:00
|
|
|
.doit = ncsi_send_cmd_nl,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2018-11-16 07:51:59 +03:00
|
|
|
{
|
|
|
|
.cmd = NCSI_CMD_SET_PACKAGE_MASK,
|
2019-04-26 15:07:31 +03:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2018-11-16 07:51:59 +03:00
|
|
|
.doit = ncsi_set_package_mask_nl,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NCSI_CMD_SET_CHANNEL_MASK,
|
2019-04-26 15:07:31 +03:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2018-11-16 07:51:59 +03:00
|
|
|
.doit = ncsi_set_channel_mask_nl,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2018-03-05 03:39:05 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct genl_family ncsi_genl_family __ro_after_init = {
|
|
|
|
.name = "NCSI",
|
|
|
|
.version = 0,
|
|
|
|
.maxattr = NCSI_ATTR_MAX,
|
genetlink: make policy common to family
Since maxattr is common, the policy can't really differ sanely,
so make it common as well.
The only user that did in fact manage to make a non-common policy
is taskstats, which has to be really careful about it (since it's
still using a common maxattr!). This is no longer supported, but
we can fake it using pre_doit.
This reduces the size of e.g. nl80211.o (which has lots of commands):
text data bss dec hex filename
398745 14323 2240 415308 6564c net/wireless/nl80211.o (before)
397913 14331 2240 414484 65314 net/wireless/nl80211.o (after)
--------------------------------
-832 +8 0 -824
Which is obviously just 8 bytes for each command, and an added 8
bytes for the new policy pointer. I'm not sure why the ops list is
counted as .text though.
Most of the code transformations were done using the following spatch:
@ops@
identifier OPS;
expression POLICY;
@@
struct genl_ops OPS[] = {
...,
{
- .policy = POLICY,
},
...
};
@@
identifier ops.OPS;
expression ops.POLICY;
identifier fam;
expression M;
@@
struct genl_family fam = {
.ops = OPS,
.maxattr = M,
+ .policy = POLICY,
...
};
This also gets rid of devlink_nl_cmd_region_read_dumpit() accessing
the cb->data as ops, which we want to change in a later genl patch.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-03-22 00:51:02 +03:00
|
|
|
.policy = ncsi_genl_policy,
|
2018-03-05 03:39:05 +03:00
|
|
|
.module = THIS_MODULE,
|
2020-10-03 00:49:54 +03:00
|
|
|
.small_ops = ncsi_ops,
|
|
|
|
.n_small_ops = ARRAY_SIZE(ncsi_ops),
|
2018-03-05 03:39:05 +03:00
|
|
|
};
|
|
|
|
|
2020-11-12 09:12:10 +03:00
|
|
|
static int __init ncsi_init_netlink(void)
|
2018-03-05 03:39:05 +03:00
|
|
|
{
|
2020-11-12 09:12:10 +03:00
|
|
|
return genl_register_family(&ncsi_genl_family);
|
2018-03-05 03:39:05 +03:00
|
|
|
}
|
2020-11-12 09:12:10 +03:00
|
|
|
subsys_initcall(ncsi_init_netlink);
|