Commit b4d72c08b358fc5b259fad0f4971112d949efd1c
Committed by
David S. Miller
1 parent
3977458c9c
Exists in
master
and in
16 other branches
qeth: bridgeport support - basic control
Introduce functions to assign roles and check state of bridgeport-capable HiperSocket devices, and sysfs attributes providing access to these functions from userspace. Introduce udev events emitted when the state of a bridgeport device changes. Signed-off-by: Eugene Crosser <eugene.crosser@ru.ibm.com> Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com> Reviewed-by: Ursula Braun <ursula.braun@de.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 9 changed files with 685 additions and 2 deletions Side-by-side Diff
Documentation/s390/qeth.txt
1 | +IBM s390 QDIO Ethernet Driver | |
2 | + | |
3 | +HiperSockets Bridge Port Support | |
4 | + | |
5 | +Uevents | |
6 | + | |
7 | +To generate the events the device must be assigned a role of either | |
8 | +a primary or a secondary Bridge Port. For more information, see | |
9 | +"z/VM Connectivity, SC24-6174". | |
10 | + | |
11 | +When run on HiperSockets Bridge Capable Port hardware, and the state | |
12 | +of some configured Bridge Port device on the channel changes, a udev | |
13 | +event with ACTION=CHANGE is emitted on behalf of the corresponding | |
14 | +ccwgroup device. The event has the following attributes: | |
15 | + | |
16 | +BRIDGEPORT=statechange - indicates that the Bridge Port device changed | |
17 | + its state. | |
18 | + | |
19 | +ROLE={primary|secondary|none} - the role assigned to the port. | |
20 | + | |
21 | +STATE={active|standby|inactive} - the newly assumed state of the port. |
drivers/s390/net/Makefile
... | ... | @@ -11,7 +11,7 @@ |
11 | 11 | obj-$(CONFIG_CLAW) += claw.o |
12 | 12 | qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o |
13 | 13 | obj-$(CONFIG_QETH) += qeth.o |
14 | -qeth_l2-y += qeth_l2_main.o | |
14 | +qeth_l2-y += qeth_l2_main.o qeth_l2_sys.o | |
15 | 15 | obj-$(CONFIG_QETH_L2) += qeth_l2.o |
16 | 16 | qeth_l3-y += qeth_l3_main.o qeth_l3_sys.o |
17 | 17 | obj-$(CONFIG_QETH_L3) += qeth_l3.o |
drivers/s390/net/qeth_core.h
... | ... | @@ -156,6 +156,24 @@ |
156 | 156 | __u32 enabled_funcs; |
157 | 157 | }; |
158 | 158 | |
159 | +/* SETBRIDGEPORT stuff */ | |
160 | +enum qeth_sbp_roles { | |
161 | + QETH_SBP_ROLE_NONE = 0, | |
162 | + QETH_SBP_ROLE_PRIMARY = 1, | |
163 | + QETH_SBP_ROLE_SECONDARY = 2, | |
164 | +}; | |
165 | + | |
166 | +enum qeth_sbp_states { | |
167 | + QETH_SBP_STATE_INACTIVE = 0, | |
168 | + QETH_SBP_STATE_STANDBY = 1, | |
169 | + QETH_SBP_STATE_ACTIVE = 2, | |
170 | +}; | |
171 | + | |
172 | +struct qeth_sbp_info { | |
173 | + __u32 supported_funcs; | |
174 | + enum qeth_sbp_roles role; | |
175 | +}; | |
176 | + | |
159 | 177 | static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, |
160 | 178 | enum qeth_ipa_funcs func) |
161 | 179 | { |
... | ... | @@ -672,6 +690,7 @@ |
672 | 690 | struct qeth_ipa_info adp; /*Adapter parameters*/ |
673 | 691 | struct qeth_routing_info route6; |
674 | 692 | struct qeth_ipa_info ipa6; |
693 | + struct qeth_sbp_info sbp; /* SETBRIDGEPORT options */ | |
675 | 694 | int fake_broadcast; |
676 | 695 | int add_hhlen; |
677 | 696 | int layer2; |
... | ... | @@ -857,6 +876,7 @@ |
857 | 876 | extern struct qeth_discipline qeth_l3_discipline; |
858 | 877 | extern const struct attribute_group *qeth_generic_attr_groups[]; |
859 | 878 | extern const struct attribute_group *qeth_osn_attr_groups[]; |
879 | +extern struct workqueue_struct *qeth_wq; | |
860 | 880 | |
861 | 881 | const char *qeth_get_cardname_short(struct qeth_card *); |
862 | 882 | int qeth_realloc_buffer_pool(struct qeth_card *, int); |
... | ... | @@ -925,6 +945,11 @@ |
925 | 945 | int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *, |
926 | 946 | int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long), |
927 | 947 | void *reply_param); |
948 | +void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd); | |
949 | +void qeth_bridgeport_query_support(struct qeth_card *card); | |
950 | +int qeth_bridgeport_query_ports(struct qeth_card *card, | |
951 | + enum qeth_sbp_roles *role, enum qeth_sbp_states *state); | |
952 | +int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); | |
928 | 953 | int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); |
929 | 954 | int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int); |
930 | 955 | int qeth_get_elements_for_frags(struct sk_buff *); |
drivers/s390/net/qeth_core_main.c
... | ... | @@ -68,7 +68,7 @@ |
68 | 68 | enum qeth_qdio_buffer_states newbufstate); |
69 | 69 | static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); |
70 | 70 | |
71 | -static struct workqueue_struct *qeth_wq; | |
71 | +struct workqueue_struct *qeth_wq; | |
72 | 72 | |
73 | 73 | static void qeth_close_dev_handler(struct work_struct *work) |
74 | 74 | { |
... | ... | @@ -615,6 +615,13 @@ |
615 | 615 | card->info.hwtrap = 2; |
616 | 616 | qeth_schedule_recovery(card); |
617 | 617 | return NULL; |
618 | + case IPA_CMD_SETBRIDGEPORT: | |
619 | + if (cmd->data.sbp.hdr.command_code == | |
620 | + IPA_SBP_BRIDGE_PORT_STATE_CHANGE) { | |
621 | + qeth_bridge_state_change(card, cmd); | |
622 | + return NULL; | |
623 | + } else | |
624 | + return cmd; | |
618 | 625 | case IPA_CMD_MODCCID: |
619 | 626 | return cmd; |
620 | 627 | case IPA_CMD_REGISTER_LOCAL_ADDR: |
621 | 628 | |
... | ... | @@ -4956,12 +4963,17 @@ |
4956 | 4963 | |
4957 | 4964 | card->options.ipa4.supported_funcs = 0; |
4958 | 4965 | card->options.adp.supported_funcs = 0; |
4966 | + card->options.sbp.supported_funcs = 0; | |
4959 | 4967 | card->info.diagass_support = 0; |
4960 | 4968 | qeth_query_ipassists(card, QETH_PROT_IPV4); |
4961 | 4969 | if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) |
4962 | 4970 | qeth_query_setadapterparms(card); |
4963 | 4971 | if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) |
4964 | 4972 | qeth_query_setdiagass(card); |
4973 | + qeth_bridgeport_query_support(card); | |
4974 | + if (card->options.sbp.supported_funcs) | |
4975 | + dev_info(&card->gdev->dev, | |
4976 | + "The device represents a HiperSockets Bridge Capable Port\n"); | |
4965 | 4977 | return 0; |
4966 | 4978 | out: |
4967 | 4979 | dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " |
drivers/s390/net/qeth_core_mpc.c
... | ... | @@ -249,6 +249,7 @@ |
249 | 249 | {IPA_CMD_DELIP, "delip"}, |
250 | 250 | {IPA_CMD_SETADAPTERPARMS, "setadapterparms"}, |
251 | 251 | {IPA_CMD_SET_DIAG_ASS, "set_diag_ass"}, |
252 | + {IPA_CMD_SETBRIDGEPORT, "set_bridge_port"}, | |
252 | 253 | {IPA_CMD_CREATE_ADDR, "create_addr"}, |
253 | 254 | {IPA_CMD_DESTROY_ADDR, "destroy_addr"}, |
254 | 255 | {IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"}, |
drivers/s390/net/qeth_core_mpc.h
... | ... | @@ -104,6 +104,7 @@ |
104 | 104 | IPA_CMD_DELIP = 0xb7, |
105 | 105 | IPA_CMD_SETADAPTERPARMS = 0xb8, |
106 | 106 | IPA_CMD_SET_DIAG_ASS = 0xb9, |
107 | + IPA_CMD_SETBRIDGEPORT = 0xbe, | |
107 | 108 | IPA_CMD_CREATE_ADDR = 0xc3, |
108 | 109 | IPA_CMD_DESTROY_ADDR = 0xc4, |
109 | 110 | IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1, |
... | ... | @@ -500,6 +501,88 @@ |
500 | 501 | __u8 cdata[64]; |
501 | 502 | } __attribute__ ((packed)); |
502 | 503 | |
504 | +/* SETBRIDGEPORT IPA Command: *********************************************/ | |
505 | +enum qeth_ipa_sbp_cmd { | |
506 | + IPA_SBP_QUERY_COMMANDS_SUPPORTED = 0x00000000L, | |
507 | + IPA_SBP_RESET_BRIDGE_PORT_ROLE = 0x00000001L, | |
508 | + IPA_SBP_SET_PRIMARY_BRIDGE_PORT = 0x00000002L, | |
509 | + IPA_SBP_SET_SECONDARY_BRIDGE_PORT = 0x00000004L, | |
510 | + IPA_SBP_QUERY_BRIDGE_PORTS = 0x00000008L, | |
511 | + IPA_SBP_BRIDGE_PORT_STATE_CHANGE = 0x00000010L, | |
512 | +}; | |
513 | + | |
514 | +struct net_if_token { | |
515 | + __u16 devnum; | |
516 | + __u8 cssid; | |
517 | + __u8 iid; | |
518 | + __u8 ssid; | |
519 | + __u8 chpid; | |
520 | + __u16 chid; | |
521 | +} __packed; | |
522 | + | |
523 | +struct qeth_ipacmd_sbp_hdr { | |
524 | + __u32 supported_sbp_cmds; | |
525 | + __u32 enabled_sbp_cmds; | |
526 | + __u16 cmdlength; | |
527 | + __u16 reserved1; | |
528 | + __u32 command_code; | |
529 | + __u16 return_code; | |
530 | + __u8 used_total; | |
531 | + __u8 seq_no; | |
532 | + __u32 reserved2; | |
533 | +} __packed; | |
534 | + | |
535 | +struct qeth_sbp_query_cmds_supp { | |
536 | + __u32 supported_cmds; | |
537 | + __u32 reserved; | |
538 | +} __packed; | |
539 | + | |
540 | +struct qeth_sbp_reset_role { | |
541 | +} __packed; | |
542 | + | |
543 | +struct qeth_sbp_set_primary { | |
544 | + struct net_if_token token; | |
545 | +} __packed; | |
546 | + | |
547 | +struct qeth_sbp_set_secondary { | |
548 | +} __packed; | |
549 | + | |
550 | +struct qeth_sbp_port_entry { | |
551 | + __u8 role; | |
552 | + __u8 state; | |
553 | + __u8 reserved1; | |
554 | + __u8 reserved2; | |
555 | + struct net_if_token token; | |
556 | +} __packed; | |
557 | + | |
558 | +struct qeth_sbp_query_ports { | |
559 | + __u8 primary_bp_supported; | |
560 | + __u8 secondary_bp_supported; | |
561 | + __u8 num_entries; | |
562 | + __u8 entry_length; | |
563 | + struct qeth_sbp_port_entry entry[]; | |
564 | +} __packed; | |
565 | + | |
566 | +struct qeth_sbp_state_change { | |
567 | + __u8 primary_bp_supported; | |
568 | + __u8 secondary_bp_supported; | |
569 | + __u8 num_entries; | |
570 | + __u8 entry_length; | |
571 | + struct qeth_sbp_port_entry entry[]; | |
572 | +} __packed; | |
573 | + | |
574 | +struct qeth_ipacmd_setbridgeport { | |
575 | + struct qeth_ipacmd_sbp_hdr hdr; | |
576 | + union { | |
577 | + struct qeth_sbp_query_cmds_supp query_cmds_supp; | |
578 | + struct qeth_sbp_reset_role reset_role; | |
579 | + struct qeth_sbp_set_primary set_primary; | |
580 | + struct qeth_sbp_set_secondary set_secondary; | |
581 | + struct qeth_sbp_query_ports query_ports; | |
582 | + struct qeth_sbp_state_change state_change; | |
583 | + } data; | |
584 | +} __packed; | |
585 | + | |
503 | 586 | /* Header for each IPA command */ |
504 | 587 | struct qeth_ipacmd_hdr { |
505 | 588 | __u8 command; |
... | ... | @@ -529,6 +612,7 @@ |
529 | 612 | struct qeth_ipacmd_setadpparms setadapterparms; |
530 | 613 | struct qeth_set_routing setrtg; |
531 | 614 | struct qeth_ipacmd_diagass diagass; |
615 | + struct qeth_ipacmd_setbridgeport sbp; | |
532 | 616 | } data; |
533 | 617 | } __attribute__ ((packed)); |
534 | 618 |
drivers/s390/net/qeth_l2.h
1 | +/* | |
2 | + * Copyright IBM Corp. 2013 | |
3 | + * Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com> | |
4 | + */ | |
5 | + | |
6 | +#ifndef __QETH_L2_H__ | |
7 | +#define __QETH_L2_H__ | |
8 | + | |
9 | +#include "qeth_core.h" | |
10 | + | |
11 | +int qeth_l2_create_device_attributes(struct device *); | |
12 | +void qeth_l2_remove_device_attributes(struct device *); | |
13 | +void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); | |
14 | + | |
15 | +#endif /* __QETH_L2_H__ */ |
drivers/s390/net/qeth_l2_main.c
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | #include <linux/list.h> |
22 | 22 | |
23 | 23 | #include "qeth_core.h" |
24 | +#include "qeth_l2.h" | |
24 | 25 | |
25 | 26 | static int qeth_l2_set_offline(struct ccwgroup_device *); |
26 | 27 | static int qeth_l2_stop(struct net_device *); |
... | ... | @@ -880,6 +881,7 @@ |
880 | 881 | { |
881 | 882 | struct qeth_card *card = dev_get_drvdata(&gdev->dev); |
882 | 883 | |
884 | + qeth_l2_create_device_attributes(&gdev->dev); | |
883 | 885 | INIT_LIST_HEAD(&card->vid_list); |
884 | 886 | INIT_LIST_HEAD(&card->mc_list); |
885 | 887 | card->options.layer2 = 1; |
... | ... | @@ -891,6 +893,7 @@ |
891 | 893 | { |
892 | 894 | struct qeth_card *card = dev_get_drvdata(&cgdev->dev); |
893 | 895 | |
896 | + qeth_l2_remove_device_attributes(&cgdev->dev); | |
894 | 897 | qeth_set_allowed_threads(card, 0, 1); |
895 | 898 | wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); |
896 | 899 | |
... | ... | @@ -1003,6 +1006,8 @@ |
1003 | 1006 | } else |
1004 | 1007 | card->info.hwtrap = 0; |
1005 | 1008 | |
1009 | + qeth_l2_setup_bridgeport_attrs(card); | |
1010 | + | |
1006 | 1011 | card->state = CARD_STATE_HARDSETUP; |
1007 | 1012 | memset(&card->rx, 0, sizeof(struct qeth_rx)); |
1008 | 1013 | qeth_print_status_message(card); |
... | ... | @@ -1346,6 +1351,365 @@ |
1346 | 1351 | return; |
1347 | 1352 | } |
1348 | 1353 | EXPORT_SYMBOL(qeth_osn_deregister); |
1354 | + | |
1355 | +/* SETBRIDGEPORT support, async notifications */ | |
1356 | + | |
1357 | +struct qeth_bridge_state_data { | |
1358 | + struct work_struct worker; | |
1359 | + struct qeth_card *card; | |
1360 | + struct qeth_sbp_state_change qports; | |
1361 | +}; | |
1362 | + | |
1363 | +static void qeth_bridge_state_change_worker(struct work_struct *work) | |
1364 | +{ | |
1365 | + struct qeth_bridge_state_data *data = | |
1366 | + container_of(work, struct qeth_bridge_state_data, worker); | |
1367 | + /* We are only interested in the first entry - local port */ | |
1368 | + struct qeth_sbp_port_entry *entry = &data->qports.entry[0]; | |
1369 | + char env_locrem[32]; | |
1370 | + char env_role[32]; | |
1371 | + char env_state[32]; | |
1372 | + char *env[] = { | |
1373 | + env_locrem, | |
1374 | + env_role, | |
1375 | + env_state, | |
1376 | + NULL | |
1377 | + }; | |
1378 | + | |
1379 | + /* Role should not change by itself, but if it did, */ | |
1380 | + /* information from the hardware is authoritative. */ | |
1381 | + mutex_lock(&data->card->conf_mutex); | |
1382 | + data->card->options.sbp.role = entry->role; | |
1383 | + mutex_unlock(&data->card->conf_mutex); | |
1384 | + | |
1385 | + snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange"); | |
1386 | + snprintf(env_role, sizeof(env_role), "ROLE=%s", | |
1387 | + (entry->role == QETH_SBP_ROLE_NONE) ? "none" : | |
1388 | + (entry->role == QETH_SBP_ROLE_PRIMARY) ? "primary" : | |
1389 | + (entry->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" : | |
1390 | + "<INVALID>"); | |
1391 | + snprintf(env_state, sizeof(env_state), "STATE=%s", | |
1392 | + (entry->state == QETH_SBP_STATE_INACTIVE) ? "inactive" : | |
1393 | + (entry->state == QETH_SBP_STATE_STANDBY) ? "standby" : | |
1394 | + (entry->state == QETH_SBP_STATE_ACTIVE) ? "active" : | |
1395 | + "<INVALID>"); | |
1396 | + kobject_uevent_env(&data->card->gdev->dev.kobj, | |
1397 | + KOBJ_CHANGE, env); | |
1398 | + kfree(data); | |
1399 | +} | |
1400 | + | |
1401 | +void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd) | |
1402 | +{ | |
1403 | + struct qeth_sbp_state_change *qports = | |
1404 | + &cmd->data.sbp.data.state_change; | |
1405 | + struct qeth_bridge_state_data *data; | |
1406 | + int extrasize; | |
1407 | + | |
1408 | + QETH_CARD_TEXT(card, 2, "brstchng"); | |
1409 | + if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { | |
1410 | + QETH_CARD_TEXT_(card, 2, "BPsz%.8d", qports->entry_length); | |
1411 | + return; | |
1412 | + } | |
1413 | + extrasize = sizeof(struct qeth_sbp_port_entry) * qports->num_entries; | |
1414 | + data = kzalloc(sizeof(struct qeth_bridge_state_data) + extrasize, | |
1415 | + GFP_ATOMIC); | |
1416 | + if (!data) { | |
1417 | + QETH_CARD_TEXT(card, 2, "BPSalloc"); | |
1418 | + return; | |
1419 | + } | |
1420 | + INIT_WORK(&data->worker, qeth_bridge_state_change_worker); | |
1421 | + data->card = card; | |
1422 | + memcpy(&data->qports, qports, | |
1423 | + sizeof(struct qeth_sbp_state_change) + extrasize); | |
1424 | + queue_work(qeth_wq, &data->worker); | |
1425 | +} | |
1426 | +EXPORT_SYMBOL(qeth_bridge_state_change); | |
1427 | + | |
1428 | +/* SETBRIDGEPORT support; sending commands */ | |
1429 | + | |
1430 | +struct _qeth_sbp_cbctl { | |
1431 | + u16 ipa_rc; | |
1432 | + u16 cmd_rc; | |
1433 | + union { | |
1434 | + u32 supported; | |
1435 | + struct { | |
1436 | + enum qeth_sbp_roles *role; | |
1437 | + enum qeth_sbp_states *state; | |
1438 | + } qports; | |
1439 | + } data; | |
1440 | +}; | |
1441 | + | |
1442 | +/** | |
1443 | + * qeth_bridgeport_makerc() - derive "traditional" error from hardware codes. | |
1444 | + * @card: qeth_card structure pointer, for debug messages. | |
1445 | + * @cbctl: state structure with hardware return codes. | |
1446 | + * @setcmd: IPA command code | |
1447 | + * | |
1448 | + * Returns negative errno-compatible error indication or 0 on success. | |
1449 | + */ | |
1450 | +static int qeth_bridgeport_makerc(struct qeth_card *card, | |
1451 | + struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd) | |
1452 | +{ | |
1453 | + int rc; | |
1454 | + | |
1455 | + switch (cbctl->ipa_rc) { | |
1456 | + case IPA_RC_SUCCESS: | |
1457 | + switch (cbctl->cmd_rc) { | |
1458 | + case 0x0000: | |
1459 | + rc = 0; | |
1460 | + break; | |
1461 | + case 0x0004: | |
1462 | + rc = -ENOSYS; | |
1463 | + break; | |
1464 | + case 0x000C: /* Not configured as bridge Port */ | |
1465 | + rc = -ENODEV; /* maybe not the best code here? */ | |
1466 | + dev_err(&card->gdev->dev, | |
1467 | + "The HiperSockets device is not configured as a Bridge Port\n"); | |
1468 | + break; | |
1469 | + case 0x0014: /* Another device is Primary */ | |
1470 | + switch (setcmd) { | |
1471 | + case IPA_SBP_SET_PRIMARY_BRIDGE_PORT: | |
1472 | + rc = -EEXIST; | |
1473 | + dev_err(&card->gdev->dev, | |
1474 | + "The HiperSockets LAN already has a primary Bridge Port\n"); | |
1475 | + break; | |
1476 | + case IPA_SBP_SET_SECONDARY_BRIDGE_PORT: | |
1477 | + rc = -EBUSY; | |
1478 | + dev_err(&card->gdev->dev, | |
1479 | + "The HiperSockets device is already a primary Bridge Port\n"); | |
1480 | + break; | |
1481 | + default: | |
1482 | + rc = -EIO; | |
1483 | + } | |
1484 | + break; | |
1485 | + case 0x0018: /* This device is currently Secondary */ | |
1486 | + rc = -EBUSY; | |
1487 | + dev_err(&card->gdev->dev, | |
1488 | + "The HiperSockets device is already a secondary Bridge Port\n"); | |
1489 | + break; | |
1490 | + case 0x001C: /* Limit for Secondary devices reached */ | |
1491 | + rc = -EEXIST; | |
1492 | + dev_err(&card->gdev->dev, | |
1493 | + "The HiperSockets LAN cannot have more secondary Bridge Ports\n"); | |
1494 | + break; | |
1495 | + case 0x0024: /* This device is currently Primary */ | |
1496 | + rc = -EBUSY; | |
1497 | + dev_err(&card->gdev->dev, | |
1498 | + "The HiperSockets device is already a primary Bridge Port\n"); | |
1499 | + break; | |
1500 | + case 0x0020: /* Not authorized by zManager */ | |
1501 | + rc = -EACCES; | |
1502 | + dev_err(&card->gdev->dev, | |
1503 | + "The HiperSockets device is not authorized to be a Bridge Port\n"); | |
1504 | + break; | |
1505 | + default: | |
1506 | + rc = -EIO; | |
1507 | + } | |
1508 | + break; | |
1509 | + case IPA_RC_NOTSUPP: | |
1510 | + rc = -ENOSYS; | |
1511 | + break; | |
1512 | + case IPA_RC_UNSUPPORTED_COMMAND: | |
1513 | + rc = -ENOSYS; | |
1514 | + break; | |
1515 | + default: | |
1516 | + rc = -EIO; | |
1517 | + } | |
1518 | + if (rc) { | |
1519 | + QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc); | |
1520 | + QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc); | |
1521 | + } | |
1522 | + return rc; | |
1523 | +} | |
1524 | + | |
1525 | +static int qeth_bridgeport_query_support_cb(struct qeth_card *card, | |
1526 | + struct qeth_reply *reply, unsigned long data) | |
1527 | +{ | |
1528 | + struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; | |
1529 | + struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | |
1530 | + QETH_CARD_TEXT(card, 2, "brqsupcb"); | |
1531 | + cbctl->ipa_rc = cmd->hdr.return_code; | |
1532 | + cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | |
1533 | + if ((cbctl->ipa_rc == 0) && (cbctl->cmd_rc == 0)) { | |
1534 | + cbctl->data.supported = | |
1535 | + cmd->data.sbp.data.query_cmds_supp.supported_cmds; | |
1536 | + } else { | |
1537 | + cbctl->data.supported = 0; | |
1538 | + } | |
1539 | + return 0; | |
1540 | +} | |
1541 | + | |
1542 | +/** | |
1543 | + * qeth_bridgeport_query_support() - store bitmask of supported subfunctions. | |
1544 | + * @card: qeth_card structure pointer. | |
1545 | + * | |
1546 | + * Sets bitmask of supported setbridgeport subfunctions in the qeth_card | |
1547 | + * strucutre: card->options.sbp.supported_funcs. | |
1548 | + */ | |
1549 | +void qeth_bridgeport_query_support(struct qeth_card *card) | |
1550 | +{ | |
1551 | + struct qeth_cmd_buffer *iob; | |
1552 | + struct qeth_ipa_cmd *cmd; | |
1553 | + struct _qeth_sbp_cbctl cbctl; | |
1554 | + | |
1555 | + QETH_CARD_TEXT(card, 2, "brqsuppo"); | |
1556 | + iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | |
1557 | + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | |
1558 | + cmd->data.sbp.hdr.cmdlength = | |
1559 | + sizeof(struct qeth_ipacmd_sbp_hdr) + | |
1560 | + sizeof(struct qeth_sbp_query_cmds_supp); | |
1561 | + cmd->data.sbp.hdr.command_code = | |
1562 | + IPA_SBP_QUERY_COMMANDS_SUPPORTED; | |
1563 | + cmd->data.sbp.hdr.used_total = 1; | |
1564 | + cmd->data.sbp.hdr.seq_no = 1; | |
1565 | + if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb, | |
1566 | + (void *)&cbctl) || | |
1567 | + qeth_bridgeport_makerc(card, &cbctl, | |
1568 | + IPA_SBP_QUERY_COMMANDS_SUPPORTED)) { | |
1569 | + /* non-zero makerc signifies failure, and produce messages */ | |
1570 | + card->options.sbp.role = QETH_SBP_ROLE_NONE; | |
1571 | + return; | |
1572 | + } | |
1573 | + card->options.sbp.supported_funcs = cbctl.data.supported; | |
1574 | +} | |
1575 | +EXPORT_SYMBOL_GPL(qeth_bridgeport_query_support); | |
1576 | + | |
1577 | +static int qeth_bridgeport_query_ports_cb(struct qeth_card *card, | |
1578 | + struct qeth_reply *reply, unsigned long data) | |
1579 | +{ | |
1580 | + struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; | |
1581 | + struct qeth_sbp_query_ports *qports = &cmd->data.sbp.data.query_ports; | |
1582 | + struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | |
1583 | + | |
1584 | + QETH_CARD_TEXT(card, 2, "brqprtcb"); | |
1585 | + cbctl->ipa_rc = cmd->hdr.return_code; | |
1586 | + cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | |
1587 | + if ((cbctl->ipa_rc != 0) || (cbctl->cmd_rc != 0)) | |
1588 | + return 0; | |
1589 | + if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { | |
1590 | + cbctl->cmd_rc = 0xffff; | |
1591 | + QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length); | |
1592 | + return 0; | |
1593 | + } | |
1594 | + /* first entry contains the state of the local port */ | |
1595 | + if (qports->num_entries > 0) { | |
1596 | + if (cbctl->data.qports.role) | |
1597 | + *cbctl->data.qports.role = qports->entry[0].role; | |
1598 | + if (cbctl->data.qports.state) | |
1599 | + *cbctl->data.qports.state = qports->entry[0].state; | |
1600 | + } | |
1601 | + return 0; | |
1602 | +} | |
1603 | + | |
1604 | +/** | |
1605 | + * qeth_bridgeport_query_ports() - query local bridgeport status. | |
1606 | + * @card: qeth_card structure pointer. | |
1607 | + * @role: Role of the port: 0-none, 1-primary, 2-secondary. | |
1608 | + * @state: State of the port: 0-inactive, 1-standby, 2-active. | |
1609 | + * | |
1610 | + * Returns negative errno-compatible error indication or 0 on success. | |
1611 | + * | |
1612 | + * 'role' and 'state' are not updated in case of hardware operation failure. | |
1613 | + */ | |
1614 | +int qeth_bridgeport_query_ports(struct qeth_card *card, | |
1615 | + enum qeth_sbp_roles *role, enum qeth_sbp_states *state) | |
1616 | +{ | |
1617 | + int rc = 0; | |
1618 | + struct qeth_cmd_buffer *iob; | |
1619 | + struct qeth_ipa_cmd *cmd; | |
1620 | + struct _qeth_sbp_cbctl cbctl = { | |
1621 | + .data = { | |
1622 | + .qports = { | |
1623 | + .role = role, | |
1624 | + .state = state, | |
1625 | + }, | |
1626 | + }, | |
1627 | + }; | |
1628 | + | |
1629 | + QETH_CARD_TEXT(card, 2, "brqports"); | |
1630 | + if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS)) | |
1631 | + return -EOPNOTSUPP; | |
1632 | + iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | |
1633 | + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | |
1634 | + cmd->data.sbp.hdr.cmdlength = | |
1635 | + sizeof(struct qeth_ipacmd_sbp_hdr); | |
1636 | + cmd->data.sbp.hdr.command_code = | |
1637 | + IPA_SBP_QUERY_BRIDGE_PORTS; | |
1638 | + cmd->data.sbp.hdr.used_total = 1; | |
1639 | + cmd->data.sbp.hdr.seq_no = 1; | |
1640 | + rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb, | |
1641 | + (void *)&cbctl); | |
1642 | + if (rc) | |
1643 | + return rc; | |
1644 | + rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS); | |
1645 | + if (rc) | |
1646 | + return rc; | |
1647 | + return 0; | |
1648 | +} | |
1649 | +EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports); | |
1650 | + | |
1651 | +static int qeth_bridgeport_set_cb(struct qeth_card *card, | |
1652 | + struct qeth_reply *reply, unsigned long data) | |
1653 | +{ | |
1654 | + struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; | |
1655 | + struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | |
1656 | + QETH_CARD_TEXT(card, 2, "brsetrcb"); | |
1657 | + cbctl->ipa_rc = cmd->hdr.return_code; | |
1658 | + cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | |
1659 | + return 0; | |
1660 | +} | |
1661 | + | |
1662 | +/** | |
1663 | + * qeth_bridgeport_setrole() - Assign primary role to the port. | |
1664 | + * @card: qeth_card structure pointer. | |
1665 | + * @role: Role to assign. | |
1666 | + * | |
1667 | + * Returns negative errno-compatible error indication or 0 on success. | |
1668 | + */ | |
1669 | +int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role) | |
1670 | +{ | |
1671 | + int rc = 0; | |
1672 | + int cmdlength; | |
1673 | + struct qeth_cmd_buffer *iob; | |
1674 | + struct qeth_ipa_cmd *cmd; | |
1675 | + struct _qeth_sbp_cbctl cbctl; | |
1676 | + enum qeth_ipa_sbp_cmd setcmd; | |
1677 | + | |
1678 | + QETH_CARD_TEXT(card, 2, "brsetrol"); | |
1679 | + switch (role) { | |
1680 | + case QETH_SBP_ROLE_NONE: | |
1681 | + setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE; | |
1682 | + cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) + | |
1683 | + sizeof(struct qeth_sbp_reset_role); | |
1684 | + break; | |
1685 | + case QETH_SBP_ROLE_PRIMARY: | |
1686 | + setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT; | |
1687 | + cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) + | |
1688 | + sizeof(struct qeth_sbp_set_primary); | |
1689 | + break; | |
1690 | + case QETH_SBP_ROLE_SECONDARY: | |
1691 | + setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT; | |
1692 | + cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) + | |
1693 | + sizeof(struct qeth_sbp_set_secondary); | |
1694 | + break; | |
1695 | + default: | |
1696 | + return -EINVAL; | |
1697 | + } | |
1698 | + if (!(card->options.sbp.supported_funcs & setcmd)) | |
1699 | + return -EOPNOTSUPP; | |
1700 | + iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | |
1701 | + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | |
1702 | + cmd->data.sbp.hdr.cmdlength = cmdlength; | |
1703 | + cmd->data.sbp.hdr.command_code = setcmd; | |
1704 | + cmd->data.sbp.hdr.used_total = 1; | |
1705 | + cmd->data.sbp.hdr.seq_no = 1; | |
1706 | + rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, | |
1707 | + (void *)&cbctl); | |
1708 | + if (rc) | |
1709 | + return rc; | |
1710 | + rc = qeth_bridgeport_makerc(card, &cbctl, setcmd); | |
1711 | + return rc; | |
1712 | +} | |
1349 | 1713 | |
1350 | 1714 | module_init(qeth_l2_init); |
1351 | 1715 | module_exit(qeth_l2_exit); |
drivers/s390/net/qeth_l2_sys.c
1 | +/* | |
2 | + * Copyright IBM Corp. 2013 | |
3 | + * Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com> | |
4 | + */ | |
5 | + | |
6 | +#include <linux/slab.h> | |
7 | +#include <asm/ebcdic.h> | |
8 | +#include "qeth_l2.h" | |
9 | + | |
10 | +#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \ | |
11 | +struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store) | |
12 | + | |
13 | +static int qeth_card_hw_is_reachable(struct qeth_card *card) | |
14 | +{ | |
15 | + return (card->state == CARD_STATE_SOFTSETUP) || | |
16 | + (card->state == CARD_STATE_UP); | |
17 | +} | |
18 | + | |
19 | +static ssize_t qeth_bridge_port_role_state_show(struct device *dev, | |
20 | + struct device_attribute *attr, char *buf, | |
21 | + int show_state) | |
22 | +{ | |
23 | + struct qeth_card *card = dev_get_drvdata(dev); | |
24 | + enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE; | |
25 | + int rc = 0; | |
26 | + char *word; | |
27 | + | |
28 | + if (!card) | |
29 | + return -EINVAL; | |
30 | + | |
31 | + mutex_lock(&card->conf_mutex); | |
32 | + | |
33 | + if (qeth_card_hw_is_reachable(card) && | |
34 | + card->options.sbp.supported_funcs) | |
35 | + rc = qeth_bridgeport_query_ports(card, | |
36 | + &card->options.sbp.role, &state); | |
37 | + if (!rc) { | |
38 | + if (show_state) | |
39 | + switch (state) { | |
40 | + case QETH_SBP_STATE_INACTIVE: | |
41 | + word = "inactive"; break; | |
42 | + case QETH_SBP_STATE_STANDBY: | |
43 | + word = "standby"; break; | |
44 | + case QETH_SBP_STATE_ACTIVE: | |
45 | + word = "active"; break; | |
46 | + default: | |
47 | + rc = -EIO; | |
48 | + } | |
49 | + else | |
50 | + switch (card->options.sbp.role) { | |
51 | + case QETH_SBP_ROLE_NONE: | |
52 | + word = "none"; break; | |
53 | + case QETH_SBP_ROLE_PRIMARY: | |
54 | + word = "primary"; break; | |
55 | + case QETH_SBP_ROLE_SECONDARY: | |
56 | + word = "secondary"; break; | |
57 | + default: | |
58 | + rc = -EIO; | |
59 | + } | |
60 | + if (rc) | |
61 | + QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x", | |
62 | + card->options.sbp.role, state); | |
63 | + else | |
64 | + rc = sprintf(buf, "%s\n", word); | |
65 | + } | |
66 | + | |
67 | + mutex_unlock(&card->conf_mutex); | |
68 | + | |
69 | + return rc; | |
70 | +} | |
71 | + | |
72 | +static ssize_t qeth_bridge_port_role_show(struct device *dev, | |
73 | + struct device_attribute *attr, char *buf) | |
74 | +{ | |
75 | + return qeth_bridge_port_role_state_show(dev, attr, buf, 0); | |
76 | +} | |
77 | + | |
78 | +static ssize_t qeth_bridge_port_role_store(struct device *dev, | |
79 | + struct device_attribute *attr, const char *buf, size_t count) | |
80 | +{ | |
81 | + struct qeth_card *card = dev_get_drvdata(dev); | |
82 | + int rc = 0; | |
83 | + enum qeth_sbp_roles role; | |
84 | + | |
85 | + if (!card) | |
86 | + return -EINVAL; | |
87 | + if (sysfs_streq(buf, "primary")) | |
88 | + role = QETH_SBP_ROLE_PRIMARY; | |
89 | + else if (sysfs_streq(buf, "secondary")) | |
90 | + role = QETH_SBP_ROLE_SECONDARY; | |
91 | + else if (sysfs_streq(buf, "none")) | |
92 | + role = QETH_SBP_ROLE_NONE; | |
93 | + else | |
94 | + return -EINVAL; | |
95 | + | |
96 | + mutex_lock(&card->conf_mutex); | |
97 | + | |
98 | + 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(&card->conf_mutex); | |
106 | + | |
107 | + return rc ? rc : count; | |
108 | +} | |
109 | + | |
110 | +static DEVICE_ATTR(bridge_role, 0644, qeth_bridge_port_role_show, | |
111 | + qeth_bridge_port_role_store); | |
112 | + | |
113 | +static ssize_t qeth_bridge_port_state_show(struct device *dev, | |
114 | + struct device_attribute *attr, char *buf) | |
115 | +{ | |
116 | + return qeth_bridge_port_role_state_show(dev, attr, buf, 1); | |
117 | +} | |
118 | + | |
119 | +static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show, | |
120 | + NULL); | |
121 | + | |
122 | +static struct attribute *qeth_l2_bridgeport_attrs[] = { | |
123 | + &dev_attr_bridge_role.attr, | |
124 | + &dev_attr_bridge_state.attr, | |
125 | + NULL, | |
126 | +}; | |
127 | + | |
128 | +static struct attribute_group qeth_l2_bridgeport_attr_group = { | |
129 | + .attrs = qeth_l2_bridgeport_attrs, | |
130 | +}; | |
131 | + | |
132 | +int qeth_l2_create_device_attributes(struct device *dev) | |
133 | +{ | |
134 | + return sysfs_create_group(&dev->kobj, &qeth_l2_bridgeport_attr_group); | |
135 | +} | |
136 | + | |
137 | +void qeth_l2_remove_device_attributes(struct device *dev) | |
138 | +{ | |
139 | + sysfs_remove_group(&dev->kobj, &qeth_l2_bridgeport_attr_group); | |
140 | +} | |
141 | + | |
142 | +/** | |
143 | + * qeth_l2_setup_bridgeport_attrs() - set/restore attrs when turning online. | |
144 | + * @card: qeth_card structure pointer | |
145 | + * | |
146 | + * Note: this function is called with conf_mutex held by the caller | |
147 | + */ | |
148 | +void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) | |
149 | +{ | |
150 | + if (!card) | |
151 | + return; | |
152 | + if (!card->options.sbp.supported_funcs) | |
153 | + return; | |
154 | + if (card->options.sbp.role != QETH_SBP_ROLE_NONE) { | |
155 | + /* Conditional to avoid spurious error messages */ | |
156 | + qeth_bridgeport_setrole(card, card->options.sbp.role); | |
157 | + /* Let the callback function refresh the stored role value. */ | |
158 | + qeth_bridgeport_query_ports(card, | |
159 | + &card->options.sbp.role, NULL); | |
160 | + } | |
161 | +} |