blob: f2c3b127b1e4ecff98f074358197cd752de122e2 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Eugene Crosserb4d72c02014-01-14 15:54:11 +01002/*
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>
Eugene Crosser511c2442014-09-02 08:20:17 +02009#include "qeth_core.h"
Eugene Crosserb4d72c02014-01-14 15:54:11 +010010#include "qeth_l2.h"
11
Eugene Crosserb4d72c02014-01-14 15:54:11 +010012static 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 (!card)
22 return -EINVAL;
23
Hans Wippelcaa1f0b2017-09-18 21:18:15 +020024 if (qeth_l2_vnicc_is_in_use(card))
25 return sprintf(buf, "n/a (VNIC characteristics)\n");
26
Eugene Crosserb4d72c02014-01-14 15:54:11 +010027 if (qeth_card_hw_is_reachable(card) &&
28 card->options.sbp.supported_funcs)
29 rc = qeth_bridgeport_query_ports(card,
30 &card->options.sbp.role, &state);
31 if (!rc) {
32 if (show_state)
33 switch (state) {
34 case QETH_SBP_STATE_INACTIVE:
35 word = "inactive"; break;
36 case QETH_SBP_STATE_STANDBY:
37 word = "standby"; break;
38 case QETH_SBP_STATE_ACTIVE:
39 word = "active"; break;
40 default:
41 rc = -EIO;
42 }
43 else
44 switch (card->options.sbp.role) {
45 case QETH_SBP_ROLE_NONE:
46 word = "none"; break;
47 case QETH_SBP_ROLE_PRIMARY:
48 word = "primary"; break;
49 case QETH_SBP_ROLE_SECONDARY:
50 word = "secondary"; break;
51 default:
52 rc = -EIO;
53 }
54 if (rc)
55 QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x",
56 card->options.sbp.role, state);
57 else
58 rc = sprintf(buf, "%s\n", word);
59 }
60
Eugene Crosserb4d72c02014-01-14 15:54:11 +010061 return rc;
62}
63
64static ssize_t qeth_bridge_port_role_show(struct device *dev,
65 struct device_attribute *attr, char *buf)
66{
Hans Wippelcaa1f0b2017-09-18 21:18:15 +020067 struct qeth_card *card = dev_get_drvdata(dev);
68
69 if (qeth_l2_vnicc_is_in_use(card))
70 return sprintf(buf, "n/a (VNIC characteristics)\n");
71
Eugene Crosserb4d72c02014-01-14 15:54:11 +010072 return qeth_bridge_port_role_state_show(dev, attr, buf, 0);
73}
74
75static ssize_t qeth_bridge_port_role_store(struct device *dev,
76 struct device_attribute *attr, const char *buf, size_t count)
77{
78 struct qeth_card *card = dev_get_drvdata(dev);
79 int rc = 0;
80 enum qeth_sbp_roles role;
81
82 if (!card)
83 return -EINVAL;
84 if (sysfs_streq(buf, "primary"))
85 role = QETH_SBP_ROLE_PRIMARY;
86 else if (sysfs_streq(buf, "secondary"))
87 role = QETH_SBP_ROLE_SECONDARY;
88 else if (sysfs_streq(buf, "none"))
89 role = QETH_SBP_ROLE_NONE;
90 else
91 return -EINVAL;
92
93 mutex_lock(&card->conf_mutex);
94
Hans Wippelcaa1f0b2017-09-18 21:18:15 +020095 if (qeth_l2_vnicc_is_in_use(card))
96 rc = -EBUSY;
97 else if (card->options.sbp.reflect_promisc)
98 /* Forbid direct manipulation */
Eugene Crosserff1d9292015-05-18 14:27:57 +020099 rc = -EPERM;
100 else if (qeth_card_hw_is_reachable(card)) {
Eugene Crosserb4d72c02014-01-14 15:54:11 +0100101 rc = qeth_bridgeport_setrole(card, role);
102 if (!rc)
103 card->options.sbp.role = role;
104 } else
105 card->options.sbp.role = role;
106
107 mutex_unlock(&card->conf_mutex);
108
109 return rc ? rc : count;
110}
111
112static DEVICE_ATTR(bridge_role, 0644, qeth_bridge_port_role_show,
113 qeth_bridge_port_role_store);
114
115static ssize_t qeth_bridge_port_state_show(struct device *dev,
116 struct device_attribute *attr, char *buf)
117{
Hans Wippelcaa1f0b2017-09-18 21:18:15 +0200118 struct qeth_card *card = dev_get_drvdata(dev);
119
120 if (qeth_l2_vnicc_is_in_use(card))
121 return sprintf(buf, "n/a (VNIC characteristics)\n");
122
Eugene Crosserb4d72c02014-01-14 15:54:11 +0100123 return qeth_bridge_port_role_state_show(dev, attr, buf, 1);
124}
125
Lakhvich Dmitriy248046b2015-09-18 16:06:49 +0200126static DEVICE_ATTR(bridge_state, 0444, qeth_bridge_port_state_show,
Eugene Crosserb4d72c02014-01-14 15:54:11 +0100127 NULL);
128
Eugene Crosser9f48b9d2014-01-14 15:54:13 +0100129static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev,
130 struct device_attribute *attr, char *buf)
131{
132 struct qeth_card *card = dev_get_drvdata(dev);
133 int enabled;
134
135 if (!card)
136 return -EINVAL;
137
Hans Wippelcaa1f0b2017-09-18 21:18:15 +0200138 if (qeth_l2_vnicc_is_in_use(card))
139 return sprintf(buf, "n/a (VNIC characteristics)\n");
140
Eugene Crosser9f48b9d2014-01-14 15:54:13 +0100141 enabled = card->options.sbp.hostnotification;
142
Eugene Crosser9f48b9d2014-01-14 15:54:13 +0100143 return sprintf(buf, "%d\n", enabled);
144}
145
146static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev,
147 struct device_attribute *attr, const char *buf, size_t count)
148{
149 struct qeth_card *card = dev_get_drvdata(dev);
Andy Shevchenko025d0df2017-10-18 17:40:16 +0200150 bool enable;
151 int rc;
Eugene Crosser9f48b9d2014-01-14 15:54:13 +0100152
153 if (!card)
154 return -EINVAL;
155
Andy Shevchenko025d0df2017-10-18 17:40:16 +0200156 rc = kstrtobool(buf, &enable);
157 if (rc)
158 return rc;
Eugene Crosser9f48b9d2014-01-14 15:54:13 +0100159
160 mutex_lock(&card->conf_mutex);
161
Hans Wippelcaa1f0b2017-09-18 21:18:15 +0200162 if (qeth_l2_vnicc_is_in_use(card))
163 rc = -EBUSY;
164 else if (qeth_card_hw_is_reachable(card)) {
Eugene Crosser9f48b9d2014-01-14 15:54:13 +0100165 rc = qeth_bridgeport_an_set(card, enable);
166 if (!rc)
167 card->options.sbp.hostnotification = enable;
168 } else
169 card->options.sbp.hostnotification = enable;
170
171 mutex_unlock(&card->conf_mutex);
172
173 return rc ? rc : count;
174}
175
176static DEVICE_ATTR(bridge_hostnotify, 0644,
177 qeth_bridgeport_hostnotification_show,
178 qeth_bridgeport_hostnotification_store);
179
Eugene Crosser0db587b2015-05-18 14:27:55 +0200180static ssize_t qeth_bridgeport_reflect_show(struct device *dev,
181 struct device_attribute *attr, char *buf)
182{
183 struct qeth_card *card = dev_get_drvdata(dev);
184 char *state;
185
186 if (!card)
187 return -EINVAL;
188
Hans Wippelcaa1f0b2017-09-18 21:18:15 +0200189 if (qeth_l2_vnicc_is_in_use(card))
190 return sprintf(buf, "n/a (VNIC characteristics)\n");
191
Eugene Crosser0db587b2015-05-18 14:27:55 +0200192 if (card->options.sbp.reflect_promisc) {
193 if (card->options.sbp.reflect_promisc_primary)
194 state = "primary";
195 else
196 state = "secondary";
197 } else
198 state = "none";
199
200 return sprintf(buf, "%s\n", state);
201}
202
203static ssize_t qeth_bridgeport_reflect_store(struct device *dev,
204 struct device_attribute *attr, const char *buf, size_t count)
205{
206 struct qeth_card *card = dev_get_drvdata(dev);
207 int enable, primary;
Eugene Crosserff1d9292015-05-18 14:27:57 +0200208 int rc = 0;
Eugene Crosser0db587b2015-05-18 14:27:55 +0200209
210 if (!card)
211 return -EINVAL;
212
213 if (sysfs_streq(buf, "none")) {
214 enable = 0;
215 primary = 0;
216 } else if (sysfs_streq(buf, "primary")) {
217 enable = 1;
218 primary = 1;
219 } else if (sysfs_streq(buf, "secondary")) {
220 enable = 1;
221 primary = 0;
222 } else
223 return -EINVAL;
224
225 mutex_lock(&card->conf_mutex);
226
Hans Wippelcaa1f0b2017-09-18 21:18:15 +0200227 if (qeth_l2_vnicc_is_in_use(card))
228 rc = -EBUSY;
229 else if (card->options.sbp.role != QETH_SBP_ROLE_NONE)
Eugene Crosserff1d9292015-05-18 14:27:57 +0200230 rc = -EPERM;
231 else {
232 card->options.sbp.reflect_promisc = enable;
233 card->options.sbp.reflect_promisc_primary = primary;
234 rc = 0;
235 }
Eugene Crosser0db587b2015-05-18 14:27:55 +0200236
237 mutex_unlock(&card->conf_mutex);
238
Eugene Crosserff1d9292015-05-18 14:27:57 +0200239 return rc ? rc : count;
Eugene Crosser0db587b2015-05-18 14:27:55 +0200240}
241
242static DEVICE_ATTR(bridge_reflect_promisc, 0644,
243 qeth_bridgeport_reflect_show,
244 qeth_bridgeport_reflect_store);
245
Eugene Crosserb4d72c02014-01-14 15:54:11 +0100246static struct attribute *qeth_l2_bridgeport_attrs[] = {
247 &dev_attr_bridge_role.attr,
248 &dev_attr_bridge_state.attr,
Eugene Crosser9f48b9d2014-01-14 15:54:13 +0100249 &dev_attr_bridge_hostnotify.attr,
Eugene Crosser0db587b2015-05-18 14:27:55 +0200250 &dev_attr_bridge_reflect_promisc.attr,
Eugene Crosserb4d72c02014-01-14 15:54:11 +0100251 NULL,
252};
253
254static struct attribute_group qeth_l2_bridgeport_attr_group = {
255 .attrs = qeth_l2_bridgeport_attrs,
256};
257
Eugene Crosserb4d72c02014-01-14 15:54:11 +0100258/**
259 * qeth_l2_setup_bridgeport_attrs() - set/restore attrs when turning online.
260 * @card: qeth_card structure pointer
261 *
262 * Note: this function is called with conf_mutex held by the caller
263 */
264void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
265{
Eugene Crosser9f48b9d2014-01-14 15:54:13 +0100266 int rc;
267
Eugene Crosserb4d72c02014-01-14 15:54:11 +0100268 if (!card)
269 return;
270 if (!card->options.sbp.supported_funcs)
271 return;
272 if (card->options.sbp.role != QETH_SBP_ROLE_NONE) {
273 /* Conditional to avoid spurious error messages */
274 qeth_bridgeport_setrole(card, card->options.sbp.role);
275 /* Let the callback function refresh the stored role value. */
276 qeth_bridgeport_query_ports(card,
277 &card->options.sbp.role, NULL);
278 }
Eugene Crosser9f48b9d2014-01-14 15:54:13 +0100279 if (card->options.sbp.hostnotification) {
280 rc = qeth_bridgeport_an_set(card, 1);
281 if (rc)
282 card->options.sbp.hostnotification = 0;
283 } else
284 qeth_bridgeport_an_set(card, 0);
Eugene Crosserb4d72c02014-01-14 15:54:11 +0100285}
Julian Wiedmann2d2ebb32017-05-10 19:07:52 +0200286
Hans Wippelcaa1f0b2017-09-18 21:18:15 +0200287/* VNIC CHARS support */
288
289/* convert sysfs attr name to VNIC characteristic */
290static u32 qeth_l2_vnicc_sysfs_attr_to_char(const char *attr_name)
291{
292 if (sysfs_streq(attr_name, "flooding"))
293 return QETH_VNICC_FLOODING;
294 else if (sysfs_streq(attr_name, "mcast_flooding"))
295 return QETH_VNICC_MCAST_FLOODING;
296 else if (sysfs_streq(attr_name, "learning"))
297 return QETH_VNICC_LEARNING;
298 else if (sysfs_streq(attr_name, "takeover_setvmac"))
299 return QETH_VNICC_TAKEOVER_SETVMAC;
300 else if (sysfs_streq(attr_name, "takeover_learning"))
301 return QETH_VNICC_TAKEOVER_LEARNING;
302 else if (sysfs_streq(attr_name, "bridge_invisible"))
303 return QETH_VNICC_BRIDGE_INVISIBLE;
304 else if (sysfs_streq(attr_name, "rx_bcast"))
305 return QETH_VNICC_RX_BCAST;
306
307 return 0;
308}
309
Hans Wippel349d13d2017-09-18 21:18:16 +0200310/* get current timeout setting */
311static ssize_t qeth_vnicc_timeout_show(struct device *dev,
312 struct device_attribute *attr, char *buf)
313{
314 struct qeth_card *card = dev_get_drvdata(dev);
315 u32 timeout;
316 int rc;
317
318 if (!card)
319 return -EINVAL;
320
321 rc = qeth_l2_vnicc_get_timeout(card, &timeout);
322 if (rc == -EBUSY)
323 return sprintf(buf, "n/a (BridgePort)\n");
324 if (rc == -EOPNOTSUPP)
325 return sprintf(buf, "n/a\n");
326 return rc ? rc : sprintf(buf, "%d\n", timeout);
327}
328
329/* change timeout setting */
330static ssize_t qeth_vnicc_timeout_store(struct device *dev,
331 struct device_attribute *attr,
332 const char *buf, size_t count)
333{
334 struct qeth_card *card = dev_get_drvdata(dev);
335 u32 timeout;
336 int rc;
337
338 if (!card)
339 return -EINVAL;
340
341 rc = kstrtou32(buf, 10, &timeout);
342 if (rc)
343 return rc;
344
345 mutex_lock(&card->conf_mutex);
346 rc = qeth_l2_vnicc_set_timeout(card, timeout);
347 mutex_unlock(&card->conf_mutex);
348 return rc ? rc : count;
349}
350
Hans Wippelcaa1f0b2017-09-18 21:18:15 +0200351/* get current setting of characteristic */
352static ssize_t qeth_vnicc_char_show(struct device *dev,
353 struct device_attribute *attr, char *buf)
354{
355 struct qeth_card *card = dev_get_drvdata(dev);
356 bool state;
357 u32 vnicc;
358 int rc;
359
360 if (!card)
361 return -EINVAL;
362
363 vnicc = qeth_l2_vnicc_sysfs_attr_to_char(attr->attr.name);
364 rc = qeth_l2_vnicc_get_state(card, vnicc, &state);
365
366 if (rc == -EBUSY)
367 return sprintf(buf, "n/a (BridgePort)\n");
368 if (rc == -EOPNOTSUPP)
369 return sprintf(buf, "n/a\n");
370 return rc ? rc : sprintf(buf, "%d\n", state);
371}
372
373/* change setting of characteristic */
374static ssize_t qeth_vnicc_char_store(struct device *dev,
375 struct device_attribute *attr,
376 const char *buf, size_t count)
377{
378 struct qeth_card *card = dev_get_drvdata(dev);
379 bool state;
380 u32 vnicc;
381 int rc;
382
383 if (!card)
384 return -EINVAL;
385
386 if (kstrtobool(buf, &state))
387 return -EINVAL;
388
389 vnicc = qeth_l2_vnicc_sysfs_attr_to_char(attr->attr.name);
390 mutex_lock(&card->conf_mutex);
391 rc = qeth_l2_vnicc_set_state(card, vnicc, state);
392 mutex_unlock(&card->conf_mutex);
393
394 return rc ? rc : count;
395}
396
397static DEVICE_ATTR(flooding, 0644, qeth_vnicc_char_show, qeth_vnicc_char_store);
398static DEVICE_ATTR(mcast_flooding, 0644, qeth_vnicc_char_show,
399 qeth_vnicc_char_store);
400static DEVICE_ATTR(learning, 0644, qeth_vnicc_char_show, qeth_vnicc_char_store);
Hans Wippel349d13d2017-09-18 21:18:16 +0200401static DEVICE_ATTR(learning_timeout, 0644, qeth_vnicc_timeout_show,
402 qeth_vnicc_timeout_store);
Hans Wippelcaa1f0b2017-09-18 21:18:15 +0200403static DEVICE_ATTR(takeover_setvmac, 0644, qeth_vnicc_char_show,
404 qeth_vnicc_char_store);
405static DEVICE_ATTR(takeover_learning, 0644, qeth_vnicc_char_show,
406 qeth_vnicc_char_store);
407static DEVICE_ATTR(bridge_invisible, 0644, qeth_vnicc_char_show,
408 qeth_vnicc_char_store);
409static DEVICE_ATTR(rx_bcast, 0644, qeth_vnicc_char_show, qeth_vnicc_char_store);
410
411static struct attribute *qeth_l2_vnicc_attrs[] = {
412 &dev_attr_flooding.attr,
413 &dev_attr_mcast_flooding.attr,
414 &dev_attr_learning.attr,
Hans Wippel349d13d2017-09-18 21:18:16 +0200415 &dev_attr_learning_timeout.attr,
Hans Wippelcaa1f0b2017-09-18 21:18:15 +0200416 &dev_attr_takeover_setvmac.attr,
417 &dev_attr_takeover_learning.attr,
418 &dev_attr_bridge_invisible.attr,
419 &dev_attr_rx_bcast.attr,
420 NULL,
421};
422
423static struct attribute_group qeth_l2_vnicc_attr_group = {
424 .attrs = qeth_l2_vnicc_attrs,
425 .name = "vnicc",
426};
427
428static const struct attribute_group *qeth_l2_only_attr_groups[] = {
429 &qeth_l2_bridgeport_attr_group,
430 &qeth_l2_vnicc_attr_group,
431 NULL,
432};
433
434int qeth_l2_create_device_attributes(struct device *dev)
435{
436 return sysfs_create_groups(&dev->kobj, qeth_l2_only_attr_groups);
437}
438
439void qeth_l2_remove_device_attributes(struct device *dev)
440{
441 sysfs_remove_groups(&dev->kobj, qeth_l2_only_attr_groups);
442}
443
Julian Wiedmann2d2ebb32017-05-10 19:07:52 +0200444const struct attribute_group *qeth_l2_attr_groups[] = {
445 &qeth_device_attr_group,
446 &qeth_device_blkt_group,
Hans Wippelcaa1f0b2017-09-18 21:18:15 +0200447 /* l2 specific, see qeth_l2_only_attr_groups: */
Julian Wiedmann2d2ebb32017-05-10 19:07:52 +0200448 &qeth_l2_bridgeport_attr_group,
Hans Wippelcaa1f0b2017-09-18 21:18:15 +0200449 &qeth_l2_vnicc_attr_group,
Julian Wiedmann2d2ebb32017-05-10 19:07:52 +0200450 NULL,
451};