Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net/ncsi: Add generic netlink family

Add a generic netlink family for NCSI. This supports three commands;
NCSI_CMD_PKG_INFO which returns information on packages and their
associated channels, NCSI_CMD_SET_INTERFACE which allows a specific
package or package/channel combination to be set as the preferred
choice, and NCSI_CMD_CLEAR_INTERFACE which clears any preferred setting.

Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Samuel Mendoza-Jonas and committed by
David S. Miller
955dc68c be631892

+586 -5
+115
include/uapi/linux/ncsi.h
··· 1 + /* 2 + * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + */ 9 + 10 + #ifndef __UAPI_NCSI_NETLINK_H__ 11 + #define __UAPI_NCSI_NETLINK_H__ 12 + 13 + /** 14 + * enum ncsi_nl_commands - supported NCSI commands 15 + * 16 + * @NCSI_CMD_UNSPEC: unspecified command to catch errors 17 + * @NCSI_CMD_PKG_INFO: list package and channel attributes. Requires 18 + * NCSI_ATTR_IFINDEX. If NCSI_ATTR_PACKAGE_ID is specified returns the 19 + * specific package and its channels - otherwise a dump request returns 20 + * all packages and their associated channels. 21 + * @NCSI_CMD_SET_INTERFACE: set preferred package and channel combination. 22 + * Requires NCSI_ATTR_IFINDEX and the preferred NCSI_ATTR_PACKAGE_ID and 23 + * optionally the preferred NCSI_ATTR_CHANNEL_ID. 24 + * @NCSI_CMD_CLEAR_INTERFACE: clear any preferred package/channel combination. 25 + * Requires NCSI_ATTR_IFINDEX. 26 + * @NCSI_CMD_MAX: highest command number 27 + */ 28 + enum ncsi_nl_commands { 29 + NCSI_CMD_UNSPEC, 30 + NCSI_CMD_PKG_INFO, 31 + NCSI_CMD_SET_INTERFACE, 32 + NCSI_CMD_CLEAR_INTERFACE, 33 + 34 + __NCSI_CMD_AFTER_LAST, 35 + NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1 36 + }; 37 + 38 + /** 39 + * enum ncsi_nl_attrs - General NCSI netlink attributes 40 + * 41 + * @NCSI_ATTR_UNSPEC: unspecified attributes to catch errors 42 + * @NCSI_ATTR_IFINDEX: ifindex of network device using NCSI 43 + * @NCSI_ATTR_PACKAGE_LIST: nested array of NCSI_PKG_ATTR attributes 44 + * @NCSI_ATTR_PACKAGE_ID: package ID 45 + * @NCSI_ATTR_CHANNEL_ID: channel ID 46 + * @NCSI_ATTR_MAX: highest attribute number 47 + */ 48 + enum ncsi_nl_attrs { 49 + NCSI_ATTR_UNSPEC, 50 + NCSI_ATTR_IFINDEX, 51 + NCSI_ATTR_PACKAGE_LIST, 52 + NCSI_ATTR_PACKAGE_ID, 53 + NCSI_ATTR_CHANNEL_ID, 54 + 55 + __NCSI_ATTR_AFTER_LAST, 56 + NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1 57 + }; 58 + 59 + /** 60 + * enum ncsi_nl_pkg_attrs - NCSI netlink package-specific attributes 61 + * 62 + * @NCSI_PKG_ATTR_UNSPEC: unspecified attributes to catch errors 63 + * @NCSI_PKG_ATTR: nested array of package attributes 64 + * @NCSI_PKG_ATTR_ID: package ID 65 + * @NCSI_PKG_ATTR_FORCED: flag signifying a package has been set as preferred 66 + * @NCSI_PKG_ATTR_CHANNEL_LIST: nested array of NCSI_CHANNEL_ATTR attributes 67 + * @NCSI_PKG_ATTR_MAX: highest attribute number 68 + */ 69 + enum ncsi_nl_pkg_attrs { 70 + NCSI_PKG_ATTR_UNSPEC, 71 + NCSI_PKG_ATTR, 72 + NCSI_PKG_ATTR_ID, 73 + NCSI_PKG_ATTR_FORCED, 74 + NCSI_PKG_ATTR_CHANNEL_LIST, 75 + 76 + __NCSI_PKG_ATTR_AFTER_LAST, 77 + NCSI_PKG_ATTR_MAX = __NCSI_PKG_ATTR_AFTER_LAST - 1 78 + }; 79 + 80 + /** 81 + * enum ncsi_nl_channel_attrs - NCSI netlink channel-specific attributes 82 + * 83 + * @NCSI_CHANNEL_ATTR_UNSPEC: unspecified attributes to catch errors 84 + * @NCSI_CHANNEL_ATTR: nested array of channel attributes 85 + * @NCSI_CHANNEL_ATTR_ID: channel ID 86 + * @NCSI_CHANNEL_ATTR_VERSION_MAJOR: channel major version number 87 + * @NCSI_CHANNEL_ATTR_VERSION_MINOR: channel minor version number 88 + * @NCSI_CHANNEL_ATTR_VERSION_STR: channel version string 89 + * @NCSI_CHANNEL_ATTR_LINK_STATE: channel link state flags 90 + * @NCSI_CHANNEL_ATTR_ACTIVE: channels with this flag are in 91 + * NCSI_CHANNEL_ACTIVE state 92 + * @NCSI_CHANNEL_ATTR_FORCED: flag signifying a channel has been set as 93 + * preferred 94 + * @NCSI_CHANNEL_ATTR_VLAN_LIST: nested array of NCSI_CHANNEL_ATTR_VLAN_IDs 95 + * @NCSI_CHANNEL_ATTR_VLAN_ID: VLAN ID being filtered on this channel 96 + * @NCSI_CHANNEL_ATTR_MAX: highest attribute number 97 + */ 98 + enum ncsi_nl_channel_attrs { 99 + NCSI_CHANNEL_ATTR_UNSPEC, 100 + NCSI_CHANNEL_ATTR, 101 + NCSI_CHANNEL_ATTR_ID, 102 + NCSI_CHANNEL_ATTR_VERSION_MAJOR, 103 + NCSI_CHANNEL_ATTR_VERSION_MINOR, 104 + NCSI_CHANNEL_ATTR_VERSION_STR, 105 + NCSI_CHANNEL_ATTR_LINK_STATE, 106 + NCSI_CHANNEL_ATTR_ACTIVE, 107 + NCSI_CHANNEL_ATTR_FORCED, 108 + NCSI_CHANNEL_ATTR_VLAN_LIST, 109 + NCSI_CHANNEL_ATTR_VLAN_ID, 110 + 111 + __NCSI_CHANNEL_ATTR_AFTER_LAST, 112 + NCSI_CHANNEL_ATTR_MAX = __NCSI_CHANNEL_ATTR_AFTER_LAST - 1 113 + }; 114 + 115 + #endif /* __UAPI_NCSI_NETLINK_H__ */
+1 -1
net/ncsi/Makefile
··· 1 1 # 2 2 # Makefile for NCSI API 3 3 # 4 - obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-aen.o ncsi-manage.o 4 + obj-$(CONFIG_NET_NCSI) += ncsi-cmd.o ncsi-rsp.o ncsi-aen.o ncsi-manage.o ncsi-netlink.o
+3
net/ncsi/internal.h
··· 276 276 unsigned int package_num; /* Number of packages */ 277 277 struct list_head packages; /* List of packages */ 278 278 struct ncsi_channel *hot_channel; /* Channel was ever active */ 279 + struct ncsi_package *force_package; /* Force a specific package */ 280 + struct ncsi_channel *force_channel; /* Force a specific channel */ 279 281 struct ncsi_request requests[256]; /* Request table */ 280 282 unsigned int request_id; /* Last used request ID */ 281 283 #define NCSI_REQ_START_IDX 1 ··· 320 318 list_for_each_entry_rcu(nc, &np->channels, node) 321 319 322 320 /* Resources */ 321 + u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index); 323 322 int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data); 324 323 int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data); 325 324 int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index);
+26 -4
net/ncsi/ncsi-manage.c
··· 12 12 #include <linux/init.h> 13 13 #include <linux/netdevice.h> 14 14 #include <linux/skbuff.h> 15 - #include <linux/netlink.h> 16 15 17 16 #include <net/ncsi.h> 18 17 #include <net/net_namespace.h> ··· 22 23 23 24 #include "internal.h" 24 25 #include "ncsi-pkt.h" 26 + #include "ncsi-netlink.h" 25 27 26 28 LIST_HEAD(ncsi_dev_list); 27 29 DEFINE_SPINLOCK(ncsi_dev_lock); ··· 38 38 return sizes[table]; 39 39 } 40 40 41 - static u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index) 41 + u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index) 42 42 { 43 43 struct ncsi_channel_filter *ncf; 44 44 int size; ··· 965 965 966 966 static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) 967 967 { 968 - struct ncsi_package *np; 969 - struct ncsi_channel *nc, *found, *hot_nc; 968 + struct ncsi_package *np, *force_package; 969 + struct ncsi_channel *nc, *found, *hot_nc, *force_channel; 970 970 struct ncsi_channel_mode *ncm; 971 971 unsigned long flags; 972 972 973 973 spin_lock_irqsave(&ndp->lock, flags); 974 974 hot_nc = ndp->hot_channel; 975 + force_channel = ndp->force_channel; 976 + force_package = ndp->force_package; 975 977 spin_unlock_irqrestore(&ndp->lock, flags); 978 + 979 + /* Force a specific channel whether or not it has link if we have been 980 + * configured to do so 981 + */ 982 + if (force_package && force_channel) { 983 + found = force_channel; 984 + ncm = &found->modes[NCSI_MODE_LINK]; 985 + if (!(ncm->data[2] & 0x1)) 986 + netdev_info(ndp->ndev.dev, 987 + "NCSI: Channel %u forced, but it is link down\n", 988 + found->id); 989 + goto out; 990 + } 976 991 977 992 /* The search is done once an inactive channel with up 978 993 * link is found. 979 994 */ 980 995 found = NULL; 981 996 NCSI_FOR_EACH_PACKAGE(ndp, np) { 997 + if (ndp->force_package && np != ndp->force_package) 998 + continue; 982 999 NCSI_FOR_EACH_CHANNEL(np, nc) { 983 1000 spin_lock_irqsave(&nc->lock, flags); 984 1001 ··· 1611 1594 ndp->ptype.dev = dev; 1612 1595 dev_add_pack(&ndp->ptype); 1613 1596 1597 + /* Set up generic netlink interface */ 1598 + ncsi_init_netlink(dev); 1599 + 1614 1600 return nd; 1615 1601 } 1616 1602 EXPORT_SYMBOL_GPL(ncsi_register_dev); ··· 1692 1672 unregister_inet6addr_notifier(&ncsi_inet6addr_notifier); 1693 1673 #endif 1694 1674 spin_unlock_irqrestore(&ncsi_dev_lock, flags); 1675 + 1676 + ncsi_unregister_netlink(nd->dev); 1695 1677 1696 1678 kfree(ndp); 1697 1679 }
+421
net/ncsi/ncsi-netlink.c
··· 1 + /* 2 + * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + */ 9 + 10 + #include <linux/module.h> 11 + #include <linux/kernel.h> 12 + #include <linux/if_arp.h> 13 + #include <linux/rtnetlink.h> 14 + #include <linux/etherdevice.h> 15 + #include <linux/module.h> 16 + #include <net/genetlink.h> 17 + #include <net/ncsi.h> 18 + #include <linux/skbuff.h> 19 + #include <net/sock.h> 20 + #include <uapi/linux/ncsi.h> 21 + 22 + #include "internal.h" 23 + #include "ncsi-netlink.h" 24 + 25 + static struct genl_family ncsi_genl_family; 26 + 27 + static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = { 28 + [NCSI_ATTR_IFINDEX] = { .type = NLA_U32 }, 29 + [NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED }, 30 + [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, 31 + [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, 32 + }; 33 + 34 + static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) 35 + { 36 + struct ncsi_dev_priv *ndp; 37 + struct net_device *dev; 38 + struct ncsi_dev *nd; 39 + struct ncsi_dev; 40 + 41 + if (!net) 42 + return NULL; 43 + 44 + dev = dev_get_by_index(net, ifindex); 45 + if (!dev) { 46 + pr_err("NCSI netlink: No device for ifindex %u\n", ifindex); 47 + return NULL; 48 + } 49 + 50 + nd = ncsi_find_dev(dev); 51 + ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL; 52 + 53 + dev_put(dev); 54 + return ndp; 55 + } 56 + 57 + static int ncsi_write_channel_info(struct sk_buff *skb, 58 + struct ncsi_dev_priv *ndp, 59 + struct ncsi_channel *nc) 60 + { 61 + struct nlattr *vid_nest; 62 + struct ncsi_channel_filter *ncf; 63 + struct ncsi_channel_mode *m; 64 + u32 *data; 65 + int i; 66 + 67 + nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id); 68 + m = &nc->modes[NCSI_MODE_LINK]; 69 + nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); 70 + if (nc->state == NCSI_CHANNEL_ACTIVE) 71 + nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); 72 + if (ndp->force_channel == nc) 73 + nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); 74 + 75 + nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); 76 + nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2); 77 + nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name); 78 + 79 + vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST); 80 + if (!vid_nest) 81 + return -ENOMEM; 82 + ncf = nc->filters[NCSI_FILTER_VLAN]; 83 + i = -1; 84 + if (ncf) { 85 + while ((i = find_next_bit((void *)&ncf->bitmap, ncf->total, 86 + i + 1)) < ncf->total) { 87 + data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, i); 88 + /* Uninitialised channels will have 'zero' vlan ids */ 89 + if (!data || !*data) 90 + continue; 91 + nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID, 92 + *(u16 *)data); 93 + } 94 + } 95 + nla_nest_end(skb, vid_nest); 96 + 97 + return 0; 98 + } 99 + 100 + static int ncsi_write_package_info(struct sk_buff *skb, 101 + struct ncsi_dev_priv *ndp, unsigned int id) 102 + { 103 + struct nlattr *pnest, *cnest, *nest; 104 + struct ncsi_package *np; 105 + struct ncsi_channel *nc; 106 + bool found; 107 + int rc; 108 + 109 + if (id > ndp->package_num) { 110 + netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id); 111 + return -ENODEV; 112 + } 113 + 114 + found = false; 115 + NCSI_FOR_EACH_PACKAGE(ndp, np) { 116 + if (np->id != id) 117 + continue; 118 + pnest = nla_nest_start(skb, NCSI_PKG_ATTR); 119 + if (!pnest) 120 + return -ENOMEM; 121 + nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); 122 + if (ndp->force_package == np) 123 + nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); 124 + cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); 125 + if (!cnest) { 126 + nla_nest_cancel(skb, pnest); 127 + return -ENOMEM; 128 + } 129 + NCSI_FOR_EACH_CHANNEL(np, nc) { 130 + nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR); 131 + if (!nest) { 132 + nla_nest_cancel(skb, cnest); 133 + nla_nest_cancel(skb, pnest); 134 + return -ENOMEM; 135 + } 136 + rc = ncsi_write_channel_info(skb, ndp, nc); 137 + if (rc) { 138 + nla_nest_cancel(skb, nest); 139 + nla_nest_cancel(skb, cnest); 140 + nla_nest_cancel(skb, pnest); 141 + return rc; 142 + } 143 + nla_nest_end(skb, nest); 144 + } 145 + nla_nest_end(skb, cnest); 146 + nla_nest_end(skb, pnest); 147 + found = true; 148 + } 149 + 150 + if (!found) 151 + return -ENODEV; 152 + 153 + return 0; 154 + } 155 + 156 + static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info) 157 + { 158 + struct ncsi_dev_priv *ndp; 159 + unsigned int package_id; 160 + struct sk_buff *skb; 161 + struct nlattr *attr; 162 + void *hdr; 163 + int rc; 164 + 165 + if (!info || !info->attrs) 166 + return -EINVAL; 167 + 168 + if (!info->attrs[NCSI_ATTR_IFINDEX]) 169 + return -EINVAL; 170 + 171 + if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 172 + return -EINVAL; 173 + 174 + ndp = ndp_from_ifindex(genl_info_net(info), 175 + nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 176 + if (!ndp) 177 + return -ENODEV; 178 + 179 + skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 180 + if (!skb) 181 + return -ENOMEM; 182 + 183 + hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, 184 + &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO); 185 + if (!hdr) { 186 + kfree(skb); 187 + return -EMSGSIZE; 188 + } 189 + 190 + package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 191 + 192 + attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST); 193 + rc = ncsi_write_package_info(skb, ndp, package_id); 194 + 195 + if (rc) { 196 + nla_nest_cancel(skb, attr); 197 + goto err; 198 + } 199 + 200 + nla_nest_end(skb, attr); 201 + 202 + genlmsg_end(skb, hdr); 203 + return genlmsg_reply(skb, info); 204 + 205 + err: 206 + genlmsg_cancel(skb, hdr); 207 + kfree(skb); 208 + return rc; 209 + } 210 + 211 + static int ncsi_pkg_info_all_nl(struct sk_buff *skb, 212 + struct netlink_callback *cb) 213 + { 214 + struct nlattr *attrs[NCSI_ATTR_MAX]; 215 + struct ncsi_package *np, *package; 216 + struct ncsi_dev_priv *ndp; 217 + unsigned int package_id; 218 + struct nlattr *attr; 219 + void *hdr; 220 + int rc; 221 + 222 + rc = genlmsg_parse(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX, 223 + ncsi_genl_policy, NULL); 224 + if (rc) 225 + return rc; 226 + 227 + if (!attrs[NCSI_ATTR_IFINDEX]) 228 + return -EINVAL; 229 + 230 + ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)), 231 + nla_get_u32(attrs[NCSI_ATTR_IFINDEX])); 232 + 233 + if (!ndp) 234 + return -ENODEV; 235 + 236 + package_id = cb->args[0]; 237 + package = NULL; 238 + NCSI_FOR_EACH_PACKAGE(ndp, np) 239 + if (np->id == package_id) 240 + package = np; 241 + 242 + if (!package) 243 + return 0; /* done */ 244 + 245 + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 246 + &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO); 247 + if (!hdr) { 248 + rc = -EMSGSIZE; 249 + goto err; 250 + } 251 + 252 + attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST); 253 + rc = ncsi_write_package_info(skb, ndp, package->id); 254 + if (rc) { 255 + nla_nest_cancel(skb, attr); 256 + goto err; 257 + } 258 + 259 + nla_nest_end(skb, attr); 260 + genlmsg_end(skb, hdr); 261 + 262 + cb->args[0] = package_id + 1; 263 + 264 + return skb->len; 265 + err: 266 + genlmsg_cancel(skb, hdr); 267 + return rc; 268 + } 269 + 270 + static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) 271 + { 272 + struct ncsi_package *np, *package; 273 + struct ncsi_channel *nc, *channel; 274 + u32 package_id, channel_id; 275 + struct ncsi_dev_priv *ndp; 276 + unsigned long flags; 277 + 278 + if (!info || !info->attrs) 279 + return -EINVAL; 280 + 281 + if (!info->attrs[NCSI_ATTR_IFINDEX]) 282 + return -EINVAL; 283 + 284 + if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 285 + return -EINVAL; 286 + 287 + ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 288 + nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 289 + if (!ndp) 290 + return -ENODEV; 291 + 292 + package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 293 + package = NULL; 294 + 295 + spin_lock_irqsave(&ndp->lock, flags); 296 + 297 + NCSI_FOR_EACH_PACKAGE(ndp, np) 298 + if (np->id == package_id) 299 + package = np; 300 + if (!package) { 301 + /* The user has set a package that does not exist */ 302 + return -ERANGE; 303 + } 304 + 305 + channel = NULL; 306 + if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { 307 + /* Allow any channel */ 308 + channel_id = NCSI_RESERVED_CHANNEL; 309 + } else { 310 + channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 311 + NCSI_FOR_EACH_CHANNEL(package, nc) 312 + if (nc->id == channel_id) 313 + channel = nc; 314 + } 315 + 316 + if (channel_id != NCSI_RESERVED_CHANNEL && !channel) { 317 + /* The user has set a channel that does not exist on this 318 + * package 319 + */ 320 + netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n", 321 + channel_id); 322 + return -ERANGE; 323 + } 324 + 325 + ndp->force_package = package; 326 + ndp->force_channel = channel; 327 + spin_unlock_irqrestore(&ndp->lock, flags); 328 + 329 + netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n", 330 + package_id, channel_id, 331 + channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : ""); 332 + 333 + /* Bounce the NCSI channel to set changes */ 334 + ncsi_stop_dev(&ndp->ndev); 335 + ncsi_start_dev(&ndp->ndev); 336 + 337 + return 0; 338 + } 339 + 340 + static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) 341 + { 342 + struct ncsi_dev_priv *ndp; 343 + unsigned long flags; 344 + 345 + if (!info || !info->attrs) 346 + return -EINVAL; 347 + 348 + if (!info->attrs[NCSI_ATTR_IFINDEX]) 349 + return -EINVAL; 350 + 351 + ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 352 + nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 353 + if (!ndp) 354 + return -ENODEV; 355 + 356 + /* Clear any override */ 357 + spin_lock_irqsave(&ndp->lock, flags); 358 + ndp->force_package = NULL; 359 + ndp->force_channel = NULL; 360 + spin_unlock_irqrestore(&ndp->lock, flags); 361 + netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); 362 + 363 + /* Bounce the NCSI channel to set changes */ 364 + ncsi_stop_dev(&ndp->ndev); 365 + ncsi_start_dev(&ndp->ndev); 366 + 367 + return 0; 368 + } 369 + 370 + static const struct genl_ops ncsi_ops[] = { 371 + { 372 + .cmd = NCSI_CMD_PKG_INFO, 373 + .policy = ncsi_genl_policy, 374 + .doit = ncsi_pkg_info_nl, 375 + .dumpit = ncsi_pkg_info_all_nl, 376 + .flags = 0, 377 + }, 378 + { 379 + .cmd = NCSI_CMD_SET_INTERFACE, 380 + .policy = ncsi_genl_policy, 381 + .doit = ncsi_set_interface_nl, 382 + .flags = GENL_ADMIN_PERM, 383 + }, 384 + { 385 + .cmd = NCSI_CMD_CLEAR_INTERFACE, 386 + .policy = ncsi_genl_policy, 387 + .doit = ncsi_clear_interface_nl, 388 + .flags = GENL_ADMIN_PERM, 389 + }, 390 + }; 391 + 392 + static struct genl_family ncsi_genl_family __ro_after_init = { 393 + .name = "NCSI", 394 + .version = 0, 395 + .maxattr = NCSI_ATTR_MAX, 396 + .module = THIS_MODULE, 397 + .ops = ncsi_ops, 398 + .n_ops = ARRAY_SIZE(ncsi_ops), 399 + }; 400 + 401 + int ncsi_init_netlink(struct net_device *dev) 402 + { 403 + int rc; 404 + 405 + rc = genl_register_family(&ncsi_genl_family); 406 + if (rc) 407 + netdev_err(dev, "ncsi: failed to register netlink family\n"); 408 + 409 + return rc; 410 + } 411 + 412 + int ncsi_unregister_netlink(struct net_device *dev) 413 + { 414 + int rc; 415 + 416 + rc = genl_unregister_family(&ncsi_genl_family); 417 + if (rc) 418 + netdev_err(dev, "ncsi: failed to unregister netlink family\n"); 419 + 420 + return rc; 421 + }
+20
net/ncsi/ncsi-netlink.h
··· 1 + /* 2 + * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + */ 9 + 10 + #ifndef __NCSI_NETLINK_H__ 11 + #define __NCSI_NETLINK_H__ 12 + 13 + #include <linux/netdevice.h> 14 + 15 + #include "internal.h" 16 + 17 + int ncsi_init_netlink(struct net_device *dev); 18 + int ncsi_unregister_netlink(struct net_device *dev); 19 + 20 + #endif /* __NCSI_NETLINK_H__ */