diff options
author | Eugene Crosser <Eugene.Crosser@ru.ibm.com> | 2014-01-14 09:54:13 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-01-15 17:48:01 -0500 |
commit | 9f48b9db9a22bdbcff8a629b3d2e4a81dcd4ed26 (patch) | |
tree | 09cb6c0447e5808c5935c500567df3ab7a235058 | |
parent | 59b55a4df24e2f105c87721bec3e6c80c3d7b20b (diff) |
qeth: bridgeport support - address notifications
Introduce functions to enable and disable bridgeport address
notification feature, sysfs attributes for access to these
functions from userspace, and udev events emitted when a host
joins or exits a bridgeport-enabled HiperSocket channel.
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>
-rw-r--r-- | Documentation/s390/qeth.txt | 29 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core.h | 5 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 3 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_mpc.c | 1 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_mpc.h | 38 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 230 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2_sys.c | 62 |
7 files changed, 368 insertions, 0 deletions
diff --git a/Documentation/s390/qeth.txt b/Documentation/s390/qeth.txt index c08c305efd4d..74122ada9949 100644 --- a/Documentation/s390/qeth.txt +++ b/Documentation/s390/qeth.txt | |||
@@ -19,3 +19,32 @@ BRIDGEPORT=statechange - indicates that the Bridge Port device changed | |||
19 | ROLE={primary|secondary|none} - the role assigned to the port. | 19 | ROLE={primary|secondary|none} - the role assigned to the port. |
20 | 20 | ||
21 | STATE={active|standby|inactive} - the newly assumed state of the port. | 21 | STATE={active|standby|inactive} - the newly assumed state of the port. |
22 | |||
23 | When run on HiperSockets Bridge Capable Port hardware with host address | ||
24 | notifications enabled, a udev event with ACTION=CHANGE is emitted. | ||
25 | It is emitted on behalf of the corresponding ccwgroup device when a host | ||
26 | or a VLAN is registered or unregistered on the network served by the device. | ||
27 | The event has the following attributes: | ||
28 | |||
29 | BRIDGEDHOST={reset|register|deregister|abort} - host address | ||
30 | notifications are started afresh, a new host or VLAN is registered or | ||
31 | deregistered on the Bridge Port HiperSockets channel, or address | ||
32 | notifications are aborted. | ||
33 | |||
34 | VLAN=numeric-vlan-id - VLAN ID on which the event occurred. Not included | ||
35 | if no VLAN is involved in the event. | ||
36 | |||
37 | MAC=xx:xx:xx:xx:xx:xx - MAC address of the host that is being registered | ||
38 | or deregistered from the HiperSockets channel. Not reported if the | ||
39 | event reports the creation or destruction of a VLAN. | ||
40 | |||
41 | NTOK_BUSID=x.y.zzzz - device bus ID (CSSID, SSID and device number). | ||
42 | |||
43 | NTOK_IID=xx - device IID. | ||
44 | |||
45 | NTOK_CHPID=xx - device CHPID. | ||
46 | |||
47 | NTOK_CHID=xxxx - device channel ID. | ||
48 | |||
49 | Note that the NTOK_* attributes refer to devices other than the one | ||
50 | connected to the system on which the OS is running. | ||
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 010f49e3e3ac..ac0bdded060f 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h | |||
@@ -169,9 +169,12 @@ enum qeth_sbp_states { | |||
169 | QETH_SBP_STATE_ACTIVE = 2, | 169 | QETH_SBP_STATE_ACTIVE = 2, |
170 | }; | 170 | }; |
171 | 171 | ||
172 | #define QETH_SBP_HOST_NOTIFICATION 1 | ||
173 | |||
172 | struct qeth_sbp_info { | 174 | struct qeth_sbp_info { |
173 | __u32 supported_funcs; | 175 | __u32 supported_funcs; |
174 | enum qeth_sbp_roles role; | 176 | enum qeth_sbp_roles role; |
177 | __u32 hostnotification:1; | ||
175 | }; | 178 | }; |
176 | 179 | ||
177 | static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, | 180 | static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, |
@@ -950,6 +953,8 @@ void qeth_bridgeport_query_support(struct qeth_card *card); | |||
950 | int qeth_bridgeport_query_ports(struct qeth_card *card, | 953 | int qeth_bridgeport_query_ports(struct qeth_card *card, |
951 | enum qeth_sbp_roles *role, enum qeth_sbp_states *state); | 954 | enum qeth_sbp_roles *role, enum qeth_sbp_states *state); |
952 | int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); | 955 | int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); |
956 | int qeth_bridgeport_an_set(struct qeth_card *card, int enable); | ||
957 | void qeth_bridge_host_event(struct qeth_card *card, struct qeth_ipa_cmd *cmd); | ||
953 | int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); | 958 | int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); |
954 | int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int); | 959 | int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int); |
955 | int qeth_get_elements_for_frags(struct sk_buff *); | 960 | int qeth_get_elements_for_frags(struct sk_buff *); |
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 1ffea16f51c6..c05dacbf4e23 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c | |||
@@ -622,6 +622,9 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, | |||
622 | return NULL; | 622 | return NULL; |
623 | } else | 623 | } else |
624 | return cmd; | 624 | return cmd; |
625 | case IPA_CMD_ADDRESS_CHANGE_NOTIF: | ||
626 | qeth_bridge_host_event(card, cmd); | ||
627 | return NULL; | ||
625 | case IPA_CMD_MODCCID: | 628 | case IPA_CMD_MODCCID: |
626 | return cmd; | 629 | return cmd; |
627 | case IPA_CMD_REGISTER_LOCAL_ADDR: | 630 | case IPA_CMD_REGISTER_LOCAL_ADDR: |
diff --git a/drivers/s390/net/qeth_core_mpc.c b/drivers/s390/net/qeth_core_mpc.c index 2f44cfd5dd00..7b55768a9592 100644 --- a/drivers/s390/net/qeth_core_mpc.c +++ b/drivers/s390/net/qeth_core_mpc.c | |||
@@ -254,6 +254,7 @@ static struct ipa_cmd_names qeth_ipa_cmd_names[] = { | |||
254 | {IPA_CMD_DESTROY_ADDR, "destroy_addr"}, | 254 | {IPA_CMD_DESTROY_ADDR, "destroy_addr"}, |
255 | {IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"}, | 255 | {IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"}, |
256 | {IPA_CMD_UNREGISTER_LOCAL_ADDR, "unregister_local_addr"}, | 256 | {IPA_CMD_UNREGISTER_LOCAL_ADDR, "unregister_local_addr"}, |
257 | {IPA_CMD_ADDRESS_CHANGE_NOTIF, "address_change_notification"}, | ||
257 | {IPA_CMD_UNKNOWN, "unknown"}, | 258 | {IPA_CMD_UNKNOWN, "unknown"}, |
258 | }; | 259 | }; |
259 | 260 | ||
diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index de6267990c5e..cf6a90ed42ae 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h | |||
@@ -109,6 +109,7 @@ enum qeth_ipa_cmds { | |||
109 | IPA_CMD_DESTROY_ADDR = 0xc4, | 109 | IPA_CMD_DESTROY_ADDR = 0xc4, |
110 | IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1, | 110 | IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1, |
111 | IPA_CMD_UNREGISTER_LOCAL_ADDR = 0xd2, | 111 | IPA_CMD_UNREGISTER_LOCAL_ADDR = 0xd2, |
112 | IPA_CMD_ADDRESS_CHANGE_NOTIF = 0xd3, | ||
112 | IPA_CMD_UNKNOWN = 0x00 | 113 | IPA_CMD_UNKNOWN = 0x00 |
113 | }; | 114 | }; |
114 | 115 | ||
@@ -520,6 +521,11 @@ struct net_if_token { | |||
520 | __u16 chid; | 521 | __u16 chid; |
521 | } __packed; | 522 | } __packed; |
522 | 523 | ||
524 | struct mac_addr_lnid { | ||
525 | __u8 mac[6]; | ||
526 | __u16 lnid; | ||
527 | } __packed; | ||
528 | |||
523 | struct qeth_ipacmd_sbp_hdr { | 529 | struct qeth_ipacmd_sbp_hdr { |
524 | __u32 supported_sbp_cmds; | 530 | __u32 supported_sbp_cmds; |
525 | __u32 enabled_sbp_cmds; | 531 | __u32 enabled_sbp_cmds; |
@@ -583,6 +589,37 @@ struct qeth_ipacmd_setbridgeport { | |||
583 | } data; | 589 | } data; |
584 | } __packed; | 590 | } __packed; |
585 | 591 | ||
592 | /* ADDRESS_CHANGE_NOTIFICATION adapter-initiated "command" *******************/ | ||
593 | /* Bitmask for entry->change_code. Both bits may be raised. */ | ||
594 | enum qeth_ipa_addr_change_code { | ||
595 | IPA_ADDR_CHANGE_CODE_VLANID = 0x01, | ||
596 | IPA_ADDR_CHANGE_CODE_MACADDR = 0x02, | ||
597 | IPA_ADDR_CHANGE_CODE_REMOVAL = 0x80, /* else addition */ | ||
598 | }; | ||
599 | enum qeth_ipa_addr_change_retcode { | ||
600 | IPA_ADDR_CHANGE_RETCODE_OK = 0x0000, | ||
601 | IPA_ADDR_CHANGE_RETCODE_LOSTEVENTS = 0x0010, | ||
602 | }; | ||
603 | enum qeth_ipa_addr_change_lostmask { | ||
604 | IPA_ADDR_CHANGE_MASK_OVERFLOW = 0x01, | ||
605 | IPA_ADDR_CHANGE_MASK_STATECHANGE = 0x02, | ||
606 | }; | ||
607 | |||
608 | struct qeth_ipacmd_addr_change_entry { | ||
609 | struct net_if_token token; | ||
610 | struct mac_addr_lnid addr_lnid; | ||
611 | __u8 change_code; | ||
612 | __u8 reserved1; | ||
613 | __u16 reserved2; | ||
614 | } __packed; | ||
615 | |||
616 | struct qeth_ipacmd_addr_change { | ||
617 | __u8 lost_event_mask; | ||
618 | __u8 reserved; | ||
619 | __u16 num_entries; | ||
620 | struct qeth_ipacmd_addr_change_entry entry[]; | ||
621 | } __packed; | ||
622 | |||
586 | /* Header for each IPA command */ | 623 | /* Header for each IPA command */ |
587 | struct qeth_ipacmd_hdr { | 624 | struct qeth_ipacmd_hdr { |
588 | __u8 command; | 625 | __u8 command; |
@@ -613,6 +650,7 @@ struct qeth_ipa_cmd { | |||
613 | struct qeth_set_routing setrtg; | 650 | struct qeth_set_routing setrtg; |
614 | struct qeth_ipacmd_diagass diagass; | 651 | struct qeth_ipacmd_diagass diagass; |
615 | struct qeth_ipacmd_setbridgeport sbp; | 652 | struct qeth_ipacmd_setbridgeport sbp; |
653 | struct qeth_ipacmd_addr_change addrchange; | ||
616 | } data; | 654 | } data; |
617 | } __attribute__ ((packed)); | 655 | } __attribute__ ((packed)); |
618 | 656 | ||
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 875d080e4e86..914d2c121fd8 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c | |||
@@ -1354,6 +1354,71 @@ EXPORT_SYMBOL(qeth_osn_deregister); | |||
1354 | 1354 | ||
1355 | /* SETBRIDGEPORT support, async notifications */ | 1355 | /* SETBRIDGEPORT support, async notifications */ |
1356 | 1356 | ||
1357 | enum qeth_an_event_type {anev_reg_unreg, anev_abort, anev_reset}; | ||
1358 | |||
1359 | /** | ||
1360 | * qeth_bridge_emit_host_event() - bridgeport address change notification | ||
1361 | * @card: qeth_card structure pointer, for udev events. | ||
1362 | * @evtype: "normal" register/unregister, or abort, or reset. For abort | ||
1363 | * and reset token and addr_lnid are unused and may be NULL. | ||
1364 | * @code: event bitmask: high order bit 0x80 value 1 means removal of an | ||
1365 | * object, 0 - addition of an object. | ||
1366 | * 0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC. | ||
1367 | * @token: "network token" structure identifying physical address of the port. | ||
1368 | * @addr_lnid: pointer to structure with MAC address and VLAN ID. | ||
1369 | * | ||
1370 | * This function is called when registrations and deregistrations are | ||
1371 | * reported by the hardware, and also when notifications are enabled - | ||
1372 | * for all currently registered addresses. | ||
1373 | */ | ||
1374 | static void qeth_bridge_emit_host_event(struct qeth_card *card, | ||
1375 | enum qeth_an_event_type evtype, | ||
1376 | u8 code, struct net_if_token *token, struct mac_addr_lnid *addr_lnid) | ||
1377 | { | ||
1378 | char str[7][32]; | ||
1379 | char *env[8]; | ||
1380 | int i = 0; | ||
1381 | |||
1382 | switch (evtype) { | ||
1383 | case anev_reg_unreg: | ||
1384 | snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=%s", | ||
1385 | (code & IPA_ADDR_CHANGE_CODE_REMOVAL) | ||
1386 | ? "deregister" : "register"); | ||
1387 | env[i] = str[i]; i++; | ||
1388 | if (code & IPA_ADDR_CHANGE_CODE_VLANID) { | ||
1389 | snprintf(str[i], sizeof(str[i]), "VLAN=%d", | ||
1390 | addr_lnid->lnid); | ||
1391 | env[i] = str[i]; i++; | ||
1392 | } | ||
1393 | if (code & IPA_ADDR_CHANGE_CODE_MACADDR) { | ||
1394 | snprintf(str[i], sizeof(str[i]), "MAC=%pM6", | ||
1395 | &addr_lnid->mac); | ||
1396 | env[i] = str[i]; i++; | ||
1397 | } | ||
1398 | snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x", | ||
1399 | token->cssid, token->ssid, token->devnum); | ||
1400 | env[i] = str[i]; i++; | ||
1401 | snprintf(str[i], sizeof(str[i]), "NTOK_IID=%02x", token->iid); | ||
1402 | env[i] = str[i]; i++; | ||
1403 | snprintf(str[i], sizeof(str[i]), "NTOK_CHPID=%02x", | ||
1404 | token->chpid); | ||
1405 | env[i] = str[i]; i++; | ||
1406 | snprintf(str[i], sizeof(str[i]), "NTOK_CHID=%04x", token->chid); | ||
1407 | env[i] = str[i]; i++; | ||
1408 | break; | ||
1409 | case anev_abort: | ||
1410 | snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=abort"); | ||
1411 | env[i] = str[i]; i++; | ||
1412 | break; | ||
1413 | case anev_reset: | ||
1414 | snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=reset"); | ||
1415 | env[i] = str[i]; i++; | ||
1416 | break; | ||
1417 | } | ||
1418 | env[i] = NULL; | ||
1419 | kobject_uevent_env(&card->gdev->dev.kobj, KOBJ_CHANGE, env); | ||
1420 | } | ||
1421 | |||
1357 | struct qeth_bridge_state_data { | 1422 | struct qeth_bridge_state_data { |
1358 | struct work_struct worker; | 1423 | struct work_struct worker; |
1359 | struct qeth_card *card; | 1424 | struct qeth_card *card; |
@@ -1425,6 +1490,78 @@ void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd) | |||
1425 | } | 1490 | } |
1426 | EXPORT_SYMBOL(qeth_bridge_state_change); | 1491 | EXPORT_SYMBOL(qeth_bridge_state_change); |
1427 | 1492 | ||
1493 | struct qeth_bridge_host_data { | ||
1494 | struct work_struct worker; | ||
1495 | struct qeth_card *card; | ||
1496 | struct qeth_ipacmd_addr_change hostevs; | ||
1497 | }; | ||
1498 | |||
1499 | static void qeth_bridge_host_event_worker(struct work_struct *work) | ||
1500 | { | ||
1501 | struct qeth_bridge_host_data *data = | ||
1502 | container_of(work, struct qeth_bridge_host_data, worker); | ||
1503 | int i; | ||
1504 | |||
1505 | if (data->hostevs.lost_event_mask) { | ||
1506 | dev_info(&data->card->gdev->dev, | ||
1507 | "Address notification from the HiperSockets Bridge Port stopped %s (%s)\n", | ||
1508 | data->card->dev->name, | ||
1509 | (data->hostevs.lost_event_mask == 0x01) | ||
1510 | ? "Overflow" | ||
1511 | : (data->hostevs.lost_event_mask == 0x02) | ||
1512 | ? "Bridge port state change" | ||
1513 | : "Unknown reason"); | ||
1514 | mutex_lock(&data->card->conf_mutex); | ||
1515 | data->card->options.sbp.hostnotification = 0; | ||
1516 | mutex_unlock(&data->card->conf_mutex); | ||
1517 | qeth_bridge_emit_host_event(data->card, anev_abort, | ||
1518 | 0, NULL, NULL); | ||
1519 | } else | ||
1520 | for (i = 0; i < data->hostevs.num_entries; i++) { | ||
1521 | struct qeth_ipacmd_addr_change_entry *entry = | ||
1522 | &data->hostevs.entry[i]; | ||
1523 | qeth_bridge_emit_host_event(data->card, | ||
1524 | anev_reg_unreg, | ||
1525 | entry->change_code, | ||
1526 | &entry->token, &entry->addr_lnid); | ||
1527 | } | ||
1528 | kfree(data); | ||
1529 | } | ||
1530 | |||
1531 | void qeth_bridge_host_event(struct qeth_card *card, struct qeth_ipa_cmd *cmd) | ||
1532 | { | ||
1533 | struct qeth_ipacmd_addr_change *hostevs = | ||
1534 | &cmd->data.addrchange; | ||
1535 | struct qeth_bridge_host_data *data; | ||
1536 | int extrasize; | ||
1537 | |||
1538 | QETH_CARD_TEXT(card, 2, "brhostev"); | ||
1539 | if (cmd->hdr.return_code != 0x0000) { | ||
1540 | if (cmd->hdr.return_code == 0x0010) { | ||
1541 | if (hostevs->lost_event_mask == 0x00) | ||
1542 | hostevs->lost_event_mask = 0xff; | ||
1543 | } else { | ||
1544 | QETH_CARD_TEXT_(card, 2, "BPHe%04x", | ||
1545 | cmd->hdr.return_code); | ||
1546 | return; | ||
1547 | } | ||
1548 | } | ||
1549 | extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) * | ||
1550 | hostevs->num_entries; | ||
1551 | data = kzalloc(sizeof(struct qeth_bridge_host_data) + extrasize, | ||
1552 | GFP_ATOMIC); | ||
1553 | if (!data) { | ||
1554 | QETH_CARD_TEXT(card, 2, "BPHalloc"); | ||
1555 | return; | ||
1556 | } | ||
1557 | INIT_WORK(&data->worker, qeth_bridge_host_event_worker); | ||
1558 | data->card = card; | ||
1559 | memcpy(&data->hostevs, hostevs, | ||
1560 | sizeof(struct qeth_ipacmd_addr_change) + extrasize); | ||
1561 | queue_work(qeth_wq, &data->worker); | ||
1562 | } | ||
1563 | EXPORT_SYMBOL(qeth_bridge_host_event); | ||
1564 | |||
1428 | /* SETBRIDGEPORT support; sending commands */ | 1565 | /* SETBRIDGEPORT support; sending commands */ |
1429 | 1566 | ||
1430 | struct _qeth_sbp_cbctl { | 1567 | struct _qeth_sbp_cbctl { |
@@ -1711,6 +1848,99 @@ int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role) | |||
1711 | return rc; | 1848 | return rc; |
1712 | } | 1849 | } |
1713 | 1850 | ||
1851 | /** | ||
1852 | * qeth_anset_makerc() - derive "traditional" error from hardware codes. | ||
1853 | * @card: qeth_card structure pointer, for debug messages. | ||
1854 | * | ||
1855 | * Returns negative errno-compatible error indication or 0 on success. | ||
1856 | */ | ||
1857 | static int qeth_anset_makerc(struct qeth_card *card, int pnso_rc, u16 response) | ||
1858 | { | ||
1859 | int rc; | ||
1860 | |||
1861 | if (pnso_rc == 0) | ||
1862 | switch (response) { | ||
1863 | case 0x0001: | ||
1864 | rc = 0; | ||
1865 | break; | ||
1866 | case 0x0004: | ||
1867 | case 0x0100: | ||
1868 | case 0x0106: | ||
1869 | rc = -ENOSYS; | ||
1870 | dev_err(&card->gdev->dev, | ||
1871 | "Setting address notification failed\n"); | ||
1872 | break; | ||
1873 | case 0x0107: | ||
1874 | rc = -EAGAIN; | ||
1875 | break; | ||
1876 | default: | ||
1877 | rc = -EIO; | ||
1878 | } | ||
1879 | else | ||
1880 | rc = -EIO; | ||
1881 | |||
1882 | if (rc) { | ||
1883 | QETH_CARD_TEXT_(card, 2, "SBPp%04x", pnso_rc); | ||
1884 | QETH_CARD_TEXT_(card, 2, "SBPr%04x", response); | ||
1885 | } | ||
1886 | return rc; | ||
1887 | } | ||
1888 | |||
1889 | static void qeth_bridgeport_an_set_cb(void *priv, | ||
1890 | enum qdio_brinfo_entry_type type, void *entry) | ||
1891 | { | ||
1892 | struct qeth_card *card = (struct qeth_card *)priv; | ||
1893 | struct qdio_brinfo_entry_l2 *l2entry; | ||
1894 | u8 code; | ||
1895 | |||
1896 | if (type != l2_addr_lnid) { | ||
1897 | WARN_ON_ONCE(1); | ||
1898 | return; | ||
1899 | } | ||
1900 | |||
1901 | l2entry = (struct qdio_brinfo_entry_l2 *)entry; | ||
1902 | code = IPA_ADDR_CHANGE_CODE_MACADDR; | ||
1903 | if (l2entry->addr_lnid.lnid) | ||
1904 | code |= IPA_ADDR_CHANGE_CODE_VLANID; | ||
1905 | qeth_bridge_emit_host_event(card, anev_reg_unreg, code, | ||
1906 | (struct net_if_token *)&l2entry->nit, | ||
1907 | (struct mac_addr_lnid *)&l2entry->addr_lnid); | ||
1908 | } | ||
1909 | |||
1910 | /** | ||
1911 | * qeth_bridgeport_an_set() - Enable or disable bridgeport address notification | ||
1912 | * @card: qeth_card structure pointer. | ||
1913 | * @enable: 0 - disable, non-zero - enable notifications | ||
1914 | * | ||
1915 | * Returns negative errno-compatible error indication or 0 on success. | ||
1916 | * | ||
1917 | * On enable, emits a series of address notifications udev events for all | ||
1918 | * currently registered hosts. | ||
1919 | */ | ||
1920 | int qeth_bridgeport_an_set(struct qeth_card *card, int enable) | ||
1921 | { | ||
1922 | int rc; | ||
1923 | u16 response; | ||
1924 | struct ccw_device *ddev; | ||
1925 | struct subchannel_id schid; | ||
1926 | |||
1927 | if (!card) | ||
1928 | return -EINVAL; | ||
1929 | if (!card->options.sbp.supported_funcs) | ||
1930 | return -EOPNOTSUPP; | ||
1931 | ddev = CARD_DDEV(card); | ||
1932 | ccw_device_get_schid(ddev, &schid); | ||
1933 | |||
1934 | if (enable) { | ||
1935 | qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL); | ||
1936 | rc = qdio_pnso_brinfo(schid, 1, &response, | ||
1937 | qeth_bridgeport_an_set_cb, card); | ||
1938 | } else | ||
1939 | rc = qdio_pnso_brinfo(schid, 0, &response, NULL, NULL); | ||
1940 | return qeth_anset_makerc(card, rc, response); | ||
1941 | } | ||
1942 | EXPORT_SYMBOL_GPL(qeth_bridgeport_an_set); | ||
1943 | |||
1714 | module_init(qeth_l2_init); | 1944 | module_init(qeth_l2_init); |
1715 | module_exit(qeth_l2_exit); | 1945 | module_exit(qeth_l2_exit); |
1716 | MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); | 1946 | MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); |
diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c index 17fd4cd888f9..ae1bc04b8653 100644 --- a/drivers/s390/net/qeth_l2_sys.c +++ b/drivers/s390/net/qeth_l2_sys.c | |||
@@ -119,9 +119,63 @@ static ssize_t qeth_bridge_port_state_show(struct device *dev, | |||
119 | static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show, | 119 | static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show, |
120 | NULL); | 120 | NULL); |
121 | 121 | ||
122 | static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev, | ||
123 | struct device_attribute *attr, char *buf) | ||
124 | { | ||
125 | struct qeth_card *card = dev_get_drvdata(dev); | ||
126 | int enabled; | ||
127 | |||
128 | if (!card) | ||
129 | return -EINVAL; | ||
130 | |||
131 | mutex_lock(&card->conf_mutex); | ||
132 | |||
133 | enabled = card->options.sbp.hostnotification; | ||
134 | |||
135 | mutex_unlock(&card->conf_mutex); | ||
136 | |||
137 | return sprintf(buf, "%d\n", enabled); | ||
138 | } | ||
139 | |||
140 | static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev, | ||
141 | struct device_attribute *attr, const char *buf, size_t count) | ||
142 | { | ||
143 | struct qeth_card *card = dev_get_drvdata(dev); | ||
144 | int rc = 0; | ||
145 | int enable; | ||
146 | |||
147 | if (!card) | ||
148 | return -EINVAL; | ||
149 | |||
150 | if (sysfs_streq(buf, "0")) | ||
151 | enable = 0; | ||
152 | else if (sysfs_streq(buf, "1")) | ||
153 | enable = 1; | ||
154 | else | ||
155 | return -EINVAL; | ||
156 | |||
157 | mutex_lock(&card->conf_mutex); | ||
158 | |||
159 | if (qeth_card_hw_is_reachable(card)) { | ||
160 | rc = qeth_bridgeport_an_set(card, enable); | ||
161 | if (!rc) | ||
162 | card->options.sbp.hostnotification = enable; | ||
163 | } else | ||
164 | card->options.sbp.hostnotification = enable; | ||
165 | |||
166 | mutex_unlock(&card->conf_mutex); | ||
167 | |||
168 | return rc ? rc : count; | ||
169 | } | ||
170 | |||
171 | static DEVICE_ATTR(bridge_hostnotify, 0644, | ||
172 | qeth_bridgeport_hostnotification_show, | ||
173 | qeth_bridgeport_hostnotification_store); | ||
174 | |||
122 | static struct attribute *qeth_l2_bridgeport_attrs[] = { | 175 | static struct attribute *qeth_l2_bridgeport_attrs[] = { |
123 | &dev_attr_bridge_role.attr, | 176 | &dev_attr_bridge_role.attr, |
124 | &dev_attr_bridge_state.attr, | 177 | &dev_attr_bridge_state.attr, |
178 | &dev_attr_bridge_hostnotify.attr, | ||
125 | NULL, | 179 | NULL, |
126 | }; | 180 | }; |
127 | 181 | ||
@@ -147,6 +201,8 @@ void qeth_l2_remove_device_attributes(struct device *dev) | |||
147 | */ | 201 | */ |
148 | void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) | 202 | void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) |
149 | { | 203 | { |
204 | int rc; | ||
205 | |||
150 | if (!card) | 206 | if (!card) |
151 | return; | 207 | return; |
152 | if (!card->options.sbp.supported_funcs) | 208 | if (!card->options.sbp.supported_funcs) |
@@ -158,4 +214,10 @@ void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) | |||
158 | qeth_bridgeport_query_ports(card, | 214 | qeth_bridgeport_query_ports(card, |
159 | &card->options.sbp.role, NULL); | 215 | &card->options.sbp.role, NULL); |
160 | } | 216 | } |
217 | if (card->options.sbp.hostnotification) { | ||
218 | rc = qeth_bridgeport_an_set(card, 1); | ||
219 | if (rc) | ||
220 | card->options.sbp.hostnotification = 0; | ||
221 | } else | ||
222 | qeth_bridgeport_an_set(card, 0); | ||
161 | } | 223 | } |