ACPI: APEI: Fix integer overflow in ghes_estatus_pool_init()
[sfrench/cifs-2.6.git] / drivers / net / ethernet / marvell / prestera / prestera_span.c
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
3
4 #include <linux/kernel.h>
5 #include <linux/list.h>
6
7 #include "prestera.h"
8 #include "prestera_hw.h"
9 #include "prestera_acl.h"
10 #include "prestera_flow.h"
11 #include "prestera_span.h"
12
13 struct prestera_span_entry {
14         struct list_head list;
15         struct prestera_port *port;
16         refcount_t ref_count;
17         u8 id;
18 };
19
20 struct prestera_span {
21         struct prestera_switch *sw;
22         struct list_head entries;
23 };
24
25 static struct prestera_span_entry *
26 prestera_span_entry_create(struct prestera_port *port, u8 span_id)
27 {
28         struct prestera_span_entry *entry;
29
30         entry = kzalloc(sizeof(*entry), GFP_KERNEL);
31         if (!entry)
32                 return ERR_PTR(-ENOMEM);
33
34         refcount_set(&entry->ref_count, 1);
35         entry->port = port;
36         entry->id = span_id;
37         list_add_tail(&entry->list, &port->sw->span->entries);
38
39         return entry;
40 }
41
42 static void prestera_span_entry_del(struct prestera_span_entry *entry)
43 {
44         list_del(&entry->list);
45         kfree(entry);
46 }
47
48 static struct prestera_span_entry *
49 prestera_span_entry_find_by_id(struct prestera_span *span, u8 span_id)
50 {
51         struct prestera_span_entry *entry;
52
53         list_for_each_entry(entry, &span->entries, list) {
54                 if (entry->id == span_id)
55                         return entry;
56         }
57
58         return NULL;
59 }
60
61 static struct prestera_span_entry *
62 prestera_span_entry_find_by_port(struct prestera_span *span,
63                                  struct prestera_port *port)
64 {
65         struct prestera_span_entry *entry;
66
67         list_for_each_entry(entry, &span->entries, list) {
68                 if (entry->port == port)
69                         return entry;
70         }
71
72         return NULL;
73 }
74
75 static int prestera_span_get(struct prestera_port *port, u8 *span_id)
76 {
77         u8 new_span_id;
78         struct prestera_switch *sw = port->sw;
79         struct prestera_span_entry *entry;
80         int err;
81
82         entry = prestera_span_entry_find_by_port(sw->span, port);
83         if (entry) {
84                 refcount_inc(&entry->ref_count);
85                 *span_id = entry->id;
86                 return 0;
87         }
88
89         err = prestera_hw_span_get(port, &new_span_id);
90         if (err)
91                 return err;
92
93         entry = prestera_span_entry_create(port, new_span_id);
94         if (IS_ERR(entry)) {
95                 prestera_hw_span_release(sw, new_span_id);
96                 return PTR_ERR(entry);
97         }
98
99         *span_id = new_span_id;
100         return 0;
101 }
102
103 static int prestera_span_put(struct prestera_switch *sw, u8 span_id)
104 {
105         struct prestera_span_entry *entry;
106         int err;
107
108         entry = prestera_span_entry_find_by_id(sw->span, span_id);
109         if (!entry)
110                 return false;
111
112         if (!refcount_dec_and_test(&entry->ref_count))
113                 return 0;
114
115         err = prestera_hw_span_release(sw, span_id);
116         if (err)
117                 return err;
118
119         prestera_span_entry_del(entry);
120         return 0;
121 }
122
123 int prestera_span_rule_add(struct prestera_flow_block_binding *binding,
124                            struct prestera_port *to_port,
125                            bool ingress)
126 {
127         struct prestera_switch *sw = binding->port->sw;
128         u8 span_id;
129         int err;
130
131         if (binding->span_id != PRESTERA_SPAN_INVALID_ID)
132                 /* port already in mirroring */
133                 return -EEXIST;
134
135         err = prestera_span_get(to_port, &span_id);
136         if (err)
137                 return err;
138
139         err = prestera_hw_span_bind(binding->port, span_id, ingress);
140         if (err) {
141                 prestera_span_put(sw, span_id);
142                 return err;
143         }
144
145         binding->span_id = span_id;
146         return 0;
147 }
148
149 int prestera_span_rule_del(struct prestera_flow_block_binding *binding,
150                            bool ingress)
151 {
152         int err;
153
154         err = prestera_hw_span_unbind(binding->port, ingress);
155         if (err)
156                 return err;
157
158         err = prestera_span_put(binding->port->sw, binding->span_id);
159         if (err)
160                 return err;
161
162         binding->span_id = PRESTERA_SPAN_INVALID_ID;
163         return 0;
164 }
165
166 int prestera_span_init(struct prestera_switch *sw)
167 {
168         struct prestera_span *span;
169
170         span = kzalloc(sizeof(*span), GFP_KERNEL);
171         if (!span)
172                 return -ENOMEM;
173
174         INIT_LIST_HEAD(&span->entries);
175
176         sw->span = span;
177         span->sw = sw;
178
179         return 0;
180 }
181
182 void prestera_span_fini(struct prestera_switch *sw)
183 {
184         struct prestera_span *span = sw->span;
185
186         WARN_ON(!list_empty(&span->entries));
187         kfree(span);
188 }