1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright IBM Corp. 2013 |
4 | * Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com> |
5 | */ |
6 | |
7 | #include <linux/slab.h> |
8 | #include <asm/ebcdic.h> |
9 | #include "qeth_core.h" |
10 | #include "qeth_l2.h" |
11 | |
12 | static ssize_t qeth_bridge_port_role_state_show(struct device *dev, |
13 | struct device_attribute *attr, char *buf, |
14 | int show_state) |
15 | { |
16 | struct qeth_card *card = dev_get_drvdata(dev); |
17 | enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE; |
18 | int rc = 0; |
19 | char *word; |
20 | |
21 | if (!qeth_bridgeport_allowed(card)) |
22 | return sysfs_emit(buf, fmt: "n/a (VNIC characteristics)\n" ); |
23 | |
24 | mutex_lock(&card->sbp_lock); |
25 | if (qeth_card_hw_is_reachable(card) && |
26 | card->options.sbp.supported_funcs) |
27 | rc = qeth_bridgeport_query_ports(card, |
28 | role: &card->options.sbp.role, state: &state); |
29 | if (!rc) { |
30 | if (show_state) |
31 | switch (state) { |
32 | case QETH_SBP_STATE_INACTIVE: |
33 | word = "inactive" ; break; |
34 | case QETH_SBP_STATE_STANDBY: |
35 | word = "standby" ; break; |
36 | case QETH_SBP_STATE_ACTIVE: |
37 | word = "active" ; break; |
38 | default: |
39 | rc = -EIO; |
40 | } |
41 | else |
42 | switch (card->options.sbp.role) { |
43 | case QETH_SBP_ROLE_NONE: |
44 | word = "none" ; break; |
45 | case QETH_SBP_ROLE_PRIMARY: |
46 | word = "primary" ; break; |
47 | case QETH_SBP_ROLE_SECONDARY: |
48 | word = "secondary" ; break; |
49 | default: |
50 | rc = -EIO; |
51 | } |
52 | if (rc) |
53 | QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x" , |
54 | card->options.sbp.role, state); |
55 | else |
56 | rc = sysfs_emit(buf, fmt: "%s\n" , word); |
57 | } |
58 | mutex_unlock(lock: &card->sbp_lock); |
59 | |
60 | return rc; |
61 | } |
62 | |
63 | static ssize_t qeth_bridge_port_role_show(struct device *dev, |
64 | struct device_attribute *attr, char *buf) |
65 | { |
66 | struct qeth_card *card = dev_get_drvdata(dev); |
67 | |
68 | if (!qeth_bridgeport_allowed(card)) |
69 | return sysfs_emit(buf, fmt: "n/a (VNIC characteristics)\n" ); |
70 | |
71 | return qeth_bridge_port_role_state_show(dev, attr, buf, show_state: 0); |
72 | } |
73 | |
74 | static ssize_t qeth_bridge_port_role_store(struct device *dev, |
75 | struct device_attribute *attr, const char *buf, size_t count) |
76 | { |
77 | struct qeth_card *card = dev_get_drvdata(dev); |
78 | int rc = 0; |
79 | enum qeth_sbp_roles role; |
80 | |
81 | if (sysfs_streq(s1: buf, s2: "primary" )) |
82 | role = QETH_SBP_ROLE_PRIMARY; |
83 | else if (sysfs_streq(s1: buf, s2: "secondary" )) |
84 | role = QETH_SBP_ROLE_SECONDARY; |
85 | else if (sysfs_streq(s1: buf, s2: "none" )) |
86 | role = QETH_SBP_ROLE_NONE; |
87 | else |
88 | return -EINVAL; |
89 | |
90 | mutex_lock(&card->conf_mutex); |
91 | mutex_lock(&card->sbp_lock); |
92 | |
93 | if (!qeth_bridgeport_allowed(card)) |
94 | rc = -EBUSY; |
95 | else if (card->options.sbp.reflect_promisc) |
96 | /* Forbid direct manipulation */ |
97 | rc = -EPERM; |
98 | else if (qeth_card_hw_is_reachable(card)) { |
99 | rc = qeth_bridgeport_setrole(card, role); |
100 | if (!rc) |
101 | card->options.sbp.role = role; |
102 | } else |
103 | card->options.sbp.role = role; |
104 | |
105 | mutex_unlock(lock: &card->sbp_lock); |
106 | mutex_unlock(lock: &card->conf_mutex); |
107 | |
108 | return rc ? rc : count; |
109 | } |
110 | |
111 | static DEVICE_ATTR(bridge_role, 0644, qeth_bridge_port_role_show, |
112 | qeth_bridge_port_role_store); |
113 | |
114 | static ssize_t qeth_bridge_port_state_show(struct device *dev, |
115 | struct device_attribute *attr, char *buf) |
116 | { |
117 | struct qeth_card *card = dev_get_drvdata(dev); |
118 | |
119 | if (!qeth_bridgeport_allowed(card)) |
120 | return sysfs_emit(buf, fmt: "n/a (VNIC characteristics)\n" ); |
121 | |
122 | return qeth_bridge_port_role_state_show(dev, attr, buf, show_state: 1); |
123 | } |
124 | |
125 | static DEVICE_ATTR(bridge_state, 0444, qeth_bridge_port_state_show, |
126 | NULL); |
127 | |
128 | static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev, |
129 | struct device_attribute *attr, char *buf) |
130 | { |
131 | struct qeth_card *card = dev_get_drvdata(dev); |
132 | int enabled; |
133 | |
134 | if (!qeth_bridgeport_allowed(card)) |
135 | return sysfs_emit(buf, fmt: "n/a (VNIC characteristics)\n" ); |
136 | |
137 | enabled = card->options.sbp.hostnotification; |
138 | |
139 | return sysfs_emit(buf, fmt: "%d\n" , enabled); |
140 | } |
141 | |
142 | static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev, |
143 | struct device_attribute *attr, const char *buf, size_t count) |
144 | { |
145 | struct qeth_card *card = dev_get_drvdata(dev); |
146 | bool enable; |
147 | int rc; |
148 | |
149 | rc = kstrtobool(s: buf, res: &enable); |
150 | if (rc) |
151 | return rc; |
152 | |
153 | mutex_lock(&card->conf_mutex); |
154 | mutex_lock(&card->sbp_lock); |
155 | |
156 | if (!qeth_bridgeport_allowed(card)) |
157 | rc = -EBUSY; |
158 | else if (qeth_card_hw_is_reachable(card)) { |
159 | rc = qeth_bridgeport_an_set(card, enable); |
160 | /* sbp_lock ensures ordering vs notifications-stopped events */ |
161 | if (!rc) |
162 | card->options.sbp.hostnotification = enable; |
163 | } else |
164 | card->options.sbp.hostnotification = enable; |
165 | |
166 | mutex_unlock(lock: &card->sbp_lock); |
167 | mutex_unlock(lock: &card->conf_mutex); |
168 | |
169 | return rc ? rc : count; |
170 | } |
171 | |
172 | static DEVICE_ATTR(bridge_hostnotify, 0644, |
173 | qeth_bridgeport_hostnotification_show, |
174 | qeth_bridgeport_hostnotification_store); |
175 | |
176 | static ssize_t qeth_bridgeport_reflect_show(struct device *dev, |
177 | struct device_attribute *attr, char *buf) |
178 | { |
179 | struct qeth_card *card = dev_get_drvdata(dev); |
180 | char *state; |
181 | |
182 | if (!qeth_bridgeport_allowed(card)) |
183 | return sysfs_emit(buf, fmt: "n/a (VNIC characteristics)\n" ); |
184 | |
185 | if (card->options.sbp.reflect_promisc) { |
186 | if (card->options.sbp.reflect_promisc_primary) |
187 | state = "primary" ; |
188 | else |
189 | state = "secondary" ; |
190 | } else |
191 | state = "none" ; |
192 | |
193 | return sysfs_emit(buf, fmt: "%s\n" , state); |
194 | } |
195 | |
196 | static ssize_t qeth_bridgeport_reflect_store(struct device *dev, |
197 | struct device_attribute *attr, const char *buf, size_t count) |
198 | { |
199 | struct qeth_card *card = dev_get_drvdata(dev); |
200 | int enable, primary; |
201 | int rc = 0; |
202 | |
203 | if (sysfs_streq(s1: buf, s2: "none" )) { |
204 | enable = 0; |
205 | primary = 0; |
206 | } else if (sysfs_streq(s1: buf, s2: "primary" )) { |
207 | enable = 1; |
208 | primary = 1; |
209 | } else if (sysfs_streq(s1: buf, s2: "secondary" )) { |
210 | enable = 1; |
211 | primary = 0; |
212 | } else |
213 | return -EINVAL; |
214 | |
215 | mutex_lock(&card->conf_mutex); |
216 | mutex_lock(&card->sbp_lock); |
217 | |
218 | if (!qeth_bridgeport_allowed(card)) |
219 | rc = -EBUSY; |
220 | else if (card->options.sbp.role != QETH_SBP_ROLE_NONE) |
221 | rc = -EPERM; |
222 | else { |
223 | card->options.sbp.reflect_promisc = enable; |
224 | card->options.sbp.reflect_promisc_primary = primary; |
225 | rc = 0; |
226 | } |
227 | |
228 | mutex_unlock(lock: &card->sbp_lock); |
229 | mutex_unlock(lock: &card->conf_mutex); |
230 | |
231 | return rc ? rc : count; |
232 | } |
233 | |
234 | static DEVICE_ATTR(bridge_reflect_promisc, 0644, |
235 | qeth_bridgeport_reflect_show, |
236 | qeth_bridgeport_reflect_store); |
237 | |
238 | static struct attribute *qeth_l2_bridgeport_attrs[] = { |
239 | &dev_attr_bridge_role.attr, |
240 | &dev_attr_bridge_state.attr, |
241 | &dev_attr_bridge_hostnotify.attr, |
242 | &dev_attr_bridge_reflect_promisc.attr, |
243 | NULL, |
244 | }; |
245 | |
246 | static struct attribute_group qeth_l2_bridgeport_attr_group = { |
247 | .attrs = qeth_l2_bridgeport_attrs, |
248 | }; |
249 | |
250 | /* VNIC CHARS support */ |
251 | |
252 | /* convert sysfs attr name to VNIC characteristic */ |
253 | static u32 qeth_l2_vnicc_sysfs_attr_to_char(const char *attr_name) |
254 | { |
255 | if (sysfs_streq(s1: attr_name, s2: "flooding" )) |
256 | return QETH_VNICC_FLOODING; |
257 | else if (sysfs_streq(s1: attr_name, s2: "mcast_flooding" )) |
258 | return QETH_VNICC_MCAST_FLOODING; |
259 | else if (sysfs_streq(s1: attr_name, s2: "learning" )) |
260 | return QETH_VNICC_LEARNING; |
261 | else if (sysfs_streq(s1: attr_name, s2: "takeover_setvmac" )) |
262 | return QETH_VNICC_TAKEOVER_SETVMAC; |
263 | else if (sysfs_streq(s1: attr_name, s2: "takeover_learning" )) |
264 | return QETH_VNICC_TAKEOVER_LEARNING; |
265 | else if (sysfs_streq(s1: attr_name, s2: "bridge_invisible" )) |
266 | return QETH_VNICC_BRIDGE_INVISIBLE; |
267 | else if (sysfs_streq(s1: attr_name, s2: "rx_bcast" )) |
268 | return QETH_VNICC_RX_BCAST; |
269 | |
270 | return 0; |
271 | } |
272 | |
273 | /* get current timeout setting */ |
274 | static ssize_t qeth_vnicc_timeout_show(struct device *dev, |
275 | struct device_attribute *attr, char *buf) |
276 | { |
277 | struct qeth_card *card = dev_get_drvdata(dev); |
278 | u32 timeout; |
279 | int rc; |
280 | |
281 | rc = qeth_l2_vnicc_get_timeout(card, timeout: &timeout); |
282 | if (rc == -EBUSY) |
283 | return sysfs_emit(buf, fmt: "n/a (BridgePort)\n" ); |
284 | if (rc == -EOPNOTSUPP) |
285 | return sysfs_emit(buf, fmt: "n/a\n" ); |
286 | return rc ? rc : sysfs_emit(buf, fmt: "%d\n" , timeout); |
287 | } |
288 | |
289 | /* change timeout setting */ |
290 | static ssize_t qeth_vnicc_timeout_store(struct device *dev, |
291 | struct device_attribute *attr, |
292 | const char *buf, size_t count) |
293 | { |
294 | struct qeth_card *card = dev_get_drvdata(dev); |
295 | u32 timeout; |
296 | int rc; |
297 | |
298 | rc = kstrtou32(s: buf, base: 10, res: &timeout); |
299 | if (rc) |
300 | return rc; |
301 | |
302 | mutex_lock(&card->conf_mutex); |
303 | rc = qeth_l2_vnicc_set_timeout(card, timeout); |
304 | mutex_unlock(lock: &card->conf_mutex); |
305 | return rc ? rc : count; |
306 | } |
307 | |
308 | /* get current setting of characteristic */ |
309 | static ssize_t qeth_vnicc_char_show(struct device *dev, |
310 | struct device_attribute *attr, char *buf) |
311 | { |
312 | struct qeth_card *card = dev_get_drvdata(dev); |
313 | bool state; |
314 | u32 vnicc; |
315 | int rc; |
316 | |
317 | vnicc = qeth_l2_vnicc_sysfs_attr_to_char(attr_name: attr->attr.name); |
318 | rc = qeth_l2_vnicc_get_state(card, vnicc, state: &state); |
319 | |
320 | if (rc == -EBUSY) |
321 | return sysfs_emit(buf, fmt: "n/a (BridgePort)\n" ); |
322 | if (rc == -EOPNOTSUPP) |
323 | return sysfs_emit(buf, fmt: "n/a\n" ); |
324 | return rc ? rc : sysfs_emit(buf, fmt: "%d\n" , state); |
325 | } |
326 | |
327 | /* change setting of characteristic */ |
328 | static ssize_t qeth_vnicc_char_store(struct device *dev, |
329 | struct device_attribute *attr, |
330 | const char *buf, size_t count) |
331 | { |
332 | struct qeth_card *card = dev_get_drvdata(dev); |
333 | bool state; |
334 | u32 vnicc; |
335 | int rc; |
336 | |
337 | if (kstrtobool(s: buf, res: &state)) |
338 | return -EINVAL; |
339 | |
340 | vnicc = qeth_l2_vnicc_sysfs_attr_to_char(attr_name: attr->attr.name); |
341 | mutex_lock(&card->conf_mutex); |
342 | rc = qeth_l2_vnicc_set_state(card, vnicc, state); |
343 | mutex_unlock(lock: &card->conf_mutex); |
344 | |
345 | return rc ? rc : count; |
346 | } |
347 | |
348 | static DEVICE_ATTR(flooding, 0644, qeth_vnicc_char_show, qeth_vnicc_char_store); |
349 | static DEVICE_ATTR(mcast_flooding, 0644, qeth_vnicc_char_show, |
350 | qeth_vnicc_char_store); |
351 | static DEVICE_ATTR(learning, 0644, qeth_vnicc_char_show, qeth_vnicc_char_store); |
352 | static DEVICE_ATTR(learning_timeout, 0644, qeth_vnicc_timeout_show, |
353 | qeth_vnicc_timeout_store); |
354 | static DEVICE_ATTR(takeover_setvmac, 0644, qeth_vnicc_char_show, |
355 | qeth_vnicc_char_store); |
356 | static DEVICE_ATTR(takeover_learning, 0644, qeth_vnicc_char_show, |
357 | qeth_vnicc_char_store); |
358 | static DEVICE_ATTR(bridge_invisible, 0644, qeth_vnicc_char_show, |
359 | qeth_vnicc_char_store); |
360 | static DEVICE_ATTR(rx_bcast, 0644, qeth_vnicc_char_show, qeth_vnicc_char_store); |
361 | |
362 | static struct attribute *qeth_l2_vnicc_attrs[] = { |
363 | &dev_attr_flooding.attr, |
364 | &dev_attr_mcast_flooding.attr, |
365 | &dev_attr_learning.attr, |
366 | &dev_attr_learning_timeout.attr, |
367 | &dev_attr_takeover_setvmac.attr, |
368 | &dev_attr_takeover_learning.attr, |
369 | &dev_attr_bridge_invisible.attr, |
370 | &dev_attr_rx_bcast.attr, |
371 | NULL, |
372 | }; |
373 | |
374 | static struct attribute_group qeth_l2_vnicc_attr_group = { |
375 | .attrs = qeth_l2_vnicc_attrs, |
376 | .name = "vnicc" , |
377 | }; |
378 | |
379 | const struct attribute_group *qeth_l2_attr_groups[] = { |
380 | &qeth_l2_bridgeport_attr_group, |
381 | &qeth_l2_vnicc_attr_group, |
382 | NULL, |
383 | }; |
384 | |