Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Eugene Crosser and committed by
David S. Miller
9f48b9db 59b55a4d

+368
+29
Documentation/s390/qeth.txt
··· 19 19 ROLE={primary|secondary|none} - the role assigned to the port. 20 20 21 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.
+5
drivers/s390/net/qeth_core.h
··· 169 169 QETH_SBP_STATE_ACTIVE = 2, 170 170 }; 171 171 172 + #define QETH_SBP_HOST_NOTIFICATION 1 173 + 172 174 struct qeth_sbp_info { 173 175 __u32 supported_funcs; 174 176 enum qeth_sbp_roles role; 177 + __u32 hostnotification:1; 175 178 }; 176 179 177 180 static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, ··· 953 950 int qeth_bridgeport_query_ports(struct qeth_card *card, 954 951 enum qeth_sbp_roles *role, enum qeth_sbp_states *state); 955 952 int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); 953 + int qeth_bridgeport_an_set(struct qeth_card *card, int enable); 954 + void qeth_bridge_host_event(struct qeth_card *card, struct qeth_ipa_cmd *cmd); 956 955 int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); 957 956 int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int); 958 957 int qeth_get_elements_for_frags(struct sk_buff *);
+3
drivers/s390/net/qeth_core_main.c
··· 622 622 return NULL; 623 623 } else 624 624 return cmd; 625 + case IPA_CMD_ADDRESS_CHANGE_NOTIF: 626 + qeth_bridge_host_event(card, cmd); 627 + return NULL; 625 628 case IPA_CMD_MODCCID: 626 629 return cmd; 627 630 case IPA_CMD_REGISTER_LOCAL_ADDR:
+1
drivers/s390/net/qeth_core_mpc.c
··· 254 254 {IPA_CMD_DESTROY_ADDR, "destroy_addr"}, 255 255 {IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"}, 256 256 {IPA_CMD_UNREGISTER_LOCAL_ADDR, "unregister_local_addr"}, 257 + {IPA_CMD_ADDRESS_CHANGE_NOTIF, "address_change_notification"}, 257 258 {IPA_CMD_UNKNOWN, "unknown"}, 258 259 }; 259 260
+38
drivers/s390/net/qeth_core_mpc.h
··· 109 109 IPA_CMD_DESTROY_ADDR = 0xc4, 110 110 IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1, 111 111 IPA_CMD_UNREGISTER_LOCAL_ADDR = 0xd2, 112 + IPA_CMD_ADDRESS_CHANGE_NOTIF = 0xd3, 112 113 IPA_CMD_UNKNOWN = 0x00 113 114 }; 114 115 ··· 521 520 __u16 chid; 522 521 } __packed; 523 522 523 + struct mac_addr_lnid { 524 + __u8 mac[6]; 525 + __u16 lnid; 526 + } __packed; 527 + 524 528 struct qeth_ipacmd_sbp_hdr { 525 529 __u32 supported_sbp_cmds; 526 530 __u32 enabled_sbp_cmds; ··· 589 583 } data; 590 584 } __packed; 591 585 586 + /* ADDRESS_CHANGE_NOTIFICATION adapter-initiated "command" *******************/ 587 + /* Bitmask for entry->change_code. Both bits may be raised. */ 588 + enum qeth_ipa_addr_change_code { 589 + IPA_ADDR_CHANGE_CODE_VLANID = 0x01, 590 + IPA_ADDR_CHANGE_CODE_MACADDR = 0x02, 591 + IPA_ADDR_CHANGE_CODE_REMOVAL = 0x80, /* else addition */ 592 + }; 593 + enum qeth_ipa_addr_change_retcode { 594 + IPA_ADDR_CHANGE_RETCODE_OK = 0x0000, 595 + IPA_ADDR_CHANGE_RETCODE_LOSTEVENTS = 0x0010, 596 + }; 597 + enum qeth_ipa_addr_change_lostmask { 598 + IPA_ADDR_CHANGE_MASK_OVERFLOW = 0x01, 599 + IPA_ADDR_CHANGE_MASK_STATECHANGE = 0x02, 600 + }; 601 + 602 + struct qeth_ipacmd_addr_change_entry { 603 + struct net_if_token token; 604 + struct mac_addr_lnid addr_lnid; 605 + __u8 change_code; 606 + __u8 reserved1; 607 + __u16 reserved2; 608 + } __packed; 609 + 610 + struct qeth_ipacmd_addr_change { 611 + __u8 lost_event_mask; 612 + __u8 reserved; 613 + __u16 num_entries; 614 + struct qeth_ipacmd_addr_change_entry entry[]; 615 + } __packed; 616 + 592 617 /* Header for each IPA command */ 593 618 struct qeth_ipacmd_hdr { 594 619 __u8 command; ··· 650 613 struct qeth_set_routing setrtg; 651 614 struct qeth_ipacmd_diagass diagass; 652 615 struct qeth_ipacmd_setbridgeport sbp; 616 + struct qeth_ipacmd_addr_change addrchange; 653 617 } data; 654 618 } __attribute__ ((packed)); 655 619
+230
drivers/s390/net/qeth_l2_main.c
··· 1354 1354 1355 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 1422 struct qeth_bridge_state_data { 1358 1423 struct work_struct worker; 1359 1424 struct qeth_card *card; ··· 1489 1424 queue_work(qeth_wq, &data->worker); 1490 1425 } 1491 1426 EXPORT_SYMBOL(qeth_bridge_state_change); 1427 + 1428 + struct qeth_bridge_host_data { 1429 + struct work_struct worker; 1430 + struct qeth_card *card; 1431 + struct qeth_ipacmd_addr_change hostevs; 1432 + }; 1433 + 1434 + static void qeth_bridge_host_event_worker(struct work_struct *work) 1435 + { 1436 + struct qeth_bridge_host_data *data = 1437 + container_of(work, struct qeth_bridge_host_data, worker); 1438 + int i; 1439 + 1440 + if (data->hostevs.lost_event_mask) { 1441 + dev_info(&data->card->gdev->dev, 1442 + "Address notification from the HiperSockets Bridge Port stopped %s (%s)\n", 1443 + data->card->dev->name, 1444 + (data->hostevs.lost_event_mask == 0x01) 1445 + ? "Overflow" 1446 + : (data->hostevs.lost_event_mask == 0x02) 1447 + ? "Bridge port state change" 1448 + : "Unknown reason"); 1449 + mutex_lock(&data->card->conf_mutex); 1450 + data->card->options.sbp.hostnotification = 0; 1451 + mutex_unlock(&data->card->conf_mutex); 1452 + qeth_bridge_emit_host_event(data->card, anev_abort, 1453 + 0, NULL, NULL); 1454 + } else 1455 + for (i = 0; i < data->hostevs.num_entries; i++) { 1456 + struct qeth_ipacmd_addr_change_entry *entry = 1457 + &data->hostevs.entry[i]; 1458 + qeth_bridge_emit_host_event(data->card, 1459 + anev_reg_unreg, 1460 + entry->change_code, 1461 + &entry->token, &entry->addr_lnid); 1462 + } 1463 + kfree(data); 1464 + } 1465 + 1466 + void qeth_bridge_host_event(struct qeth_card *card, struct qeth_ipa_cmd *cmd) 1467 + { 1468 + struct qeth_ipacmd_addr_change *hostevs = 1469 + &cmd->data.addrchange; 1470 + struct qeth_bridge_host_data *data; 1471 + int extrasize; 1472 + 1473 + QETH_CARD_TEXT(card, 2, "brhostev"); 1474 + if (cmd->hdr.return_code != 0x0000) { 1475 + if (cmd->hdr.return_code == 0x0010) { 1476 + if (hostevs->lost_event_mask == 0x00) 1477 + hostevs->lost_event_mask = 0xff; 1478 + } else { 1479 + QETH_CARD_TEXT_(card, 2, "BPHe%04x", 1480 + cmd->hdr.return_code); 1481 + return; 1482 + } 1483 + } 1484 + extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) * 1485 + hostevs->num_entries; 1486 + data = kzalloc(sizeof(struct qeth_bridge_host_data) + extrasize, 1487 + GFP_ATOMIC); 1488 + if (!data) { 1489 + QETH_CARD_TEXT(card, 2, "BPHalloc"); 1490 + return; 1491 + } 1492 + INIT_WORK(&data->worker, qeth_bridge_host_event_worker); 1493 + data->card = card; 1494 + memcpy(&data->hostevs, hostevs, 1495 + sizeof(struct qeth_ipacmd_addr_change) + extrasize); 1496 + queue_work(qeth_wq, &data->worker); 1497 + } 1498 + EXPORT_SYMBOL(qeth_bridge_host_event); 1492 1499 1493 1500 /* SETBRIDGEPORT support; sending commands */ 1494 1501 ··· 1847 1710 rc = qeth_bridgeport_makerc(card, &cbctl, setcmd); 1848 1711 return rc; 1849 1712 } 1713 + 1714 + /** 1715 + * qeth_anset_makerc() - derive "traditional" error from hardware codes. 1716 + * @card: qeth_card structure pointer, for debug messages. 1717 + * 1718 + * Returns negative errno-compatible error indication or 0 on success. 1719 + */ 1720 + static int qeth_anset_makerc(struct qeth_card *card, int pnso_rc, u16 response) 1721 + { 1722 + int rc; 1723 + 1724 + if (pnso_rc == 0) 1725 + switch (response) { 1726 + case 0x0001: 1727 + rc = 0; 1728 + break; 1729 + case 0x0004: 1730 + case 0x0100: 1731 + case 0x0106: 1732 + rc = -ENOSYS; 1733 + dev_err(&card->gdev->dev, 1734 + "Setting address notification failed\n"); 1735 + break; 1736 + case 0x0107: 1737 + rc = -EAGAIN; 1738 + break; 1739 + default: 1740 + rc = -EIO; 1741 + } 1742 + else 1743 + rc = -EIO; 1744 + 1745 + if (rc) { 1746 + QETH_CARD_TEXT_(card, 2, "SBPp%04x", pnso_rc); 1747 + QETH_CARD_TEXT_(card, 2, "SBPr%04x", response); 1748 + } 1749 + return rc; 1750 + } 1751 + 1752 + static void qeth_bridgeport_an_set_cb(void *priv, 1753 + enum qdio_brinfo_entry_type type, void *entry) 1754 + { 1755 + struct qeth_card *card = (struct qeth_card *)priv; 1756 + struct qdio_brinfo_entry_l2 *l2entry; 1757 + u8 code; 1758 + 1759 + if (type != l2_addr_lnid) { 1760 + WARN_ON_ONCE(1); 1761 + return; 1762 + } 1763 + 1764 + l2entry = (struct qdio_brinfo_entry_l2 *)entry; 1765 + code = IPA_ADDR_CHANGE_CODE_MACADDR; 1766 + if (l2entry->addr_lnid.lnid) 1767 + code |= IPA_ADDR_CHANGE_CODE_VLANID; 1768 + qeth_bridge_emit_host_event(card, anev_reg_unreg, code, 1769 + (struct net_if_token *)&l2entry->nit, 1770 + (struct mac_addr_lnid *)&l2entry->addr_lnid); 1771 + } 1772 + 1773 + /** 1774 + * qeth_bridgeport_an_set() - Enable or disable bridgeport address notification 1775 + * @card: qeth_card structure pointer. 1776 + * @enable: 0 - disable, non-zero - enable notifications 1777 + * 1778 + * Returns negative errno-compatible error indication or 0 on success. 1779 + * 1780 + * On enable, emits a series of address notifications udev events for all 1781 + * currently registered hosts. 1782 + */ 1783 + int qeth_bridgeport_an_set(struct qeth_card *card, int enable) 1784 + { 1785 + int rc; 1786 + u16 response; 1787 + struct ccw_device *ddev; 1788 + struct subchannel_id schid; 1789 + 1790 + if (!card) 1791 + return -EINVAL; 1792 + if (!card->options.sbp.supported_funcs) 1793 + return -EOPNOTSUPP; 1794 + ddev = CARD_DDEV(card); 1795 + ccw_device_get_schid(ddev, &schid); 1796 + 1797 + if (enable) { 1798 + qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL); 1799 + rc = qdio_pnso_brinfo(schid, 1, &response, 1800 + qeth_bridgeport_an_set_cb, card); 1801 + } else 1802 + rc = qdio_pnso_brinfo(schid, 0, &response, NULL, NULL); 1803 + return qeth_anset_makerc(card, rc, response); 1804 + } 1805 + EXPORT_SYMBOL_GPL(qeth_bridgeport_an_set); 1850 1806 1851 1807 module_init(qeth_l2_init); 1852 1808 module_exit(qeth_l2_exit);
+62
drivers/s390/net/qeth_l2_sys.c
··· 119 119 static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show, 120 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 175 static struct attribute *qeth_l2_bridgeport_attrs[] = { 123 176 &dev_attr_bridge_role.attr, 124 177 &dev_attr_bridge_state.attr, 178 + &dev_attr_bridge_hostnotify.attr, 125 179 NULL, 126 180 }; 127 181 ··· 201 147 */ 202 148 void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) 203 149 { 150 + int rc; 151 + 204 152 if (!card) 205 153 return; 206 154 if (!card->options.sbp.supported_funcs) ··· 214 158 qeth_bridgeport_query_ports(card, 215 159 &card->options.sbp.role, NULL); 216 160 } 161 + if (card->options.sbp.hostnotification) { 162 + rc = qeth_bridgeport_an_set(card, 1); 163 + if (rc) 164 + card->options.sbp.hostnotification = 0; 165 + } else 166 + qeth_bridgeport_an_set(card, 0); 217 167 }