blob: 0966eff48ce70ef49b51d37600617b1bb2e9c663 [file] [log] [blame]
Gavin Shan2d283bd2016-07-19 11:54:16 +10001/*
2 * Copyright Gavin Shan, IBM Corporation 2016.
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/init.h>
13#include <linux/netdevice.h>
14#include <linux/skbuff.h>
15#include <linux/netlink.h>
16
17#include <net/ncsi.h>
18#include <net/net_namespace.h>
19#include <net/sock.h>
Gavin Shane6f44ed2016-07-19 11:54:19 +100020#include <net/addrconf.h>
21#include <net/ipv6.h>
22#include <net/if_inet6.h>
Gavin Shan2d283bd2016-07-19 11:54:16 +100023
24#include "internal.h"
Gavin Shane6f44ed2016-07-19 11:54:19 +100025#include "ncsi-pkt.h"
Gavin Shan2d283bd2016-07-19 11:54:16 +100026
27LIST_HEAD(ncsi_dev_list);
28DEFINE_SPINLOCK(ncsi_dev_lock);
29
30static inline int ncsi_filter_size(int table)
31{
32 int sizes[] = { 2, 6, 6, 6 };
33
34 BUILD_BUG_ON(ARRAY_SIZE(sizes) != NCSI_FILTER_MAX);
35 if (table < NCSI_FILTER_BASE || table >= NCSI_FILTER_MAX)
36 return -EINVAL;
37
38 return sizes[table];
39}
40
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +100041u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index)
42{
43 struct ncsi_channel_filter *ncf;
44 int size;
45
46 ncf = nc->filters[table];
47 if (!ncf)
48 return NULL;
49
50 size = ncsi_filter_size(table);
51 if (size < 0)
52 return NULL;
53
54 return ncf->data + size * index;
55}
56
57/* Find the first active filter in a filter table that matches the given
58 * data parameter. If data is NULL, this returns the first active filter.
59 */
Gavin Shan2d283bd2016-07-19 11:54:16 +100060int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data)
61{
62 struct ncsi_channel_filter *ncf;
63 void *bitmap;
64 int index, size;
65 unsigned long flags;
66
67 ncf = nc->filters[table];
68 if (!ncf)
69 return -ENXIO;
70
71 size = ncsi_filter_size(table);
72 if (size < 0)
73 return size;
74
75 spin_lock_irqsave(&nc->lock, flags);
76 bitmap = (void *)&ncf->bitmap;
77 index = -1;
78 while ((index = find_next_bit(bitmap, ncf->total, index + 1))
79 < ncf->total) {
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +100080 if (!data || !memcmp(ncf->data + size * index, data, size)) {
Gavin Shan2d283bd2016-07-19 11:54:16 +100081 spin_unlock_irqrestore(&nc->lock, flags);
82 return index;
83 }
84 }
85 spin_unlock_irqrestore(&nc->lock, flags);
86
87 return -ENOENT;
88}
89
90int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data)
91{
92 struct ncsi_channel_filter *ncf;
93 int index, size;
94 void *bitmap;
95 unsigned long flags;
96
97 size = ncsi_filter_size(table);
98 if (size < 0)
99 return size;
100
101 index = ncsi_find_filter(nc, table, data);
102 if (index >= 0)
103 return index;
104
105 ncf = nc->filters[table];
106 if (!ncf)
107 return -ENODEV;
108
109 spin_lock_irqsave(&nc->lock, flags);
110 bitmap = (void *)&ncf->bitmap;
111 do {
112 index = find_next_zero_bit(bitmap, ncf->total, 0);
113 if (index >= ncf->total) {
114 spin_unlock_irqrestore(&nc->lock, flags);
115 return -ENOSPC;
116 }
117 } while (test_and_set_bit(index, bitmap));
118
119 memcpy(ncf->data + size * index, data, size);
120 spin_unlock_irqrestore(&nc->lock, flags);
121
122 return index;
123}
124
125int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index)
126{
127 struct ncsi_channel_filter *ncf;
128 int size;
129 void *bitmap;
130 unsigned long flags;
131
132 size = ncsi_filter_size(table);
133 if (size < 0)
134 return size;
135
136 ncf = nc->filters[table];
137 if (!ncf || index >= ncf->total)
138 return -ENODEV;
139
140 spin_lock_irqsave(&nc->lock, flags);
141 bitmap = (void *)&ncf->bitmap;
142 if (test_and_clear_bit(index, bitmap))
143 memset(ncf->data + size * index, 0, size);
144 spin_unlock_irqrestore(&nc->lock, flags);
145
146 return 0;
147}
148
Gavin Shane6f44ed2016-07-19 11:54:19 +1000149static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
150{
151 struct ncsi_dev *nd = &ndp->ndev;
152 struct ncsi_package *np;
153 struct ncsi_channel *nc;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100154 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000155
156 nd->state = ncsi_dev_state_functional;
157 if (force_down) {
158 nd->link_up = 0;
159 goto report;
160 }
161
162 nd->link_up = 0;
163 NCSI_FOR_EACH_PACKAGE(ndp, np) {
164 NCSI_FOR_EACH_CHANNEL(np, nc) {
Gavin Shand8cedaa2016-10-04 11:25:47 +1100165 spin_lock_irqsave(&nc->lock, flags);
166
Gavin Shane6f44ed2016-07-19 11:54:19 +1000167 if (!list_empty(&nc->link) ||
Gavin Shand8cedaa2016-10-04 11:25:47 +1100168 nc->state != NCSI_CHANNEL_ACTIVE) {
169 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000170 continue;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100171 }
Gavin Shane6f44ed2016-07-19 11:54:19 +1000172
173 if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
Gavin Shand8cedaa2016-10-04 11:25:47 +1100174 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000175 nd->link_up = 1;
176 goto report;
177 }
Gavin Shand8cedaa2016-10-04 11:25:47 +1100178
179 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000180 }
181 }
182
183report:
184 nd->handler(nd);
185}
186
187static void ncsi_channel_monitor(unsigned long data)
188{
189 struct ncsi_channel *nc = (struct ncsi_channel *)data;
190 struct ncsi_package *np = nc->package;
191 struct ncsi_dev_priv *ndp = np->ndp;
192 struct ncsi_cmd_arg nca;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100193 bool enabled, chained;
Gavin Shan83afdc62016-10-04 11:25:52 +1100194 unsigned int monitor_state;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000195 unsigned long flags;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100196 int state, ret;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000197
198 spin_lock_irqsave(&nc->lock, flags);
Gavin Shand8cedaa2016-10-04 11:25:47 +1100199 state = nc->state;
200 chained = !list_empty(&nc->link);
Gavin Shan83afdc62016-10-04 11:25:52 +1100201 enabled = nc->monitor.enabled;
202 monitor_state = nc->monitor.state;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000203 spin_unlock_irqrestore(&nc->lock, flags);
204
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100205 if (!enabled || chained) {
206 ncsi_stop_channel_monitor(nc);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000207 return;
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100208 }
Gavin Shand8cedaa2016-10-04 11:25:47 +1100209 if (state != NCSI_CHANNEL_INACTIVE &&
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100210 state != NCSI_CHANNEL_ACTIVE) {
211 ncsi_stop_channel_monitor(nc);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000212 return;
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100213 }
Gavin Shane6f44ed2016-07-19 11:54:19 +1000214
Gavin Shan83afdc62016-10-04 11:25:52 +1100215 switch (monitor_state) {
216 case NCSI_CHANNEL_MONITOR_START:
217 case NCSI_CHANNEL_MONITOR_RETRY:
Gavin Shane6f44ed2016-07-19 11:54:19 +1000218 nca.ndp = ndp;
219 nca.package = np->id;
220 nca.channel = nc->id;
221 nca.type = NCSI_PKT_CMD_GLS;
Gavin Shana0509cb2016-10-04 11:25:51 +1100222 nca.req_flags = 0;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000223 ret = ncsi_xmit_cmd(&nca);
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100224 if (ret)
Gavin Shane6f44ed2016-07-19 11:54:19 +1000225 netdev_err(ndp->ndev.dev, "Error %d sending GLS\n",
226 ret);
Gavin Shan83afdc62016-10-04 11:25:52 +1100227 break;
228 case NCSI_CHANNEL_MONITOR_WAIT ... NCSI_CHANNEL_MONITOR_WAIT_MAX:
229 break;
230 default:
Gavin Shane6f44ed2016-07-19 11:54:19 +1000231 if (!(ndp->flags & NCSI_DEV_HWA) &&
Gavin Shan83afdc62016-10-04 11:25:52 +1100232 state == NCSI_CHANNEL_ACTIVE) {
Gavin Shane6f44ed2016-07-19 11:54:19 +1000233 ncsi_report_link(ndp, true);
Gavin Shan83afdc62016-10-04 11:25:52 +1100234 ndp->flags |= NCSI_DEV_RESHUFFLE;
235 }
Gavin Shane6f44ed2016-07-19 11:54:19 +1000236
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100237 ncsi_stop_channel_monitor(nc);
238
Gavin Shand8cedaa2016-10-04 11:25:47 +1100239 spin_lock_irqsave(&nc->lock, flags);
240 nc->state = NCSI_CHANNEL_INVISIBLE;
241 spin_unlock_irqrestore(&nc->lock, flags);
242
Gavin Shane6f44ed2016-07-19 11:54:19 +1000243 spin_lock_irqsave(&ndp->lock, flags);
Gavin Shand8cedaa2016-10-04 11:25:47 +1100244 nc->state = NCSI_CHANNEL_INACTIVE;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000245 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
246 spin_unlock_irqrestore(&ndp->lock, flags);
247 ncsi_process_next_channel(ndp);
248 return;
249 }
250
251 spin_lock_irqsave(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100252 nc->monitor.state++;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000253 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100254 mod_timer(&nc->monitor.timer, jiffies + HZ);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000255}
256
257void ncsi_start_channel_monitor(struct ncsi_channel *nc)
258{
259 unsigned long flags;
260
261 spin_lock_irqsave(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100262 WARN_ON_ONCE(nc->monitor.enabled);
263 nc->monitor.enabled = true;
264 nc->monitor.state = NCSI_CHANNEL_MONITOR_START;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000265 spin_unlock_irqrestore(&nc->lock, flags);
266
Gavin Shan83afdc62016-10-04 11:25:52 +1100267 mod_timer(&nc->monitor.timer, jiffies + HZ);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000268}
269
270void ncsi_stop_channel_monitor(struct ncsi_channel *nc)
271{
272 unsigned long flags;
273
274 spin_lock_irqsave(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100275 if (!nc->monitor.enabled) {
Gavin Shane6f44ed2016-07-19 11:54:19 +1000276 spin_unlock_irqrestore(&nc->lock, flags);
277 return;
278 }
Gavin Shan83afdc62016-10-04 11:25:52 +1100279 nc->monitor.enabled = false;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000280 spin_unlock_irqrestore(&nc->lock, flags);
281
Gavin Shan83afdc62016-10-04 11:25:52 +1100282 del_timer_sync(&nc->monitor.timer);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000283}
284
Gavin Shan2d283bd2016-07-19 11:54:16 +1000285struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
286 unsigned char id)
287{
288 struct ncsi_channel *nc;
289
290 NCSI_FOR_EACH_CHANNEL(np, nc) {
291 if (nc->id == id)
292 return nc;
293 }
294
295 return NULL;
296}
297
298struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
299{
300 struct ncsi_channel *nc, *tmp;
301 int index;
302 unsigned long flags;
303
304 nc = kzalloc(sizeof(*nc), GFP_ATOMIC);
305 if (!nc)
306 return NULL;
307
308 nc->id = id;
309 nc->package = np;
310 nc->state = NCSI_CHANNEL_INACTIVE;
Gavin Shan83afdc62016-10-04 11:25:52 +1100311 nc->monitor.enabled = false;
312 setup_timer(&nc->monitor.timer,
313 ncsi_channel_monitor, (unsigned long)nc);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000314 spin_lock_init(&nc->lock);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000315 INIT_LIST_HEAD(&nc->link);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000316 for (index = 0; index < NCSI_CAP_MAX; index++)
317 nc->caps[index].index = index;
318 for (index = 0; index < NCSI_MODE_MAX; index++)
319 nc->modes[index].index = index;
320
321 spin_lock_irqsave(&np->lock, flags);
322 tmp = ncsi_find_channel(np, id);
323 if (tmp) {
324 spin_unlock_irqrestore(&np->lock, flags);
325 kfree(nc);
326 return tmp;
327 }
328
329 list_add_tail_rcu(&nc->node, &np->channels);
330 np->channel_num++;
331 spin_unlock_irqrestore(&np->lock, flags);
332
333 return nc;
334}
335
336static void ncsi_remove_channel(struct ncsi_channel *nc)
337{
338 struct ncsi_package *np = nc->package;
339 struct ncsi_channel_filter *ncf;
340 unsigned long flags;
341 int i;
342
343 /* Release filters */
344 spin_lock_irqsave(&nc->lock, flags);
345 for (i = 0; i < NCSI_FILTER_MAX; i++) {
346 ncf = nc->filters[i];
347 if (!ncf)
348 continue;
349
350 nc->filters[i] = NULL;
351 kfree(ncf);
352 }
353
354 nc->state = NCSI_CHANNEL_INACTIVE;
355 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000356 ncsi_stop_channel_monitor(nc);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000357
358 /* Remove and free channel */
359 spin_lock_irqsave(&np->lock, flags);
360 list_del_rcu(&nc->node);
361 np->channel_num--;
362 spin_unlock_irqrestore(&np->lock, flags);
363
364 kfree(nc);
365}
366
367struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
368 unsigned char id)
369{
370 struct ncsi_package *np;
371
372 NCSI_FOR_EACH_PACKAGE(ndp, np) {
373 if (np->id == id)
374 return np;
375 }
376
377 return NULL;
378}
379
380struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
381 unsigned char id)
382{
383 struct ncsi_package *np, *tmp;
384 unsigned long flags;
385
386 np = kzalloc(sizeof(*np), GFP_ATOMIC);
387 if (!np)
388 return NULL;
389
390 np->id = id;
391 np->ndp = ndp;
392 spin_lock_init(&np->lock);
393 INIT_LIST_HEAD(&np->channels);
394
395 spin_lock_irqsave(&ndp->lock, flags);
396 tmp = ncsi_find_package(ndp, id);
397 if (tmp) {
398 spin_unlock_irqrestore(&ndp->lock, flags);
399 kfree(np);
400 return tmp;
401 }
402
403 list_add_tail_rcu(&np->node, &ndp->packages);
404 ndp->package_num++;
405 spin_unlock_irqrestore(&ndp->lock, flags);
406
407 return np;
408}
409
410void ncsi_remove_package(struct ncsi_package *np)
411{
412 struct ncsi_dev_priv *ndp = np->ndp;
413 struct ncsi_channel *nc, *tmp;
414 unsigned long flags;
415
416 /* Release all child channels */
417 list_for_each_entry_safe(nc, tmp, &np->channels, node)
418 ncsi_remove_channel(nc);
419
420 /* Remove and free package */
421 spin_lock_irqsave(&ndp->lock, flags);
422 list_del_rcu(&np->node);
423 ndp->package_num--;
424 spin_unlock_irqrestore(&ndp->lock, flags);
425
426 kfree(np);
427}
428
429void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
430 unsigned char id,
431 struct ncsi_package **np,
432 struct ncsi_channel **nc)
433{
434 struct ncsi_package *p;
435 struct ncsi_channel *c;
436
437 p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id));
438 c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL;
439
440 if (np)
441 *np = p;
442 if (nc)
443 *nc = c;
444}
445
446/* For two consecutive NCSI commands, the packet IDs shouldn't
447 * be same. Otherwise, the bogus response might be replied. So
448 * the available IDs are allocated in round-robin fashion.
449 */
Gavin Shana0509cb2016-10-04 11:25:51 +1100450struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
451 unsigned int req_flags)
Gavin Shan2d283bd2016-07-19 11:54:16 +1000452{
453 struct ncsi_request *nr = NULL;
454 int i, limit = ARRAY_SIZE(ndp->requests);
455 unsigned long flags;
456
457 /* Check if there is one available request until the ceiling */
458 spin_lock_irqsave(&ndp->lock, flags);
Gavin Shana15af542016-10-04 11:25:50 +1100459 for (i = ndp->request_id; i < limit; i++) {
Gavin Shan2d283bd2016-07-19 11:54:16 +1000460 if (ndp->requests[i].used)
461 continue;
462
463 nr = &ndp->requests[i];
464 nr->used = true;
Gavin Shana0509cb2016-10-04 11:25:51 +1100465 nr->flags = req_flags;
Gavin Shana15af542016-10-04 11:25:50 +1100466 ndp->request_id = i + 1;
467 goto found;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000468 }
469
470 /* Fail back to check from the starting cursor */
Gavin Shana15af542016-10-04 11:25:50 +1100471 for (i = NCSI_REQ_START_IDX; i < ndp->request_id; i++) {
Gavin Shan2d283bd2016-07-19 11:54:16 +1000472 if (ndp->requests[i].used)
473 continue;
474
475 nr = &ndp->requests[i];
476 nr->used = true;
Gavin Shana0509cb2016-10-04 11:25:51 +1100477 nr->flags = req_flags;
Gavin Shana15af542016-10-04 11:25:50 +1100478 ndp->request_id = i + 1;
479 goto found;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000480 }
Gavin Shan2d283bd2016-07-19 11:54:16 +1000481
Gavin Shana15af542016-10-04 11:25:50 +1100482found:
483 spin_unlock_irqrestore(&ndp->lock, flags);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000484 return nr;
485}
486
487void ncsi_free_request(struct ncsi_request *nr)
488{
489 struct ncsi_dev_priv *ndp = nr->ndp;
490 struct sk_buff *cmd, *rsp;
491 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000492 bool driven;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000493
494 if (nr->enabled) {
495 nr->enabled = false;
496 del_timer_sync(&nr->timer);
497 }
498
499 spin_lock_irqsave(&ndp->lock, flags);
500 cmd = nr->cmd;
501 rsp = nr->rsp;
502 nr->cmd = NULL;
503 nr->rsp = NULL;
504 nr->used = false;
Gavin Shana0509cb2016-10-04 11:25:51 +1100505 driven = !!(nr->flags & NCSI_REQ_FLAG_EVENT_DRIVEN);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000506 spin_unlock_irqrestore(&ndp->lock, flags);
507
Gavin Shane6f44ed2016-07-19 11:54:19 +1000508 if (driven && cmd && --ndp->pending_req_num == 0)
509 schedule_work(&ndp->work);
510
Gavin Shan2d283bd2016-07-19 11:54:16 +1000511 /* Release command and response */
512 consume_skb(cmd);
513 consume_skb(rsp);
514}
515
516struct ncsi_dev *ncsi_find_dev(struct net_device *dev)
517{
518 struct ncsi_dev_priv *ndp;
519
520 NCSI_FOR_EACH_DEV(ndp) {
521 if (ndp->ndev.dev == dev)
522 return &ndp->ndev;
523 }
524
525 return NULL;
526}
527
528static void ncsi_request_timeout(unsigned long data)
529{
530 struct ncsi_request *nr = (struct ncsi_request *)data;
531 struct ncsi_dev_priv *ndp = nr->ndp;
532 unsigned long flags;
533
534 /* If the request already had associated response,
535 * let the response handler to release it.
536 */
537 spin_lock_irqsave(&ndp->lock, flags);
538 nr->enabled = false;
539 if (nr->rsp || !nr->cmd) {
540 spin_unlock_irqrestore(&ndp->lock, flags);
541 return;
542 }
543 spin_unlock_irqrestore(&ndp->lock, flags);
544
545 /* Release the request */
546 ncsi_free_request(nr);
547}
548
Gavin Shane6f44ed2016-07-19 11:54:19 +1000549static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
550{
551 struct ncsi_dev *nd = &ndp->ndev;
552 struct ncsi_package *np = ndp->active_package;
553 struct ncsi_channel *nc = ndp->active_channel;
554 struct ncsi_cmd_arg nca;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100555 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000556 int ret;
557
558 nca.ndp = ndp;
Gavin Shana0509cb2016-10-04 11:25:51 +1100559 nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000560 switch (nd->state) {
561 case ncsi_dev_state_suspend:
562 nd->state = ncsi_dev_state_suspend_select;
563 /* Fall through */
564 case ncsi_dev_state_suspend_select:
Gavin Shan7ba5c002016-10-20 11:45:49 +1100565 ndp->pending_req_num = 1;
566
567 nca.type = NCSI_PKT_CMD_SP;
568 nca.package = np->id;
569 nca.channel = NCSI_RESERVED_CHANNEL;
570 if (ndp->flags & NCSI_DEV_HWA)
571 nca.bytes[0] = 0;
572 else
573 nca.bytes[0] = 1;
574
Gavin Shan008a4242016-10-20 11:45:50 +1100575 /* To retrieve the last link states of channels in current
576 * package when current active channel needs fail over to
577 * another one. It means we will possibly select another
578 * channel as next active one. The link states of channels
579 * are most important factor of the selection. So we need
580 * accurate link states. Unfortunately, the link states on
581 * inactive channels can't be updated with LSC AEN in time.
582 */
583 if (ndp->flags & NCSI_DEV_RESHUFFLE)
584 nd->state = ncsi_dev_state_suspend_gls;
585 else
586 nd->state = ncsi_dev_state_suspend_dcnt;
Gavin Shan7ba5c002016-10-20 11:45:49 +1100587 ret = ncsi_xmit_cmd(&nca);
588 if (ret)
589 goto error;
590
591 break;
Gavin Shan008a4242016-10-20 11:45:50 +1100592 case ncsi_dev_state_suspend_gls:
593 ndp->pending_req_num = np->channel_num;
594
595 nca.type = NCSI_PKT_CMD_GLS;
596 nca.package = np->id;
597
598 nd->state = ncsi_dev_state_suspend_dcnt;
599 NCSI_FOR_EACH_CHANNEL(np, nc) {
600 nca.channel = nc->id;
601 ret = ncsi_xmit_cmd(&nca);
602 if (ret)
603 goto error;
604 }
605
606 break;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000607 case ncsi_dev_state_suspend_dcnt:
Gavin Shan7ba5c002016-10-20 11:45:49 +1100608 ndp->pending_req_num = 1;
609
610 nca.type = NCSI_PKT_CMD_DCNT;
611 nca.package = np->id;
612 nca.channel = nc->id;
613
614 nd->state = ncsi_dev_state_suspend_dc;
615 ret = ncsi_xmit_cmd(&nca);
616 if (ret)
617 goto error;
618
619 break;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000620 case ncsi_dev_state_suspend_dc:
Gavin Shan7ba5c002016-10-20 11:45:49 +1100621 ndp->pending_req_num = 1;
622
623 nca.type = NCSI_PKT_CMD_DC;
624 nca.package = np->id;
625 nca.channel = nc->id;
626 nca.bytes[0] = 1;
627
628 nd->state = ncsi_dev_state_suspend_deselect;
629 ret = ncsi_xmit_cmd(&nca);
630 if (ret)
631 goto error;
632
633 break;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000634 case ncsi_dev_state_suspend_deselect:
635 ndp->pending_req_num = 1;
636
Gavin Shan7ba5c002016-10-20 11:45:49 +1100637 nca.type = NCSI_PKT_CMD_DP;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000638 nca.package = np->id;
Gavin Shan7ba5c002016-10-20 11:45:49 +1100639 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000640
Gavin Shan7ba5c002016-10-20 11:45:49 +1100641 nd->state = ncsi_dev_state_suspend_done;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000642 ret = ncsi_xmit_cmd(&nca);
Gavin Shan7ba5c002016-10-20 11:45:49 +1100643 if (ret)
644 goto error;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000645
646 break;
647 case ncsi_dev_state_suspend_done:
Gavin Shand8cedaa2016-10-04 11:25:47 +1100648 spin_lock_irqsave(&nc->lock, flags);
649 nc->state = NCSI_CHANNEL_INACTIVE;
650 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000651 ncsi_process_next_channel(ndp);
652
653 break;
654 default:
655 netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
656 nd->state);
657 }
Gavin Shan7ba5c002016-10-20 11:45:49 +1100658
659 return;
660error:
661 nd->state = ncsi_dev_state_functional;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000662}
663
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000664/* Check the VLAN filter bitmap for a set filter, and construct a
665 * "Set VLAN Filter - Disable" packet if found.
666 */
667static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
668 struct ncsi_cmd_arg *nca)
669{
670 int index;
671 u32 *data;
672 u16 vid;
673
674 index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, NULL);
675 if (index < 0) {
676 /* Filter table empty */
677 return -1;
678 }
679
680 data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, index);
681 if (!data) {
682 netdev_err(ndp->ndev.dev,
683 "ncsi: failed to retrieve filter %d\n", index);
684 /* Set the VLAN id to 0 - this will still disable the entry in
685 * the filter table, but we won't know what it was.
686 */
687 vid = 0;
688 } else {
689 vid = *(u16 *)data;
690 }
691
692 netdev_printk(KERN_DEBUG, ndp->ndev.dev,
693 "ncsi: removed vlan tag %u at index %d\n",
694 vid, index + 1);
695 ncsi_remove_filter(nc, NCSI_FILTER_VLAN, index);
696
697 nca->type = NCSI_PKT_CMD_SVF;
698 nca->words[1] = vid;
699 /* HW filter index starts at 1 */
700 nca->bytes[6] = index + 1;
701 nca->bytes[7] = 0x00;
702 return 0;
703}
704
705/* Find an outstanding VLAN tag and constuct a "Set VLAN Filter - Enable"
706 * packet.
707 */
708static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
709 struct ncsi_cmd_arg *nca)
710{
711 struct vlan_vid *vlan = NULL;
712 int index = 0;
713
714 list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
715 index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, &vlan->vid);
716 if (index < 0) {
717 /* New tag to add */
718 netdev_printk(KERN_DEBUG, ndp->ndev.dev,
719 "ncsi: new vlan id to set: %u\n",
720 vlan->vid);
721 break;
722 }
723 netdev_printk(KERN_DEBUG, ndp->ndev.dev,
724 "vid %u already at filter pos %d\n",
725 vlan->vid, index);
726 }
727
728 if (!vlan || index >= 0) {
729 netdev_printk(KERN_DEBUG, ndp->ndev.dev,
730 "no vlan ids left to set\n");
731 return -1;
732 }
733
734 index = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan->vid);
735 if (index < 0) {
736 netdev_err(ndp->ndev.dev,
737 "Failed to add new VLAN tag, error %d\n", index);
Samuel Mendoza-Jonas6e9c0072017-10-11 16:54:27 +1100738 if (index == -ENOSPC)
739 netdev_err(ndp->ndev.dev,
740 "Channel %u already has all VLAN filters set\n",
741 nc->id);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000742 return -1;
743 }
744
745 netdev_printk(KERN_DEBUG, ndp->ndev.dev,
746 "ncsi: set vid %u in packet, index %u\n",
747 vlan->vid, index + 1);
748 nca->type = NCSI_PKT_CMD_SVF;
749 nca->words[1] = vlan->vid;
750 /* HW filter index starts at 1 */
751 nca->bytes[6] = index + 1;
752 nca->bytes[7] = 0x01;
753
754 return 0;
755}
756
Gavin Shane6f44ed2016-07-19 11:54:19 +1000757static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
758{
759 struct ncsi_dev *nd = &ndp->ndev;
760 struct net_device *dev = nd->dev;
761 struct ncsi_package *np = ndp->active_package;
762 struct ncsi_channel *nc = ndp->active_channel;
Gavin Shanbbc7c012016-10-20 11:45:51 +1100763 struct ncsi_channel *hot_nc = NULL;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000764 struct ncsi_cmd_arg nca;
765 unsigned char index;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100766 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000767 int ret;
768
769 nca.ndp = ndp;
Gavin Shana0509cb2016-10-04 11:25:51 +1100770 nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000771 switch (nd->state) {
772 case ncsi_dev_state_config:
773 case ncsi_dev_state_config_sp:
774 ndp->pending_req_num = 1;
775
776 /* Select the specific package */
777 nca.type = NCSI_PKT_CMD_SP;
778 if (ndp->flags & NCSI_DEV_HWA)
779 nca.bytes[0] = 0;
780 else
781 nca.bytes[0] = 1;
782 nca.package = np->id;
Gavin Shanbc7e0f52016-10-04 11:25:48 +1100783 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000784 ret = ncsi_xmit_cmd(&nca);
785 if (ret)
786 goto error;
787
788 nd->state = ncsi_dev_state_config_cis;
789 break;
790 case ncsi_dev_state_config_cis:
791 ndp->pending_req_num = 1;
792
793 /* Clear initial state */
794 nca.type = NCSI_PKT_CMD_CIS;
795 nca.package = np->id;
796 nca.channel = nc->id;
797 ret = ncsi_xmit_cmd(&nca);
798 if (ret)
799 goto error;
800
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000801 nd->state = ncsi_dev_state_config_clear_vids;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000802 break;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000803 case ncsi_dev_state_config_clear_vids:
804 case ncsi_dev_state_config_svf:
805 case ncsi_dev_state_config_ev:
Gavin Shane6f44ed2016-07-19 11:54:19 +1000806 case ncsi_dev_state_config_sma:
807 case ncsi_dev_state_config_ebf:
808#if IS_ENABLED(CONFIG_IPV6)
809 case ncsi_dev_state_config_egmf:
810#endif
811 case ncsi_dev_state_config_ecnt:
812 case ncsi_dev_state_config_ec:
813 case ncsi_dev_state_config_ae:
814 case ncsi_dev_state_config_gls:
815 ndp->pending_req_num = 1;
816
817 nca.package = np->id;
818 nca.channel = nc->id;
819
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000820 /* Clear any active filters on the channel before setting */
821 if (nd->state == ncsi_dev_state_config_clear_vids) {
822 ret = clear_one_vid(ndp, nc, &nca);
823 if (ret) {
824 nd->state = ncsi_dev_state_config_svf;
825 schedule_work(&ndp->work);
826 break;
827 }
828 /* Repeat */
829 nd->state = ncsi_dev_state_config_clear_vids;
830 /* Add known VLAN tags to the filter */
831 } else if (nd->state == ncsi_dev_state_config_svf) {
832 ret = set_one_vid(ndp, nc, &nca);
833 if (ret) {
834 nd->state = ncsi_dev_state_config_ev;
835 schedule_work(&ndp->work);
836 break;
837 }
838 /* Repeat */
839 nd->state = ncsi_dev_state_config_svf;
840 /* Enable/Disable the VLAN filter */
841 } else if (nd->state == ncsi_dev_state_config_ev) {
842 if (list_empty(&ndp->vlan_vids)) {
843 nca.type = NCSI_PKT_CMD_DV;
844 } else {
845 nca.type = NCSI_PKT_CMD_EV;
846 nca.bytes[3] = NCSI_CAP_VLAN_NO;
847 }
848 nd->state = ncsi_dev_state_config_sma;
849 } else if (nd->state == ncsi_dev_state_config_sma) {
Gavin Shane6f44ed2016-07-19 11:54:19 +1000850 /* Use first entry in unicast filter table. Note that
851 * the MAC filter table starts from entry 1 instead of
852 * 0.
853 */
Gavin Shane6f44ed2016-07-19 11:54:19 +1000854 nca.type = NCSI_PKT_CMD_SMA;
855 for (index = 0; index < 6; index++)
856 nca.bytes[index] = dev->dev_addr[index];
857 nca.bytes[6] = 0x1;
858 nca.bytes[7] = 0x1;
859 nd->state = ncsi_dev_state_config_ebf;
860 } else if (nd->state == ncsi_dev_state_config_ebf) {
861 nca.type = NCSI_PKT_CMD_EBF;
862 nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap;
863 nd->state = ncsi_dev_state_config_ecnt;
864#if IS_ENABLED(CONFIG_IPV6)
865 if (ndp->inet6_addr_num > 0 &&
866 (nc->caps[NCSI_CAP_GENERIC].cap &
867 NCSI_CAP_GENERIC_MC))
868 nd->state = ncsi_dev_state_config_egmf;
869 else
870 nd->state = ncsi_dev_state_config_ecnt;
871 } else if (nd->state == ncsi_dev_state_config_egmf) {
872 nca.type = NCSI_PKT_CMD_EGMF;
873 nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
874 nd->state = ncsi_dev_state_config_ecnt;
875#endif /* CONFIG_IPV6 */
876 } else if (nd->state == ncsi_dev_state_config_ecnt) {
877 nca.type = NCSI_PKT_CMD_ECNT;
878 nd->state = ncsi_dev_state_config_ec;
879 } else if (nd->state == ncsi_dev_state_config_ec) {
880 /* Enable AEN if it's supported */
881 nca.type = NCSI_PKT_CMD_EC;
882 nd->state = ncsi_dev_state_config_ae;
883 if (!(nc->caps[NCSI_CAP_AEN].cap & NCSI_CAP_AEN_MASK))
884 nd->state = ncsi_dev_state_config_gls;
885 } else if (nd->state == ncsi_dev_state_config_ae) {
886 nca.type = NCSI_PKT_CMD_AE;
887 nca.bytes[0] = 0;
888 nca.dwords[1] = nc->caps[NCSI_CAP_AEN].cap;
889 nd->state = ncsi_dev_state_config_gls;
890 } else if (nd->state == ncsi_dev_state_config_gls) {
891 nca.type = NCSI_PKT_CMD_GLS;
892 nd->state = ncsi_dev_state_config_done;
893 }
894
895 ret = ncsi_xmit_cmd(&nca);
896 if (ret)
897 goto error;
898 break;
899 case ncsi_dev_state_config_done:
Gavin Shand8cedaa2016-10-04 11:25:47 +1100900 spin_lock_irqsave(&nc->lock, flags);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000901 if (nc->reconfigure_needed) {
902 /* This channel's configuration has been updated
903 * part-way during the config state - start the
904 * channel configuration over
905 */
906 nc->reconfigure_needed = false;
907 nc->state = NCSI_CHANNEL_INACTIVE;
908 spin_unlock_irqrestore(&nc->lock, flags);
909
910 spin_lock_irqsave(&ndp->lock, flags);
911 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
912 spin_unlock_irqrestore(&ndp->lock, flags);
913
914 netdev_printk(KERN_DEBUG, dev,
915 "Dirty NCSI channel state reset\n");
916 ncsi_process_next_channel(ndp);
917 break;
918 }
919
Gavin Shanbbc7c012016-10-20 11:45:51 +1100920 if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
921 hot_nc = nc;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100922 nc->state = NCSI_CHANNEL_ACTIVE;
Gavin Shanbbc7c012016-10-20 11:45:51 +1100923 } else {
924 hot_nc = NULL;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100925 nc->state = NCSI_CHANNEL_INACTIVE;
Gavin Shanbbc7c012016-10-20 11:45:51 +1100926 }
Gavin Shand8cedaa2016-10-04 11:25:47 +1100927 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000928
Gavin Shanbbc7c012016-10-20 11:45:51 +1100929 /* Update the hot channel */
930 spin_lock_irqsave(&ndp->lock, flags);
931 ndp->hot_channel = hot_nc;
932 spin_unlock_irqrestore(&ndp->lock, flags);
933
Gavin Shane6f44ed2016-07-19 11:54:19 +1000934 ncsi_start_channel_monitor(nc);
935 ncsi_process_next_channel(ndp);
936 break;
937 default:
938 netdev_warn(dev, "Wrong NCSI state 0x%x in config\n",
939 nd->state);
940 }
941
942 return;
943
944error:
945 ncsi_report_link(ndp, true);
946}
947
948static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
949{
950 struct ncsi_package *np;
Gavin Shanbbc7c012016-10-20 11:45:51 +1100951 struct ncsi_channel *nc, *found, *hot_nc;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000952 struct ncsi_channel_mode *ncm;
953 unsigned long flags;
954
Gavin Shanbbc7c012016-10-20 11:45:51 +1100955 spin_lock_irqsave(&ndp->lock, flags);
956 hot_nc = ndp->hot_channel;
957 spin_unlock_irqrestore(&ndp->lock, flags);
958
Gavin Shane6f44ed2016-07-19 11:54:19 +1000959 /* The search is done once an inactive channel with up
960 * link is found.
961 */
962 found = NULL;
963 NCSI_FOR_EACH_PACKAGE(ndp, np) {
964 NCSI_FOR_EACH_CHANNEL(np, nc) {
Gavin Shand8cedaa2016-10-04 11:25:47 +1100965 spin_lock_irqsave(&nc->lock, flags);
966
Gavin Shane6f44ed2016-07-19 11:54:19 +1000967 if (!list_empty(&nc->link) ||
Gavin Shand8cedaa2016-10-04 11:25:47 +1100968 nc->state != NCSI_CHANNEL_INACTIVE) {
969 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000970 continue;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100971 }
Gavin Shane6f44ed2016-07-19 11:54:19 +1000972
973 if (!found)
974 found = nc;
975
Gavin Shanbbc7c012016-10-20 11:45:51 +1100976 if (nc == hot_nc)
977 found = nc;
978
Gavin Shane6f44ed2016-07-19 11:54:19 +1000979 ncm = &nc->modes[NCSI_MODE_LINK];
980 if (ncm->data[2] & 0x1) {
Gavin Shand8cedaa2016-10-04 11:25:47 +1100981 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000982 found = nc;
983 goto out;
984 }
Gavin Shand8cedaa2016-10-04 11:25:47 +1100985
986 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000987 }
988 }
989
990 if (!found) {
991 ncsi_report_link(ndp, true);
992 return -ENODEV;
993 }
994
995out:
996 spin_lock_irqsave(&ndp->lock, flags);
997 list_add_tail_rcu(&found->link, &ndp->channel_queue);
998 spin_unlock_irqrestore(&ndp->lock, flags);
999
1000 return ncsi_process_next_channel(ndp);
1001}
1002
1003static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
1004{
1005 struct ncsi_package *np;
1006 struct ncsi_channel *nc;
1007 unsigned int cap;
Gavin Shan100ef012017-10-19 13:43:07 +11001008 bool has_channel = false;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001009
1010 /* The hardware arbitration is disabled if any one channel
1011 * doesn't support explicitly.
1012 */
1013 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1014 NCSI_FOR_EACH_CHANNEL(np, nc) {
Gavin Shan100ef012017-10-19 13:43:07 +11001015 has_channel = true;
1016
Gavin Shane6f44ed2016-07-19 11:54:19 +10001017 cap = nc->caps[NCSI_CAP_GENERIC].cap;
1018 if (!(cap & NCSI_CAP_GENERIC_HWA) ||
1019 (cap & NCSI_CAP_GENERIC_HWA_MASK) !=
1020 NCSI_CAP_GENERIC_HWA_SUPPORT) {
1021 ndp->flags &= ~NCSI_DEV_HWA;
1022 return false;
1023 }
1024 }
1025 }
1026
Gavin Shan100ef012017-10-19 13:43:07 +11001027 if (has_channel) {
1028 ndp->flags |= NCSI_DEV_HWA;
1029 return true;
1030 }
1031
1032 ndp->flags &= ~NCSI_DEV_HWA;
1033 return false;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001034}
1035
1036static int ncsi_enable_hwa(struct ncsi_dev_priv *ndp)
1037{
1038 struct ncsi_package *np;
1039 struct ncsi_channel *nc;
1040 unsigned long flags;
1041
1042 /* Move all available channels to processing queue */
1043 spin_lock_irqsave(&ndp->lock, flags);
1044 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1045 NCSI_FOR_EACH_CHANNEL(np, nc) {
1046 WARN_ON_ONCE(nc->state != NCSI_CHANNEL_INACTIVE ||
1047 !list_empty(&nc->link));
1048 ncsi_stop_channel_monitor(nc);
1049 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
1050 }
1051 }
1052 spin_unlock_irqrestore(&ndp->lock, flags);
1053
1054 /* We can have no channels in extremely case */
1055 if (list_empty(&ndp->channel_queue)) {
1056 ncsi_report_link(ndp, false);
1057 return -ENOENT;
1058 }
1059
1060 return ncsi_process_next_channel(ndp);
1061}
1062
1063static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
1064{
1065 struct ncsi_dev *nd = &ndp->ndev;
1066 struct ncsi_package *np;
1067 struct ncsi_channel *nc;
1068 struct ncsi_cmd_arg nca;
1069 unsigned char index;
1070 int ret;
1071
1072 nca.ndp = ndp;
Gavin Shana0509cb2016-10-04 11:25:51 +11001073 nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001074 switch (nd->state) {
1075 case ncsi_dev_state_probe:
1076 nd->state = ncsi_dev_state_probe_deselect;
1077 /* Fall through */
1078 case ncsi_dev_state_probe_deselect:
1079 ndp->pending_req_num = 8;
1080
1081 /* Deselect all possible packages */
1082 nca.type = NCSI_PKT_CMD_DP;
Gavin Shanbc7e0f52016-10-04 11:25:48 +11001083 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001084 for (index = 0; index < 8; index++) {
1085 nca.package = index;
1086 ret = ncsi_xmit_cmd(&nca);
1087 if (ret)
1088 goto error;
1089 }
1090
1091 nd->state = ncsi_dev_state_probe_package;
1092 break;
1093 case ncsi_dev_state_probe_package:
1094 ndp->pending_req_num = 16;
1095
1096 /* Select all possible packages */
1097 nca.type = NCSI_PKT_CMD_SP;
1098 nca.bytes[0] = 1;
Gavin Shanbc7e0f52016-10-04 11:25:48 +11001099 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001100 for (index = 0; index < 8; index++) {
1101 nca.package = index;
1102 ret = ncsi_xmit_cmd(&nca);
1103 if (ret)
1104 goto error;
1105 }
1106
1107 /* Disable all possible packages */
1108 nca.type = NCSI_PKT_CMD_DP;
1109 for (index = 0; index < 8; index++) {
1110 nca.package = index;
1111 ret = ncsi_xmit_cmd(&nca);
1112 if (ret)
1113 goto error;
1114 }
1115
1116 nd->state = ncsi_dev_state_probe_channel;
1117 break;
1118 case ncsi_dev_state_probe_channel:
1119 if (!ndp->active_package)
1120 ndp->active_package = list_first_or_null_rcu(
1121 &ndp->packages, struct ncsi_package, node);
1122 else if (list_is_last(&ndp->active_package->node,
1123 &ndp->packages))
1124 ndp->active_package = NULL;
1125 else
1126 ndp->active_package = list_next_entry(
1127 ndp->active_package, node);
1128
1129 /* All available packages and channels are enumerated. The
1130 * enumeration happens for once when the NCSI interface is
1131 * started. So we need continue to start the interface after
1132 * the enumeration.
1133 *
1134 * We have to choose an active channel before configuring it.
1135 * Note that we possibly don't have active channel in extreme
1136 * situation.
1137 */
1138 if (!ndp->active_package) {
1139 ndp->flags |= NCSI_DEV_PROBED;
1140 if (ncsi_check_hwa(ndp))
1141 ncsi_enable_hwa(ndp);
1142 else
1143 ncsi_choose_active_channel(ndp);
1144 return;
1145 }
1146
1147 /* Select the active package */
1148 ndp->pending_req_num = 1;
1149 nca.type = NCSI_PKT_CMD_SP;
1150 nca.bytes[0] = 1;
1151 nca.package = ndp->active_package->id;
Gavin Shanbc7e0f52016-10-04 11:25:48 +11001152 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001153 ret = ncsi_xmit_cmd(&nca);
1154 if (ret)
1155 goto error;
1156
1157 nd->state = ncsi_dev_state_probe_cis;
1158 break;
1159 case ncsi_dev_state_probe_cis:
Gavin Shan55e02d02016-10-04 11:25:49 +11001160 ndp->pending_req_num = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001161
1162 /* Clear initial state */
1163 nca.type = NCSI_PKT_CMD_CIS;
1164 nca.package = ndp->active_package->id;
Gavin Shan55e02d02016-10-04 11:25:49 +11001165 for (index = 0; index < NCSI_RESERVED_CHANNEL; index++) {
Gavin Shane6f44ed2016-07-19 11:54:19 +10001166 nca.channel = index;
1167 ret = ncsi_xmit_cmd(&nca);
1168 if (ret)
1169 goto error;
1170 }
1171
1172 nd->state = ncsi_dev_state_probe_gvi;
1173 break;
1174 case ncsi_dev_state_probe_gvi:
1175 case ncsi_dev_state_probe_gc:
1176 case ncsi_dev_state_probe_gls:
1177 np = ndp->active_package;
1178 ndp->pending_req_num = np->channel_num;
1179
1180 /* Retrieve version, capability or link status */
1181 if (nd->state == ncsi_dev_state_probe_gvi)
1182 nca.type = NCSI_PKT_CMD_GVI;
1183 else if (nd->state == ncsi_dev_state_probe_gc)
1184 nca.type = NCSI_PKT_CMD_GC;
1185 else
1186 nca.type = NCSI_PKT_CMD_GLS;
1187
1188 nca.package = np->id;
1189 NCSI_FOR_EACH_CHANNEL(np, nc) {
1190 nca.channel = nc->id;
1191 ret = ncsi_xmit_cmd(&nca);
1192 if (ret)
1193 goto error;
1194 }
1195
1196 if (nd->state == ncsi_dev_state_probe_gvi)
1197 nd->state = ncsi_dev_state_probe_gc;
1198 else if (nd->state == ncsi_dev_state_probe_gc)
1199 nd->state = ncsi_dev_state_probe_gls;
1200 else
1201 nd->state = ncsi_dev_state_probe_dp;
1202 break;
1203 case ncsi_dev_state_probe_dp:
1204 ndp->pending_req_num = 1;
1205
1206 /* Deselect the active package */
1207 nca.type = NCSI_PKT_CMD_DP;
1208 nca.package = ndp->active_package->id;
Gavin Shanbc7e0f52016-10-04 11:25:48 +11001209 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001210 ret = ncsi_xmit_cmd(&nca);
1211 if (ret)
1212 goto error;
1213
1214 /* Scan channels in next package */
1215 nd->state = ncsi_dev_state_probe_channel;
1216 break;
1217 default:
1218 netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n",
1219 nd->state);
1220 }
1221
1222 return;
1223error:
1224 ncsi_report_link(ndp, true);
1225}
1226
1227static void ncsi_dev_work(struct work_struct *work)
1228{
1229 struct ncsi_dev_priv *ndp = container_of(work,
1230 struct ncsi_dev_priv, work);
1231 struct ncsi_dev *nd = &ndp->ndev;
1232
1233 switch (nd->state & ncsi_dev_state_major) {
1234 case ncsi_dev_state_probe:
1235 ncsi_probe_channel(ndp);
1236 break;
1237 case ncsi_dev_state_suspend:
1238 ncsi_suspend_channel(ndp);
1239 break;
1240 case ncsi_dev_state_config:
1241 ncsi_configure_channel(ndp);
1242 break;
1243 default:
1244 netdev_warn(nd->dev, "Wrong NCSI state 0x%x in workqueue\n",
1245 nd->state);
1246 }
1247}
1248
1249int ncsi_process_next_channel(struct ncsi_dev_priv *ndp)
1250{
1251 struct ncsi_channel *nc;
1252 int old_state;
1253 unsigned long flags;
1254
1255 spin_lock_irqsave(&ndp->lock, flags);
1256 nc = list_first_or_null_rcu(&ndp->channel_queue,
1257 struct ncsi_channel, link);
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001258 if (!nc) {
1259 spin_unlock_irqrestore(&ndp->lock, flags);
1260 goto out;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001261 }
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001262
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001263 list_del_init(&nc->link);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001264 spin_unlock_irqrestore(&ndp->lock, flags);
1265
Gavin Shand8cedaa2016-10-04 11:25:47 +11001266 spin_lock_irqsave(&nc->lock, flags);
1267 old_state = nc->state;
1268 nc->state = NCSI_CHANNEL_INVISIBLE;
1269 spin_unlock_irqrestore(&nc->lock, flags);
1270
Gavin Shane6f44ed2016-07-19 11:54:19 +10001271 ndp->active_channel = nc;
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001272 ndp->active_package = nc->package;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001273
1274 switch (old_state) {
1275 case NCSI_CHANNEL_INACTIVE:
1276 ndp->ndev.state = ncsi_dev_state_config;
1277 ncsi_configure_channel(ndp);
1278 break;
1279 case NCSI_CHANNEL_ACTIVE:
1280 ndp->ndev.state = ncsi_dev_state_suspend;
1281 ncsi_suspend_channel(ndp);
1282 break;
1283 default:
1284 netdev_err(ndp->ndev.dev, "Invalid state 0x%x on %d:%d\n",
Gavin Shand8cedaa2016-10-04 11:25:47 +11001285 old_state, nc->package->id, nc->id);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001286 ncsi_report_link(ndp, false);
1287 return -EINVAL;
1288 }
1289
1290 return 0;
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001291
1292out:
1293 ndp->active_channel = NULL;
1294 ndp->active_package = NULL;
1295 if (ndp->flags & NCSI_DEV_RESHUFFLE) {
1296 ndp->flags &= ~NCSI_DEV_RESHUFFLE;
1297 return ncsi_choose_active_channel(ndp);
1298 }
1299
1300 ncsi_report_link(ndp, false);
1301 return -ENODEV;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001302}
1303
1304#if IS_ENABLED(CONFIG_IPV6)
1305static int ncsi_inet6addr_event(struct notifier_block *this,
1306 unsigned long event, void *data)
1307{
1308 struct inet6_ifaddr *ifa = data;
1309 struct net_device *dev = ifa->idev->dev;
1310 struct ncsi_dev *nd = ncsi_find_dev(dev);
1311 struct ncsi_dev_priv *ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
1312 struct ncsi_package *np;
1313 struct ncsi_channel *nc;
1314 struct ncsi_cmd_arg nca;
1315 bool action;
1316 int ret;
1317
1318 if (!ndp || (ipv6_addr_type(&ifa->addr) &
1319 (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK)))
1320 return NOTIFY_OK;
1321
1322 switch (event) {
1323 case NETDEV_UP:
1324 action = (++ndp->inet6_addr_num) == 1;
1325 nca.type = NCSI_PKT_CMD_EGMF;
1326 break;
1327 case NETDEV_DOWN:
1328 action = (--ndp->inet6_addr_num == 0);
1329 nca.type = NCSI_PKT_CMD_DGMF;
1330 break;
1331 default:
1332 return NOTIFY_OK;
1333 }
1334
1335 /* We might not have active channel or packages. The IPv6
1336 * required multicast will be enabled when active channel
1337 * or packages are chosen.
1338 */
1339 np = ndp->active_package;
1340 nc = ndp->active_channel;
1341 if (!action || !np || !nc)
1342 return NOTIFY_OK;
1343
1344 /* We needn't enable or disable it if the function isn't supported */
1345 if (!(nc->caps[NCSI_CAP_GENERIC].cap & NCSI_CAP_GENERIC_MC))
1346 return NOTIFY_OK;
1347
1348 nca.ndp = ndp;
Gavin Shana0509cb2016-10-04 11:25:51 +11001349 nca.req_flags = 0;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001350 nca.package = np->id;
1351 nca.channel = nc->id;
1352 nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
1353 ret = ncsi_xmit_cmd(&nca);
1354 if (ret) {
1355 netdev_warn(dev, "Fail to %s global multicast filter (%d)\n",
1356 (event == NETDEV_UP) ? "enable" : "disable", ret);
1357 return NOTIFY_DONE;
1358 }
1359
1360 return NOTIFY_OK;
1361}
1362
1363static struct notifier_block ncsi_inet6addr_notifier = {
1364 .notifier_call = ncsi_inet6addr_event,
1365};
1366#endif /* CONFIG_IPV6 */
1367
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001368static int ncsi_kick_channels(struct ncsi_dev_priv *ndp)
1369{
1370 struct ncsi_dev *nd = &ndp->ndev;
1371 struct ncsi_channel *nc;
1372 struct ncsi_package *np;
1373 unsigned long flags;
1374 unsigned int n = 0;
1375
1376 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1377 NCSI_FOR_EACH_CHANNEL(np, nc) {
1378 spin_lock_irqsave(&nc->lock, flags);
1379
1380 /* Channels may be busy, mark dirty instead of
1381 * kicking if;
1382 * a) not ACTIVE (configured)
1383 * b) in the channel_queue (to be configured)
1384 * c) it's ndev is in the config state
1385 */
1386 if (nc->state != NCSI_CHANNEL_ACTIVE) {
1387 if ((ndp->ndev.state & 0xff00) ==
1388 ncsi_dev_state_config ||
1389 !list_empty(&nc->link)) {
1390 netdev_printk(KERN_DEBUG, nd->dev,
1391 "ncsi: channel %p marked dirty\n",
1392 nc);
1393 nc->reconfigure_needed = true;
1394 }
1395 spin_unlock_irqrestore(&nc->lock, flags);
1396 continue;
1397 }
1398
1399 spin_unlock_irqrestore(&nc->lock, flags);
1400
1401 ncsi_stop_channel_monitor(nc);
1402 spin_lock_irqsave(&nc->lock, flags);
1403 nc->state = NCSI_CHANNEL_INACTIVE;
1404 spin_unlock_irqrestore(&nc->lock, flags);
1405
1406 spin_lock_irqsave(&ndp->lock, flags);
1407 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
1408 spin_unlock_irqrestore(&ndp->lock, flags);
1409
1410 netdev_printk(KERN_DEBUG, nd->dev,
1411 "ncsi: kicked channel %p\n", nc);
1412 n++;
1413 }
1414 }
1415
1416 return n;
1417}
1418
1419int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
1420{
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001421 struct ncsi_dev_priv *ndp;
1422 unsigned int n_vids = 0;
1423 struct vlan_vid *vlan;
1424 struct ncsi_dev *nd;
1425 bool found = false;
1426
1427 if (vid == 0)
1428 return 0;
1429
1430 nd = ncsi_find_dev(dev);
1431 if (!nd) {
1432 netdev_warn(dev, "ncsi: No net_device?\n");
1433 return 0;
1434 }
1435
1436 ndp = TO_NCSI_DEV_PRIV(nd);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001437
1438 /* Add the VLAN id to our internal list */
1439 list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
1440 n_vids++;
1441 if (vlan->vid == vid) {
1442 netdev_printk(KERN_DEBUG, dev,
1443 "vid %u already registered\n", vid);
1444 return 0;
1445 }
1446 }
Samuel Mendoza-Jonas6e9c0072017-10-11 16:54:27 +11001447 if (n_vids >= NCSI_MAX_VLAN_VIDS) {
1448 netdev_warn(dev,
1449 "tried to add vlan id %u but NCSI max already registered (%u)\n",
1450 vid, NCSI_MAX_VLAN_VIDS);
1451 return -ENOSPC;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001452 }
1453
1454 vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
1455 if (!vlan)
1456 return -ENOMEM;
1457
1458 vlan->proto = proto;
1459 vlan->vid = vid;
1460 list_add_rcu(&vlan->list, &ndp->vlan_vids);
1461
1462 netdev_printk(KERN_DEBUG, dev, "Added new vid %u\n", vid);
1463
1464 found = ncsi_kick_channels(ndp) != 0;
1465
1466 return found ? ncsi_process_next_channel(ndp) : 0;
1467}
Arnd Bergmannfd0c88b2017-09-05 10:05:47 +02001468EXPORT_SYMBOL_GPL(ncsi_vlan_rx_add_vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001469
1470int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
1471{
1472 struct vlan_vid *vlan, *tmp;
1473 struct ncsi_dev_priv *ndp;
1474 struct ncsi_dev *nd;
1475 bool found = false;
1476
1477 if (vid == 0)
1478 return 0;
1479
1480 nd = ncsi_find_dev(dev);
1481 if (!nd) {
1482 netdev_warn(dev, "ncsi: no net_device?\n");
1483 return 0;
1484 }
1485
1486 ndp = TO_NCSI_DEV_PRIV(nd);
1487
1488 /* Remove the VLAN id from our internal list */
1489 list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list)
1490 if (vlan->vid == vid) {
1491 netdev_printk(KERN_DEBUG, dev,
1492 "vid %u found, removing\n", vid);
1493 list_del_rcu(&vlan->list);
1494 found = true;
1495 kfree(vlan);
1496 }
1497
1498 if (!found) {
1499 netdev_err(dev, "ncsi: vid %u wasn't registered!\n", vid);
1500 return -EINVAL;
1501 }
1502
1503 found = ncsi_kick_channels(ndp) != 0;
1504
1505 return found ? ncsi_process_next_channel(ndp) : 0;
1506}
Arnd Bergmannfd0c88b2017-09-05 10:05:47 +02001507EXPORT_SYMBOL_GPL(ncsi_vlan_rx_kill_vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001508
Gavin Shan2d283bd2016-07-19 11:54:16 +10001509struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
1510 void (*handler)(struct ncsi_dev *ndev))
1511{
1512 struct ncsi_dev_priv *ndp;
1513 struct ncsi_dev *nd;
1514 unsigned long flags;
1515 int i;
1516
1517 /* Check if the device has been registered or not */
1518 nd = ncsi_find_dev(dev);
1519 if (nd)
1520 return nd;
1521
1522 /* Create NCSI device */
1523 ndp = kzalloc(sizeof(*ndp), GFP_ATOMIC);
1524 if (!ndp)
1525 return NULL;
1526
1527 nd = &ndp->ndev;
1528 nd->state = ncsi_dev_state_registered;
1529 nd->dev = dev;
1530 nd->handler = handler;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001531 ndp->pending_req_num = 0;
1532 INIT_LIST_HEAD(&ndp->channel_queue);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001533 INIT_LIST_HEAD(&ndp->vlan_vids);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001534 INIT_WORK(&ndp->work, ncsi_dev_work);
Gavin Shan2d283bd2016-07-19 11:54:16 +10001535
1536 /* Initialize private NCSI device */
1537 spin_lock_init(&ndp->lock);
1538 INIT_LIST_HEAD(&ndp->packages);
Gavin Shana15af542016-10-04 11:25:50 +11001539 ndp->request_id = NCSI_REQ_START_IDX;
Gavin Shan2d283bd2016-07-19 11:54:16 +10001540 for (i = 0; i < ARRAY_SIZE(ndp->requests); i++) {
1541 ndp->requests[i].id = i;
1542 ndp->requests[i].ndp = ndp;
1543 setup_timer(&ndp->requests[i].timer,
1544 ncsi_request_timeout,
1545 (unsigned long)&ndp->requests[i]);
1546 }
1547
1548 spin_lock_irqsave(&ncsi_dev_lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001549#if IS_ENABLED(CONFIG_IPV6)
1550 ndp->inet6_addr_num = 0;
1551 if (list_empty(&ncsi_dev_list))
1552 register_inet6addr_notifier(&ncsi_inet6addr_notifier);
1553#endif
Gavin Shan2d283bd2016-07-19 11:54:16 +10001554 list_add_tail_rcu(&ndp->node, &ncsi_dev_list);
1555 spin_unlock_irqrestore(&ncsi_dev_lock, flags);
1556
Gavin Shane6f44ed2016-07-19 11:54:19 +10001557 /* Register NCSI packet Rx handler */
1558 ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
1559 ndp->ptype.func = ncsi_rcv_rsp;
1560 ndp->ptype.dev = dev;
1561 dev_add_pack(&ndp->ptype);
1562
Gavin Shan2d283bd2016-07-19 11:54:16 +10001563 return nd;
1564}
1565EXPORT_SYMBOL_GPL(ncsi_register_dev);
1566
Gavin Shane6f44ed2016-07-19 11:54:19 +10001567int ncsi_start_dev(struct ncsi_dev *nd)
1568{
1569 struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001570 int ret;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001571
1572 if (nd->state != ncsi_dev_state_registered &&
1573 nd->state != ncsi_dev_state_functional)
1574 return -ENOTTY;
1575
1576 if (!(ndp->flags & NCSI_DEV_PROBED)) {
1577 nd->state = ncsi_dev_state_probe;
1578 schedule_work(&ndp->work);
1579 return 0;
1580 }
1581
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001582 if (ndp->flags & NCSI_DEV_HWA)
1583 ret = ncsi_enable_hwa(ndp);
1584 else
1585 ret = ncsi_choose_active_channel(ndp);
1586
1587 return ret;
1588}
1589EXPORT_SYMBOL_GPL(ncsi_start_dev);
1590
1591void ncsi_stop_dev(struct ncsi_dev *nd)
1592{
1593 struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
1594 struct ncsi_package *np;
1595 struct ncsi_channel *nc;
1596 bool chained;
1597 int old_state;
1598 unsigned long flags;
1599
1600 /* Stop the channel monitor and reset channel's state */
Gavin Shane6f44ed2016-07-19 11:54:19 +10001601 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1602 NCSI_FOR_EACH_CHANNEL(np, nc) {
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001603 ncsi_stop_channel_monitor(nc);
1604
Gavin Shand8cedaa2016-10-04 11:25:47 +11001605 spin_lock_irqsave(&nc->lock, flags);
1606 chained = !list_empty(&nc->link);
1607 old_state = nc->state;
1608 nc->state = NCSI_CHANNEL_INACTIVE;
1609 spin_unlock_irqrestore(&nc->lock, flags);
1610
1611 WARN_ON_ONCE(chained ||
Gavin Shane6f44ed2016-07-19 11:54:19 +10001612 old_state == NCSI_CHANNEL_INVISIBLE);
1613 }
1614 }
1615
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001616 ncsi_report_link(ndp, true);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001617}
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001618EXPORT_SYMBOL_GPL(ncsi_stop_dev);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001619
Gavin Shan2d283bd2016-07-19 11:54:16 +10001620void ncsi_unregister_dev(struct ncsi_dev *nd)
1621{
1622 struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
1623 struct ncsi_package *np, *tmp;
1624 unsigned long flags;
1625
Gavin Shane6f44ed2016-07-19 11:54:19 +10001626 dev_remove_pack(&ndp->ptype);
1627
Gavin Shan2d283bd2016-07-19 11:54:16 +10001628 list_for_each_entry_safe(np, tmp, &ndp->packages, node)
1629 ncsi_remove_package(np);
1630
1631 spin_lock_irqsave(&ncsi_dev_lock, flags);
1632 list_del_rcu(&ndp->node);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001633#if IS_ENABLED(CONFIG_IPV6)
1634 if (list_empty(&ncsi_dev_list))
1635 unregister_inet6addr_notifier(&ncsi_inet6addr_notifier);
1636#endif
Gavin Shan2d283bd2016-07-19 11:54:16 +10001637 spin_unlock_irqrestore(&ncsi_dev_lock, flags);
1638
1639 kfree(ndp);
1640}
1641EXPORT_SYMBOL_GPL(ncsi_unregister_dev);