s390/qeth: Detect PNSO OC3 capability
authorAlexandra Winter <wintera@linux.ibm.com>
Thu, 10 Sep 2020 17:23:46 +0000 (19:23 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 15 Sep 2020 20:21:47 +0000 (13:21 -0700)
This patch detects whether device-to-bridge-notification, provided
by the Perform Network Subchannel Operation (PNSO) operation code
ADDR_INFO (OC3), is supported by this card. A following patch will
map this to the learning_sync bridgeport flag, so we store it in
priv->brport_hw_features in bridgeport flag format.

Only IQD cards provide PNSO.
There is a feature bit to indicate whether the machine provides OC3,
unfortunately it is not set on old machines.
So PNSO is called to find out. As this will disable notification
and is exclusive with bridgeport_notification, this must be done
during card initialisation before previous settings are restored.

PNSO functionality requires some configuration values that are added to
the qeth_card.info structure. Some helper functions are defined to fill
them out when the card is brought online and some other places are
adapted, that can also benefit from these fields.

Signed-off-by: Alexandra Winter <wintera@linux.ibm.com>
Reviewed-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_l2_main.c

index da46af682af85cbb65a03cd6eedfbda9795ab00f..14c583b5ea11d26bdc0ec4c1d0a5d15da7787002 100644 (file)
@@ -684,9 +684,16 @@ enum qeth_pnso_mode {
 struct qeth_card_info {
        unsigned short unit_addr2;
        unsigned short cula;
-       u8 chpid;
        __u16 func_level;
        char mcl_level[QETH_MCL_LENGTH + 1];
+       /* doubleword below corresponds to net_if_token */
+       u16 ddev_devno;
+       u8 cssid;
+       u8 iid;
+       u8 ssid;
+       u8 chpid;
+       u16 chid;
+       u8 ids_valid:1; /* cssid,iid,chid */
        u8 dev_addr_is_registered:1;
        u8 open_when_online:1;
        u8 promisc_mode:1;
@@ -780,6 +787,7 @@ struct qeth_switch_info {
 
 struct qeth_priv {
        unsigned int rx_copybreak;
+       u32 brport_hw_features;
 };
 
 #define QETH_NAPI_WEIGHT NAPI_POLL_WEIGHT
index e19640bc6daa142a4c374eb898a156b28f43d601..7cd0cbf8a4f0fb6a8cf805897427aaa5c6e1e455 100644 (file)
@@ -2311,12 +2311,10 @@ static void qeth_idx_setup_activate_cmd(struct qeth_card *card,
        u16 addr = (card->info.cula << 8) + card->info.unit_addr2;
        u8 port = ((u8)card->dev->dev_port) | 0x80;
        struct ccw1 *ccw = __ccw_from_cmd(iob);
-       struct ccw_dev_id dev_id;
 
        qeth_setup_ccw(&ccw[0], CCW_CMD_WRITE, CCW_FLAG_CC, IDX_ACTIVATE_SIZE,
                       iob->data);
        qeth_setup_ccw(&ccw[1], CCW_CMD_READ, 0, iob->length, iob->data);
-       ccw_device_get_id(CARD_DDEV(card), &dev_id);
        iob->finalize = qeth_idx_finalize_cmd;
 
        port |= QETH_IDX_ACT_INVAL_FRAME;
@@ -2325,7 +2323,7 @@ static void qeth_idx_setup_activate_cmd(struct qeth_card *card,
               &card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH);
        memcpy(QETH_IDX_ACT_FUNC_LEVEL(iob->data),
               &card->info.func_level, 2);
-       memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &dev_id.devno, 2);
+       memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &card->info.ddev_devno, 2);
        memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &addr, 2);
 }
 
@@ -2599,7 +2597,6 @@ static int qeth_ulp_setup(struct qeth_card *card)
 {
        __u16 temp;
        struct qeth_cmd_buffer *iob;
-       struct ccw_dev_id dev_id;
 
        QETH_CARD_TEXT(card, 2, "ulpsetup");
 
@@ -2614,8 +2611,7 @@ static int qeth_ulp_setup(struct qeth_card *card)
        memcpy(QETH_ULP_SETUP_FILTER_TOKEN(iob->data),
               &card->token.ulp_filter_r, QETH_MPC_TOKEN_LENGTH);
 
-       ccw_device_get_id(CARD_DDEV(card), &dev_id);
-       memcpy(QETH_ULP_SETUP_CUA(iob->data), &dev_id.devno, 2);
+       memcpy(QETH_ULP_SETUP_CUA(iob->data), &card->info.ddev_devno, 2);
        temp = (card->info.cula << 8) + card->info.unit_addr2;
        memcpy(QETH_ULP_SETUP_REAL_DEVADDR(iob->data), &temp, 2);
        return qeth_send_control_data(card, iob, qeth_ulp_setup_cb, NULL);
@@ -4920,7 +4916,6 @@ int qeth_vm_request_mac(struct qeth_card *card)
 {
        struct diag26c_mac_resp *response;
        struct diag26c_mac_req *request;
-       struct ccw_dev_id id;
        int rc;
 
        QETH_CARD_TEXT(card, 2, "vmreqmac");
@@ -4932,11 +4927,10 @@ int qeth_vm_request_mac(struct qeth_card *card)
                goto out;
        }
 
-       ccw_device_get_id(CARD_DDEV(card), &id);
        request->resp_buf_len = sizeof(*response);
        request->resp_version = DIAG26C_VERSION2;
        request->op_code = DIAG26C_GET_MAC;
-       request->devno = id.devno;
+       request->devno = card->info.ddev_devno;
 
        QETH_DBF_HEX(CTRL, 2, request, sizeof(*request));
        rc = diag26c(request, response, DIAG26C_MAC_SERVICES);
@@ -5017,6 +5011,33 @@ out:
        return;
 }
 
+static void qeth_read_ccw_conf_data(struct qeth_card *card)
+{
+       struct qeth_card_info *info = &card->info;
+       struct ccw_device *cdev = CARD_DDEV(card);
+       struct ccw_dev_id dev_id;
+
+       QETH_CARD_TEXT(card, 2, "ccwconfd");
+       ccw_device_get_id(cdev, &dev_id);
+
+       info->ddev_devno = dev_id.devno;
+       info->ids_valid = !ccw_device_get_cssid(cdev, &info->cssid) &&
+                         !ccw_device_get_iid(cdev, &info->iid) &&
+                         !ccw_device_get_chid(cdev, 0, &info->chid);
+       info->ssid = dev_id.ssid;
+
+       dev_info(&card->gdev->dev, "CHID: %x CHPID: %x\n",
+                info->chid, info->chpid);
+
+       QETH_CARD_TEXT_(card, 3, "devn%x", info->ddev_devno);
+       QETH_CARD_TEXT_(card, 3, "cssid:%x", info->cssid);
+       QETH_CARD_TEXT_(card, 3, "iid:%x", info->iid);
+       QETH_CARD_TEXT_(card, 3, "ssid:%x", info->ssid);
+       QETH_CARD_TEXT_(card, 3, "chpid:%x", info->chpid);
+       QETH_CARD_TEXT_(card, 3, "chid:%x", info->chid);
+       QETH_CARD_TEXT_(card, 3, "idval%x", info->ids_valid);
+}
+
 static int qeth_qdio_establish(struct qeth_card *card)
 {
        struct qdio_buffer **out_sbal_ptrs[QETH_MAX_OUT_QUEUES];
@@ -5185,6 +5206,7 @@ retriable:
        }
 
        qeth_determine_capabilities(card);
+       qeth_read_ccw_conf_data(card);
        qeth_idx_init(card);
 
        rc = qeth_idx_activate_read_channel(card);
index 2ab130d5c42d6d7c4175a7cec45fe9067398a99e..7cba3d0035bfc299d225792fd05aeadbcceeb74c 100644 (file)
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
 #include <linux/list.h>
 #include <linux/hash.h>
 #include <linux/hashtable.h>
 #include <asm/chsc.h>
+#include <asm/css_chars.h>
 #include <asm/setup.h>
 #include "qeth_core.h"
 #include "qeth_l2.h"
@@ -826,6 +828,46 @@ static void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
        }
 }
 
