summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorSamuel Mendoza-Jonas <sam@mendozajonas.com>2018-04-17 00:23:23 -0400
committerDavid S. Miller <davem@davemloft.net>2018-04-17 13:50:58 -0400
commit062b3e1b6d4f2a33c1d0fd7ae9b4550da5cf7e4b (patch)
tree21ce411c781f4162e64fca32804bc12376bedd9b /net
parentc210f7b411790ee0fc4252547d645c5d8648adc5 (diff)
net/ncsi: Refactor MAC, VLAN filters
The NCSI driver defines a generic ncsi_channel_filter struct that can be used to store arbitrarily formatted filters, and several generic methods of accessing data stored in such a filter. However in both the driver and as defined in the NCSI specification there are only two actual filters: VLAN ID filters and MAC address filters. The splitting of the MAC filter into unicast, multicast, and mixed is also technically not necessary as these are stored in the same location in hardware. To save complexity, particularly in the set up and accessing of these generic filters, remove them in favour of two specific structs. These can be acted on directly and do not need several generic helper functions to use. This also fixes a memory error found by KASAN on ARM32 (which is not upstream yet), where response handlers accessing a filter's data field could write past allocated memory. [ 114.926512] ================================================================== [ 114.933861] BUG: KASAN: slab-out-of-bounds in ncsi_configure_channel+0x4b8/0xc58 [ 114.941304] Read of size 2 at addr 94888558 by task kworker/0:2/546 [ 114.947593] [ 114.949146] CPU: 0 PID: 546 Comm: kworker/0:2 Not tainted 4.16.0-rc6-00119-ge156398bfcad #13 ... [ 115.170233] The buggy address belongs to the object at 94888540 [ 115.170233] which belongs to the cache kmalloc-32 of size 32 [ 115.181917] The buggy address is located 24 bytes inside of [ 115.181917] 32-byte region [94888540, 94888560) [ 115.192115] The buggy address belongs to the page: [ 115.196943] page:9eeac100 count:1 mapcount:0 mapping:94888000 index:0x94888fc1 [ 115.204200] flags: 0x100(slab) [ 115.207330] raw: 00000100 94888000 94888fc1 0000003f 00000001 9eea2014 9eecaa74 96c003e0 [ 115.215444] page dumped because: kasan: bad access detected [ 115.221036] [ 115.222544] Memory state around the buggy address: [ 115.227384] 94888400: fb fb fb fb fc fc fc fc 04 fc fc fc fc fc fc fc [ 115.233959] 94888480: 00 00 00 fc fc fc fc fc 00 04 fc fc fc fc fc fc [ 115.240529] >94888500: 00 00 04 fc fc fc fc fc 00 00 04 fc fc fc fc fc [ 115.247077] ^ [ 115.252523] 94888580: 00 04 fc fc fc fc fc fc 06 fc fc fc fc fc fc fc [ 115.259093] 94888600: 00 00 06 fc fc fc fc fc 00 00 04 fc fc fc fc fc [ 115.265639] ================================================================== Reported-by: Joel Stanley <joel@jms.id.au> Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ncsi/internal.h34
-rw-r--r--net/ncsi/ncsi-manage.c226
-rw-r--r--net/ncsi/ncsi-netlink.c20
-rw-r--r--net/ncsi/ncsi-rsp.c178
4 files changed, 147 insertions, 311 deletions
diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index 8da84312cd3b..8055e3965cef 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -68,15 +68,6 @@ enum {
68 NCSI_MODE_MAX 68 NCSI_MODE_MAX
69}; 69};
70 70
71enum {
72 NCSI_FILTER_BASE = 0,
73 NCSI_FILTER_VLAN = 0,
74 NCSI_FILTER_UC,
75 NCSI_FILTER_MC,
76 NCSI_FILTER_MIXED,
77 NCSI_FILTER_MAX
78};
79
80struct ncsi_channel_version { 71struct ncsi_channel_version {
81 u32 version; /* Supported BCD encoded NCSI version */ 72 u32 version; /* Supported BCD encoded NCSI version */
82 u32 alpha2; /* Supported BCD encoded NCSI version */ 73 u32 alpha2; /* Supported BCD encoded NCSI version */
@@ -98,11 +89,18 @@ struct ncsi_channel_mode {
98 u32 data[8]; /* Data entries */ 89 u32 data[8]; /* Data entries */
99}; 90};
100 91
101struct ncsi_channel_filter { 92struct ncsi_channel_mac_filter {
102 u32 index; /* Index of channel filters */ 93 u8 n_uc;
103 u32 total; /* Total entries in the filter table */ 94 u8 n_mc;
104 u64 bitmap; /* Bitmap of valid entries */ 95 u8 n_mixed;
105 u32 data[]; /* Data for the valid entries */ 96 u64 bitmap;
97 unsigned char *addrs;
98};
99
100struct ncsi_channel_vlan_filter {
101 u8 n_vids;
102 u64 bitmap;
103 u16 *vids;
106}; 104};
107 105
108struct ncsi_channel_stats { 106struct ncsi_channel_stats {
@@ -186,7 +184,9 @@ struct ncsi_channel {
186 struct ncsi_channel_version version; 184 struct ncsi_channel_version version;
187 struct ncsi_channel_cap caps[NCSI_CAP_MAX]; 185 struct ncsi_channel_cap caps[NCSI_CAP_MAX];
188 struct ncsi_channel_mode modes[NCSI_MODE_MAX]; 186 struct ncsi_channel_mode modes[NCSI_MODE_MAX];
189 struct ncsi_channel_filter *filters[NCSI_FILTER_MAX]; 187 /* Filtering Settings */
188 struct ncsi_channel_mac_filter mac_filter;
189 struct ncsi_channel_vlan_filter vlan_filter;
190 struct ncsi_channel_stats stats; 190 struct ncsi_channel_stats stats;
191 struct { 191 struct {
192 struct timer_list timer; 192 struct timer_list timer;
@@ -320,10 +320,6 @@ extern spinlock_t ncsi_dev_lock;
320 list_for_each_entry_rcu(nc, &np->channels, node) 320 list_for_each_entry_rcu(nc, &np->channels, node)
321 321
322/* Resources */ 322/* Resources */
323u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index);
324int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data);
325int ncsi_add_filter(struct ncsi_channel *nc, int table, void *data);
326int ncsi_remove_filter(struct ncsi_channel *nc, int table, int index);
327void ncsi_start_channel_monitor(struct ncsi_channel *nc); 323void ncsi_start_channel_monitor(struct ncsi_channel *nc);
328void ncsi_stop_channel_monitor(struct ncsi_channel *nc); 324void ncsi_stop_channel_monitor(struct ncsi_channel *nc);
329struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np, 325struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index c3695ba0cf94..5561e221b71f 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -27,125 +27,6 @@
27LIST_HEAD(ncsi_dev_list); 27LIST_HEAD(ncsi_dev_list);
28DEFINE_SPINLOCK(ncsi_dev_lock); 28DEFINE_SPINLOCK(ncsi_dev_lock);
29 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
41u32 *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 */
60int 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) {
80 if (!data || !memcmp(ncf->data + size * index, data, size)) {
81 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
149static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) 30static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
150{ 31{
151 struct ncsi_dev *nd = &ndp->ndev; 32 struct ncsi_dev *nd = &ndp->ndev;
@@ -339,20 +220,13 @@ struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
339static void ncsi_remove_channel(struct ncsi_channel *nc) 220static void ncsi_remove_channel(struct ncsi_channel *nc)
340{ 221{
341 struct ncsi_package *np = nc->package; 222 struct ncsi_package *np = nc->package;
342 struct ncsi_channel_filter *ncf;
343 unsigned long flags; 223 unsigned long flags;
344 int i;
345 224
346 /* Release filters */
347 spin_lock_irqsave(&nc->lock, flags); 225 spin_lock_irqsave(&nc->lock, flags);
348 for (i = 0; i < NCSI_FILTER_MAX; i++) {
349 ncf = nc->filters[i];
350 if (!ncf)
351 continue;
352 226
353 nc->filters[i] = NULL; 227 /* Release filters */
354 kfree(ncf); 228 kfree(nc->mac_filter.addrs);
355 } 229 kfree(nc->vlan_filter.vids);
356 230
357 nc->state = NCSI_CHANNEL_INACTIVE; 231 nc->state = NCSI_CHANNEL_INACTIVE;
358 spin_unlock_irqrestore(&nc->lock, flags); 232 spin_unlock_irqrestore(&nc->lock, flags);
@@ -670,32 +544,26 @@ error:
670static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc, 544static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
671 struct ncsi_cmd_arg *nca) 545 struct ncsi_cmd_arg *nca)
672{ 546{
547 struct ncsi_channel_vlan_filter *ncf;
548 unsigned long flags;
549 void *bitmap;
673 int index; 550 int index;
674 u32 *data;
675 u16 vid; 551 u16 vid;
676 552
677 index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, NULL); 553 ncf = &nc->vlan_filter;
678 if (index < 0) { 554 bitmap = &ncf->bitmap;
679 /* Filter table empty */
680 return -1;
681 }
682 555
683 data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, index); 556 spin_lock_irqsave(&nc->lock, flags);
684 if (!data) { 557 index = find_next_bit(bitmap, ncf->n_vids, 0);
685 netdev_err(ndp->ndev.dev, 558 if (index >= ncf->n_vids) {
686 "NCSI: failed to retrieve filter %d\n", index); 559 spin_unlock_irqrestore(&nc->lock, flags);
687 /* Set the VLAN id to 0 - this will still disable the entry in 560 return -1;
688 * the filter table, but we won't know what it was.
689 */
690 vid = 0;
691 } else {
692 vid = *(u16 *)data;
693 } 561 }
562 vid = ncf->vids[index];
694 563
695 netdev_printk(KERN_DEBUG, ndp->ndev.dev, 564 clear_bit(index, bitmap);
696 "NCSI: removed vlan tag %u at index %d\n", 565 ncf->vids[index] = 0;
697 vid, index + 1); 566 spin_unlock_irqrestore(&nc->lock, flags);
698 ncsi_remove_filter(nc, NCSI_FILTER_VLAN, index);
699 567
700 nca->type = NCSI_PKT_CMD_SVF; 568 nca->type = NCSI_PKT_CMD_SVF;
701 nca->words[1] = vid; 569 nca->words[1] = vid;
@@ -711,45 +579,55 @@ static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
711static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc, 579static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
712 struct ncsi_cmd_arg *nca) 580 struct ncsi_cmd_arg *nca)
713{ 581{
582 struct ncsi_channel_vlan_filter *ncf;
714 struct vlan_vid *vlan = NULL; 583 struct vlan_vid *vlan = NULL;
715 int index = 0; 584 unsigned long flags;
585 int i, index;
586 void *bitmap;
587 u16 vid;
716 588
589 if (list_empty(&ndp->vlan_vids))
590 return -1;
591
592 ncf = &nc->vlan_filter;
593 bitmap = &ncf->bitmap;
594
595 spin_lock_irqsave(&nc->lock, flags);
596
597 rcu_read_lock();
717 list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) { 598 list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
718 index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, &vlan->vid); 599 vid = vlan->vid;
719 if (index < 0) { 600 for (i = 0; i < ncf->n_vids; i++)
720 /* New tag to add */ 601 if (ncf->vids[i] == vid) {
721 netdev_printk(KERN_DEBUG, ndp->ndev.dev, 602 vid = 0;
722 "NCSI: new vlan id to set: %u\n", 603 break;
723 vlan->vid); 604 }
605 if (vid)
724 break; 606 break;
725 }
726 netdev_printk(KERN_DEBUG, ndp->ndev.dev,
727 "vid %u already at filter pos %d\n",
728 vlan->vid, index);
729 } 607 }
608 rcu_read_unlock();
730 609
731 if (!vlan || index >= 0) { 610 if (!vid) {
732 netdev_printk(KERN_DEBUG, ndp->ndev.dev, 611 /* No VLAN ID is not set */
733 "no vlan ids left to set\n"); 612 spin_unlock_irqrestore(&nc->lock, flags);
734 return -1; 613 return -1;
735 } 614 }
736 615
737 index = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan->vid); 616 index = find_next_zero_bit(bitmap, ncf->n_vids, 0);
738 if (index < 0) { 617 if (index < 0 || index >= ncf->n_vids) {
739 netdev_err(ndp->ndev.dev, 618 netdev_err(ndp->ndev.dev,
740 "Failed to add new VLAN tag, error %d\n", index); 619 "Channel %u already has all VLAN filters set\n",
741 if (index == -ENOSPC) 620 nc->id);
742 netdev_err(ndp->ndev.dev, 621 spin_unlock_irqrestore(&nc->lock, flags);
743 "Channel %u already has all VLAN filters set\n",
744 nc->id);
745 return -1; 622 return -1;
746 } 623 }
747 624
748 netdev_printk(KERN_DEBUG, ndp->ndev.dev, 625 ncf->vids[index] = vid;
749 "NCSI: set vid %u in packet, index %u\n", 626 set_bit(index, bitmap);
750 vlan->vid, index + 1); 627 spin_unlock_irqrestore(&nc->lock, flags);
628
751 nca->type = NCSI_PKT_CMD_SVF; 629 nca->type = NCSI_PKT_CMD_SVF;
752 nca->words[1] = vlan->vid; 630 nca->words[1] = vid;
753 /* HW filter index starts at 1 */ 631 /* HW filter index starts at 1 */
754 nca->bytes[6] = index + 1; 632 nca->bytes[6] = index + 1;
755 nca->bytes[7] = 0x01; 633 nca->bytes[7] = 0x01;
diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c
index 8d7e849d4825..b09ef77bf4cd 100644
--- a/net/ncsi/ncsi-netlink.c
+++ b/net/ncsi/ncsi-netlink.c
@@ -58,10 +58,9 @@ static int ncsi_write_channel_info(struct sk_buff *skb,
58 struct ncsi_dev_priv *ndp, 58 struct ncsi_dev_priv *ndp,
59 struct ncsi_channel *nc) 59 struct ncsi_channel *nc)
60{ 60{
61 struct nlattr *vid_nest; 61 struct ncsi_channel_vlan_filter *ncf;
62 struct ncsi_channel_filter *ncf;
63 struct ncsi_channel_mode *m; 62 struct ncsi_channel_mode *m;
64 u32 *data; 63 struct nlattr *vid_nest;
65 int i; 64 int i;
66 65
67 nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id); 66 nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id);
@@ -79,18 +78,13 @@ static int ncsi_write_channel_info(struct sk_buff *skb,
79 vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST); 78 vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST);
80 if (!vid_nest) 79 if (!vid_nest)
81 return -ENOMEM; 80 return -ENOMEM;
82 ncf = nc->filters[NCSI_FILTER_VLAN]; 81 ncf = &nc->vlan_filter;
83 i = -1; 82 i = -1;
84 if (ncf) { 83 while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids,
85 while ((i = find_next_bit((void *)&ncf->bitmap, ncf->total, 84 i + 1)) < ncf->n_vids) {
86 i + 1)) < ncf->total) { 85 if (ncf->vids[i])
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, 86 nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID,
92 *(u16 *)data); 87 ncf->vids[i]);
93 }
94 } 88 }
95 nla_nest_end(skb, vid_nest); 89 nla_nest_end(skb, vid_nest);
96 90
diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
index efd933ff5570..ce9497966ebe 100644
--- a/net/ncsi/ncsi-rsp.c
+++ b/net/ncsi/ncsi-rsp.c
@@ -334,9 +334,9 @@ static int ncsi_rsp_handler_svf(struct ncsi_request *nr)
334 struct ncsi_rsp_pkt *rsp; 334 struct ncsi_rsp_pkt *rsp;
335 struct ncsi_dev_priv *ndp = nr->ndp; 335 struct ncsi_dev_priv *ndp = nr->ndp;
336 struct ncsi_channel *nc; 336 struct ncsi_channel *nc;
337 struct ncsi_channel_filter *ncf; 337 struct ncsi_channel_vlan_filter *ncf;
338 unsigned short vlan; 338 unsigned long flags;
339 int ret; 339 void *bitmap;
340 340
341 /* Find the package and channel */ 341 /* Find the package and channel */
342 rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp); 342 rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
@@ -346,22 +346,23 @@ static int ncsi_rsp_handler_svf(struct ncsi_request *nr)
346 return -ENODEV; 346 return -ENODEV;
347 347
348 cmd = (struct ncsi_cmd_svf_pkt *)skb_network_header(nr->cmd); 348 cmd = (struct ncsi_cmd_svf_pkt *)skb_network_header(nr->cmd);
349 ncf = nc->filters[NCSI_FILTER_VLAN]; 349 ncf = &nc->vlan_filter;
350 if (!ncf) 350 if (cmd->index > ncf->n_vids)
351 return -ENOENT;
352 if (cmd->index >= ncf->total)
353 return -ERANGE; 351 return -ERANGE;
354 352
355 /* Add or remove the VLAN filter */ 353 /* Add or remove the VLAN filter. Remember HW indexes from 1 */
354 spin_lock_irqsave(&nc->lock, flags);
355 bitmap = &ncf->bitmap;
356 if (!(cmd->enable & 0x1)) { 356 if (!(cmd->enable & 0x1)) {
357 /* HW indexes from 1 */ 357 if (test_and_clear_bit(cmd->index - 1, bitmap))
358 ret = ncsi_remove_filter(nc, NCSI_FILTER_VLAN, cmd->index - 1); 358 ncf->vids[cmd->index - 1] = 0;
359 } else { 359 } else {
360 vlan = ntohs(cmd->vlan); 360 set_bit(cmd->index - 1, bitmap);
361 ret = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan); 361 ncf->vids[cmd->index - 1] = ntohs(cmd->vlan);
362 } 362 }
363 spin_unlock_irqrestore(&nc->lock, flags);
363 364
364 return ret; 365 return 0;
365} 366}
366 367
367static int ncsi_rsp_handler_ev(struct ncsi_request *nr) 368static int ncsi_rsp_handler_ev(struct ncsi_request *nr)
@@ -422,8 +423,12 @@ static int ncsi_rsp_handler_sma(struct ncsi_request *nr)
422 struct ncsi_rsp_pkt *rsp; 423 struct ncsi_rsp_pkt *rsp;
423 struct ncsi_dev_priv *ndp = nr->ndp; 424 struct ncsi_dev_priv *ndp = nr->ndp;
424 struct ncsi_channel *nc; 425 struct ncsi_channel *nc;
425 struct ncsi_channel_filter *ncf; 426 struct ncsi_channel_mac_filter *ncf;
427 unsigned long flags;
426 void *bitmap; 428 void *bitmap;
429 bool enabled;
430 int index;
431
427 432
428 /* Find the package and channel */ 433 /* Find the package and channel */
429 rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp); 434 rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
@@ -436,31 +441,23 @@ static int ncsi_rsp_handler_sma(struct ncsi_request *nr)
436 * isn't supported yet. 441 * isn't supported yet.
437 */ 442 */
438 cmd = (struct ncsi_cmd_sma_pkt *)skb_network_header(nr->cmd); 443 cmd = (struct ncsi_cmd_sma_pkt *)skb_network_header(nr->cmd);
439 switch (cmd->at_e >> 5) { 444 enabled = cmd->at_e & 0x1;
440 case 0x0: /* UC address */ 445 ncf = &nc->mac_filter;
441 ncf = nc->filters[NCSI_FILTER_UC]; 446 bitmap = &ncf->bitmap;
442 break;
443 case 0x1: /* MC address */
444 ncf = nc->filters[NCSI_FILTER_MC];
445 break;
446 default:
447 return -EINVAL;
448 }
449 447
450 /* Sanity check on the filter */ 448 if (cmd->index > ncf->n_uc + ncf->n_mc + ncf->n_mixed)
451 if (!ncf)
452 return -ENOENT;
453 else if (cmd->index >= ncf->total)
454 return -ERANGE; 449 return -ERANGE;
455 450
456 bitmap = &ncf->bitmap; 451 index = (cmd->index - 1) * ETH_ALEN;
457 if (cmd->at_e & 0x1) { 452 spin_lock_irqsave(&nc->lock, flags);
458 set_bit(cmd->index, bitmap); 453 if (enabled) {
459 memcpy(ncf->data + 6 * cmd->index, cmd->mac, 6); 454 set_bit(cmd->index - 1, bitmap);
455 memcpy(&ncf->addrs[index], cmd->mac, ETH_ALEN);
460 } else { 456 } else {
461 clear_bit(cmd->index, bitmap); 457 clear_bit(cmd->index - 1, bitmap);
462 memset(ncf->data + 6 * cmd->index, 0, 6); 458 memset(&ncf->addrs[index], 0, ETH_ALEN);
463 } 459 }
460 spin_unlock_irqrestore(&nc->lock, flags);
464 461
465 return 0; 462 return 0;
466} 463}
@@ -631,9 +628,7 @@ static int ncsi_rsp_handler_gc(struct ncsi_request *nr)
631 struct ncsi_rsp_gc_pkt *rsp; 628 struct ncsi_rsp_gc_pkt *rsp;
632 struct ncsi_dev_priv *ndp = nr->ndp; 629 struct ncsi_dev_priv *ndp = nr->ndp;
633 struct ncsi_channel *nc; 630 struct ncsi_channel *nc;
634 struct ncsi_channel_filter *ncf; 631 size_t size;
635 size_t size, entry_size;
636 int cnt, i;
637 632
638 /* Find the channel */ 633 /* Find the channel */
639 rsp = (struct ncsi_rsp_gc_pkt *)skb_network_header(nr->rsp); 634 rsp = (struct ncsi_rsp_gc_pkt *)skb_network_header(nr->rsp);
@@ -655,64 +650,40 @@ static int ncsi_rsp_handler_gc(struct ncsi_request *nr)
655 nc->caps[NCSI_CAP_VLAN].cap = rsp->vlan_mode & 650 nc->caps[NCSI_CAP_VLAN].cap = rsp->vlan_mode &
656 NCSI_CAP_VLAN_MASK; 651 NCSI_CAP_VLAN_MASK;
657 652
658 /* Build filters */ 653 size = (rsp->uc_cnt + rsp->mc_cnt + rsp->mixed_cnt) * ETH_ALEN;
659 for (i = 0; i < NCSI_FILTER_MAX; i++) { 654 nc->mac_filter.addrs = kzalloc(size, GFP_KERNEL);
660 switch (i) { 655 if (!nc->mac_filter.addrs)
661 case NCSI_FILTER_VLAN: 656 return -ENOMEM;
662 cnt = rsp->vlan_cnt; 657 nc->mac_filter.n_uc = rsp->uc_cnt;
663 entry_size = 2; 658 nc->mac_filter.n_mc = rsp->mc_cnt;
664 break; 659 nc->mac_filter.n_mixed = rsp->mixed_cnt;
665 case NCSI_FILTER_MIXED: 660
666 cnt = rsp->mixed_cnt; 661 nc->vlan_filter.vids = kcalloc(rsp->vlan_cnt,
667 entry_size = 6; 662 sizeof(*nc->vlan_filter.vids),
668 break; 663 GFP_KERNEL);
669 case NCSI_FILTER_MC: 664 if (!nc->vlan_filter.vids)
670 cnt = rsp->mc_cnt; 665 return -ENOMEM;
671 entry_size = 6; 666 /* Set VLAN filters active so they are cleared in the first
672 break; 667 * configuration state
673 case NCSI_FILTER_UC: 668 */
674 cnt = rsp->uc_cnt; 669 nc->vlan_filter.bitmap = U64_MAX;
675 entry_size = 6; 670 nc->vlan_filter.n_vids = rsp->vlan_cnt;
676 break;
677 default:
678 continue;
679 }
680
681 if (!cnt || nc->filters[i])
682 continue;
683
684 size = sizeof(*ncf) + cnt * entry_size;
685 ncf = kzalloc(size, GFP_ATOMIC);
686 if (!ncf) {
687 pr_warn("%s: Cannot alloc filter table (%d)\n",
688 __func__, i);
689 return -ENOMEM;
690 }
691
692 ncf->index = i;
693 ncf->total = cnt;
694 if (i == NCSI_FILTER_VLAN) {
695 /* Set VLAN filters active so they are cleared in
696 * first configuration state
697 */
698 ncf->bitmap = U64_MAX;
699 } else {
700 ncf->bitmap = 0x0ul;
701 }
702 nc->filters[i] = ncf;
703 }
704 671
705 return 0; 672 return 0;
706} 673}
707 674
708static int ncsi_rsp_handler_gp(struct ncsi_request *nr) 675static int ncsi_rsp_handler_gp(struct ncsi_request *nr)
709{ 676{
710 struct ncsi_rsp_gp_pkt *rsp; 677 struct ncsi_channel_vlan_filter *ncvf;
678 struct ncsi_channel_mac_filter *ncmf;
711 struct ncsi_dev_priv *ndp = nr->ndp; 679 struct ncsi_dev_priv *ndp = nr->ndp;
680 struct ncsi_rsp_gp_pkt *rsp;
712 struct ncsi_channel *nc; 681 struct ncsi_channel *nc;
713 unsigned short enable, vlan; 682 unsigned short enable;
714 unsigned char *pdata; 683 unsigned char *pdata;
715 int table, i; 684 unsigned long flags;
685 void *bitmap;
686 int i;
716 687
717 /* Find the channel */ 688 /* Find the channel */
718 rsp = (struct ncsi_rsp_gp_pkt *)skb_network_header(nr->rsp); 689 rsp = (struct ncsi_rsp_gp_pkt *)skb_network_header(nr->rsp);
@@ -746,36 +717,33 @@ static int ncsi_rsp_handler_gp(struct ncsi_request *nr)
746 /* MAC addresses filter table */ 717 /* MAC addresses filter table */
747 pdata = (unsigned char *)rsp + 48; 718 pdata = (unsigned char *)rsp + 48;
748 enable = rsp->mac_enable; 719 enable = rsp->mac_enable;
720 ncmf = &nc->mac_filter;
721 spin_lock_irqsave(&nc->lock, flags);
722 bitmap = &ncmf->bitmap;
749 for (i = 0; i < rsp->mac_cnt; i++, pdata += 6) { 723 for (i = 0; i < rsp->mac_cnt; i++, pdata += 6) {
750 if (i >= (nc->filters[NCSI_FILTER_UC]->total +
751 nc->filters[NCSI_FILTER_MC]->total))
752 table = NCSI_FILTER_MIXED;
753 else if (i >= nc->filters[NCSI_FILTER_UC]->total)
754 table = NCSI_FILTER_MC;
755 else
756 table = NCSI_FILTER_UC;
757
758 if (!(enable & (0x1 << i))) 724 if (!(enable & (0x1 << i)))
759 continue; 725 clear_bit(i, bitmap);
760 726 else
761 if (ncsi_find_filter(nc, table, pdata) >= 0) 727 set_bit(i, bitmap);
762 continue;
763 728
764 ncsi_add_filter(nc, table, pdata); 729 memcpy(&ncmf->addrs[i * ETH_ALEN], pdata, ETH_ALEN);
765 } 730 }
731 spin_unlock_irqrestore(&nc->lock, flags);
766 732
767 /* VLAN filter table */ 733 /* VLAN filter table */
768 enable = ntohs(rsp->vlan_enable); 734 enable = ntohs(rsp->vlan_enable);
735 ncvf = &nc->vlan_filter;
736 bitmap = &ncvf->bitmap;
737 spin_lock_irqsave(&nc->lock, flags);
769 for (i = 0; i < rsp->vlan_cnt; i++, pdata += 2) { 738 for (i = 0; i < rsp->vlan_cnt; i++, pdata += 2) {
770 if (!(enable & (0x1 << i))) 739 if (!(enable & (0x1 << i)))
771 continue; 740 clear_bit(i, bitmap);
772 741 else
773 vlan = ntohs(*(__be16 *)pdata); 742 set_bit(i, bitmap);
774 if (ncsi_find_filter(nc, NCSI_FILTER_VLAN, &vlan) >= 0)
775 continue;
776 743
777 ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan); 744 ncvf->vids[i] = ntohs(*(__be16 *)pdata);
778 } 745 }
746 spin_unlock_irqrestore(&nc->lock, flags);
779 747
780 return 0; 748 return 0;
781} 749}