net: phy: trigger state machine immediately in phy_start_machine
[linux-2.6-block.git] / net / ncsi / ncsi-netlink.c
CommitLineData
955dc68c
SMJ
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>
955dc68c
SMJ
15#include <net/genetlink.h>
16#include <net/ncsi.h>
17#include <linux/skbuff.h>
18#include <net/sock.h>
19#include <uapi/linux/ncsi.h>
20
21#include "internal.h"
22#include "ncsi-netlink.h"
23
24static struct genl_family ncsi_genl_family;
25
26static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
27 [NCSI_ATTR_IFINDEX] = { .type = NLA_U32 },
28 [NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED },
29 [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 },
30 [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 },
31};
32
33static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
34{
35 struct ncsi_dev_priv *ndp;
36 struct net_device *dev;
37 struct ncsi_dev *nd;
38 struct ncsi_dev;
39
40 if (!net)
41 return NULL;
42
43 dev = dev_get_by_index(net, ifindex);
44 if (!dev) {
45 pr_err("NCSI netlink: No device for ifindex %u\n", ifindex);
46 return NULL;
47 }
48
49 nd = ncsi_find_dev(dev);
50 ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
51
52 dev_put(dev);
53 return ndp;
54}
55
56static int ncsi_write_channel_info(struct sk_buff *skb,
57 struct ncsi_dev_priv *ndp,
58 struct ncsi_channel *nc)
59{
062b3e1b 60 struct ncsi_channel_vlan_filter *ncf;
955dc68c 61 struct ncsi_channel_mode *m;
062b3e1b 62 struct nlattr *vid_nest;
955dc68c
SMJ
63 int i;
64
65 nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id);
66 m = &nc->modes[NCSI_MODE_LINK];
67 nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]);
68 if (nc->state == NCSI_CHANNEL_ACTIVE)
69 nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE);
70 if (ndp->force_channel == nc)
71 nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
72
73 nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version);
74 nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2);
75 nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name);
76
77 vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST);
78 if (!vid_nest)
79 return -ENOMEM;
062b3e1b 80 ncf = &nc->vlan_filter;
955dc68c 81 i = -1;
062b3e1b
SMJ
82 while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids,
83 i + 1)) < ncf->n_vids) {
84 if (ncf->vids[i])
955dc68c 85 nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID,
062b3e1b 86 ncf->vids[i]);
955dc68c
SMJ
87 }
88 nla_nest_end(skb, vid_nest);
89
90 return 0;
91}
92
93static int ncsi_write_package_info(struct sk_buff *skb,
94 struct ncsi_dev_priv *ndp, unsigned int id)
95{
96 struct nlattr *pnest, *cnest, *nest;
97 struct ncsi_package *np;
98 struct ncsi_channel *nc;
99 bool found;
100 int rc;
101
3d0371b3 102 if (id > ndp->package_num - 1) {
955dc68c
SMJ
103 netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id);
104 return -ENODEV;
105 }
106
107 found = false;
108 NCSI_FOR_EACH_PACKAGE(ndp, np) {
109 if (np->id != id)
110 continue;
111 pnest = nla_nest_start(skb, NCSI_PKG_ATTR);
112 if (!pnest)
113 return -ENOMEM;
114 nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
115 if (ndp->force_package == np)
116 nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
117 cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
118 if (!cnest) {
119 nla_nest_cancel(skb, pnest);
120 return -ENOMEM;
121 }
122 NCSI_FOR_EACH_CHANNEL(np, nc) {
123 nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR);
124 if (!nest) {
125 nla_nest_cancel(skb, cnest);
126 nla_nest_cancel(skb, pnest);
127 return -ENOMEM;
128 }
129 rc = ncsi_write_channel_info(skb, ndp, nc);
130 if (rc) {
131 nla_nest_cancel(skb, nest);
132 nla_nest_cancel(skb, cnest);
133 nla_nest_cancel(skb, pnest);
134 return rc;
135 }
136 nla_nest_end(skb, nest);
137 }
138 nla_nest_end(skb, cnest);
139 nla_nest_end(skb, pnest);
140 found = true;
141 }
142
143 if (!found)
144 return -ENODEV;
145
146 return 0;
147}
148
149static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info)
150{
151 struct ncsi_dev_priv *ndp;
152 unsigned int package_id;
153 struct sk_buff *skb;
154 struct nlattr *attr;
155 void *hdr;
156 int rc;
157
158 if (!info || !info->attrs)
159 return -EINVAL;
160
161 if (!info->attrs[NCSI_ATTR_IFINDEX])
162 return -EINVAL;
163
164 if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
165 return -EINVAL;
166
167 ndp = ndp_from_ifindex(genl_info_net(info),
168 nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
169 if (!ndp)
170 return -ENODEV;
171
172 skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
173 if (!skb)
174 return -ENOMEM;
175
176 hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
177 &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO);
178 if (!hdr) {
50db64b0 179 kfree_skb(skb);
955dc68c
SMJ
180 return -EMSGSIZE;
181 }
182
183 package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
184
185 attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST);
8daf1a2d
CIK
186 if (!attr) {
187 kfree_skb(skb);
188 return -EMSGSIZE;
189 }
955dc68c
SMJ
190 rc = ncsi_write_package_info(skb, ndp, package_id);
191
192 if (rc) {
193 nla_nest_cancel(skb, attr);
194 goto err;
195 }
196
197 nla_nest_end(skb, attr);
198
199 genlmsg_end(skb, hdr);
200 return genlmsg_reply(skb, info);
201
202err:
50db64b0 203 kfree_skb(skb);
955dc68c
SMJ
204 return rc;
205}
206
207static int ncsi_pkg_info_all_nl(struct sk_buff *skb,
208 struct netlink_callback *cb)
209{
0f51f358 210 struct nlattr *attrs[NCSI_ATTR_MAX + 1];
955dc68c
SMJ
211 struct ncsi_package *np, *package;
212 struct ncsi_dev_priv *ndp;
213 unsigned int package_id;
214 struct nlattr *attr;
215 void *hdr;
216 int rc;
217
218 rc = genlmsg_parse(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX,
219 ncsi_genl_policy, NULL);
220 if (rc)
221 return rc;
222
223 if (!attrs[NCSI_ATTR_IFINDEX])
224 return -EINVAL;
225
226 ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)),
227 nla_get_u32(attrs[NCSI_ATTR_IFINDEX]));
228
229 if (!ndp)
230 return -ENODEV;
231
232 package_id = cb->args[0];
233 package = NULL;
234 NCSI_FOR_EACH_PACKAGE(ndp, np)
235 if (np->id == package_id)
236 package = np;
237
238 if (!package)
239 return 0; /* done */
240
241 hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
3d0371b3 242 &ncsi_genl_family, NLM_F_MULTI, NCSI_CMD_PKG_INFO);
955dc68c
SMJ
243 if (!hdr) {
244 rc = -EMSGSIZE;
245 goto err;
246 }
247
248 attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST);
249 rc = ncsi_write_package_info(skb, ndp, package->id);
250 if (rc) {
251 nla_nest_cancel(skb, attr);
252 goto err;
253 }
254
255 nla_nest_end(skb, attr);
256 genlmsg_end(skb, hdr);
257
258 cb->args[0] = package_id + 1;
259
260 return skb->len;
261err:
262 genlmsg_cancel(skb, hdr);
263 return rc;
264}
265
266static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
267{
268 struct ncsi_package *np, *package;
269 struct ncsi_channel *nc, *channel;
270 u32 package_id, channel_id;
271 struct ncsi_dev_priv *ndp;
272 unsigned long flags;
273
274 if (!info || !info->attrs)
275 return -EINVAL;
276
277 if (!info->attrs[NCSI_ATTR_IFINDEX])
278 return -EINVAL;
279
280 if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
281 return -EINVAL;
282
283 ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
284 nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
285 if (!ndp)
286 return -ENODEV;
287
288 package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
289 package = NULL;
290
291 spin_lock_irqsave(&ndp->lock, flags);
292
293 NCSI_FOR_EACH_PACKAGE(ndp, np)
294 if (np->id == package_id)
295 package = np;
296 if (!package) {
297 /* The user has set a package that does not exist */
054f34da 298 spin_unlock_irqrestore(&ndp->lock, flags);
955dc68c
SMJ
299 return -ERANGE;
300 }
301
302 channel = NULL;
303 if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
304 /* Allow any channel */
305 channel_id = NCSI_RESERVED_CHANNEL;
306 } else {
307 channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
308 NCSI_FOR_EACH_CHANNEL(package, nc)
309 if (nc->id == channel_id)
310 channel = nc;
311 }
312
313 if (channel_id != NCSI_RESERVED_CHANNEL && !channel) {
314 /* The user has set a channel that does not exist on this
315 * package
316 */
054f34da 317 spin_unlock_irqrestore(&ndp->lock, flags);
955dc68c
SMJ
318 netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n",
319 channel_id);
320 return -ERANGE;
321 }
322
323 ndp->force_package = package;
324 ndp->force_channel = channel;
325 spin_unlock_irqrestore(&ndp->lock, flags);
326
327 netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n",
328 package_id, channel_id,
329 channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : "");
330
331 /* Bounce the NCSI channel to set changes */
332 ncsi_stop_dev(&ndp->ndev);
333 ncsi_start_dev(&ndp->ndev);
334
335 return 0;
336}
337
338static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
339{
340 struct ncsi_dev_priv *ndp;
341 unsigned long flags;
342
343 if (!info || !info->attrs)
344 return -EINVAL;
345
346 if (!info->attrs[NCSI_ATTR_IFINDEX])
347 return -EINVAL;
348
349 ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
350 nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
351 if (!ndp)
352 return -ENODEV;
353
354 /* Clear any override */
355 spin_lock_irqsave(&ndp->lock, flags);
356 ndp->force_package = NULL;
357 ndp->force_channel = NULL;
358 spin_unlock_irqrestore(&ndp->lock, flags);
359 netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
360
361 /* Bounce the NCSI channel to set changes */
362 ncsi_stop_dev(&ndp->ndev);
363 ncsi_start_dev(&ndp->ndev);
364
365 return 0;
366}
367
368static const struct genl_ops ncsi_ops[] = {
369 {
370 .cmd = NCSI_CMD_PKG_INFO,
371 .policy = ncsi_genl_policy,
372 .doit = ncsi_pkg_info_nl,
373 .dumpit = ncsi_pkg_info_all_nl,
374 .flags = 0,
375 },
376 {
377 .cmd = NCSI_CMD_SET_INTERFACE,
378 .policy = ncsi_genl_policy,
379 .doit = ncsi_set_interface_nl,
380 .flags = GENL_ADMIN_PERM,
381 },
382 {
383 .cmd = NCSI_CMD_CLEAR_INTERFACE,
384 .policy = ncsi_genl_policy,
385 .doit = ncsi_clear_interface_nl,
386 .flags = GENL_ADMIN_PERM,
387 },
388};
389
390static struct genl_family ncsi_genl_family __ro_after_init = {
391 .name = "NCSI",
392 .version = 0,
393 .maxattr = NCSI_ATTR_MAX,
394 .module = THIS_MODULE,
395 .ops = ncsi_ops,
396 .n_ops = ARRAY_SIZE(ncsi_ops),
397};
398
399int ncsi_init_netlink(struct net_device *dev)
400{
401 int rc;
402
403 rc = genl_register_family(&ncsi_genl_family);
404 if (rc)
405 netdev_err(dev, "ncsi: failed to register netlink family\n");
406
407 return rc;
408}
409
410int ncsi_unregister_netlink(struct net_device *dev)
411{
412 int rc;
413
414 rc = genl_unregister_family(&ncsi_genl_family);
415 if (rc)
416 netdev_err(dev, "ncsi: failed to unregister netlink family\n");
417
418 return rc;
419}