+/**
+ *     qeth_l2_detect_dev2br_support() -
+ *     Detect whether this card supports 'dev to bridge fdb network address
+ *     change notification' and thus can support the learning_sync bridgeport
+ *     attribute
+ *     @card: qeth_card structure pointer
+ *
+ *     This is a destructive test and must be called before dev2br or
+ *     bridgeport address notification is enabled!
+ */
+static void qeth_l2_detect_dev2br_support(struct qeth_card *card)
+{
+       struct qeth_priv *priv = netdev_priv(card->dev);
+       bool dev2br_supported;
+       int rc;
+
+       QETH_CARD_TEXT(card, 2, "d2brsup");
+       if (!IS_IQD(card))
+               return;
+
+       /* dev2br requires valid cssid,iid,chid */
+       if (!card->info.ids_valid) {
+               dev2br_supported = false;
+       } else if (css_general_characteristics.enarf) {
+               dev2br_supported = true;
+       } else {
+               /* Old machines don't have the feature bit:
+                * Probe by testing whether a disable succeeds
+                */
+               rc = qeth_l2_pnso(card, PNSO_OC_NET_ADDR_INFO, 0, NULL, NULL);
+               dev2br_supported = !rc;
+       }
+       QETH_CARD_TEXT_(card, 2, "D2Bsup%02x", dev2br_supported);
+
+       if (dev2br_supported)
+               priv->brport_hw_features |= BR_LEARNING_SYNC;
+       else
+               priv->brport_hw_features &= ~BR_LEARNING_SYNC;
+}
+
 static int qeth_l2_set_online(struct qeth_card *card)
 {
        struct ccwgroup_device *gdev = card->gdev;
@@ -840,6 +882,9 @@ static int qeth_l2_set_online(struct qeth_card *card)
                goto out_remove;
        }
 
+       /* query before bridgeport_notification may be enabled */
+       qeth_l2_detect_dev2br_support(card);
+
        mutex_lock(&card->sbp_lock);
        qeth_bridgeport_query_support(card);
        if (card->options.sbp.supported_funcs) {