s390/qeth: allow bridgeport queries despite OS_MISMATCH

When HiperSockets interfaces on the same VCHID span different OS
families, reads of the sysfs attributes bridge_role and bridge_state
fail with -EPERM if bridge port ownership belongs to another OS family.

As a result, userspace tools such as 'lszdev -ii' cannot retrieve
bridge_role and bridge_state, even though firmware returns valid bridge
port data for QUERY_BRIDGE_PORTS requests.

The firmware reports IPA_RC_SBP_IQD_OS_MISMATCH (0x0010) to indicate
that bridge port ownership belongs to a different OS family. For
QUERY_BRIDGE_PORTS operations, firmware still returns valid bridge port
data (role=none, state=inactive) together with a primary return code of
0x0000 (success).

Allow QUERY_BRIDGE_PORTS requests to return the bridge port data
provided by the firmware despite OS_MISMATCH. To make the OS family
mismatch visible to userspace, represent the firmware-reported role
"none" as "none (OS family mismatch)" while preserving the reported
bridge_state.

The behavior for non-QUERY bridge port commands is unchanged; SET
operations continue to return -EPERM when another OS family owns the
bridge port.

This restores readability of bridge_role and bridge_state.

Fixes: 1b05cf6285 ("qeth: Include error message for "OS Mismatch"")
Cc: stable@vger.kernel.org
Suggested-by: Halil Pasic <pasic@linux.ibm.com>
Reviewed-by: Alexandra Winter <wintera@linux.ibm.com>
Signed-off-by: Nagamani PV <nagamani@linux.ibm.com>
Link: https://patch.msgid.link/20260901155344.3561483-1-nagamani@linux.ibm.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Nagamani PV 2026-09-01 17:53:44 +02:00 committed by Jakub Kicinski
parent 38b6be1010
commit 74f27fc864
3 changed files with 30 additions and 6 deletions

View File

@ -13,7 +13,8 @@ extern const struct attribute_group *qeth_l2_attr_groups[];
int qeth_bridgeport_query_ports(struct qeth_card *card,
enum qeth_sbp_roles *role,
enum qeth_sbp_states *state);
enum qeth_sbp_states *state,
bool *os_mismatch);
int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role);
int qeth_bridgeport_an_set(struct qeth_card *card, int enable);

View File

@ -1158,7 +1158,7 @@ static void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
qeth_bridgeport_setrole(card, card->options.sbp.role);
/* Let the callback function refresh the stored role value. */
qeth_bridgeport_query_ports(card, &card->options.sbp.role,
NULL);
NULL, NULL);
}
if (card->options.sbp.hostnotification) {
if (qeth_bridgeport_an_set(card, 1))
@ -1545,6 +1545,7 @@ struct _qeth_sbp_cbctl {
struct {
enum qeth_sbp_roles *role;
enum qeth_sbp_states *state;
bool *os_mismatch;
} qports;
} data;
};
@ -1721,10 +1722,19 @@ static int qeth_bridgeport_query_ports_cb(struct qeth_card *card,
struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;
struct qeth_sbp_port_data *qports;
u16 sbp_rc;
int rc;
QETH_CARD_TEXT(card, 2, "brqprtcb");
rc = qeth_bridgeport_makerc(card, cmd);
sbp_rc = cmd->data.sbp.hdr.return_code;
/* on OS family mismatch, query still returns valid port data;
* treat as success
*/
if (sbp_rc == IPA_RC_SBP_IQD_OS_MISMATCH && !cmd->hdr.return_code)
rc = 0;
else
rc = qeth_bridgeport_makerc(card, cmd);
if (rc)
return rc;
@ -1740,6 +1750,9 @@ static int qeth_bridgeport_query_ports_cb(struct qeth_card *card,
if (cbctl->data.qports.state)
*cbctl->data.qports.state = qports->entry[0].state;
}
if (cbctl->data.qports.os_mismatch)
*cbctl->data.qports.os_mismatch =
(sbp_rc == IPA_RC_SBP_IQD_OS_MISMATCH);
return 0;
}
@ -1748,13 +1761,17 @@ static int qeth_bridgeport_query_ports_cb(struct qeth_card *card,
* @card: qeth_card structure pointer.
* @role: Role of the port: 0-none, 1-primary, 2-secondary.
* @state: State of the port: 0-inactive, 1-standby, 2-active.
* @os_mismatch: if non-NULL, set to true when firmware reports
* OS family mismatch.
*
* Returns negative errno-compatible error indication or 0 on success.
*
* 'role' and 'state' are not updated in case of hardware operation failure.
* 'role', 'state' and 'os_mismatch' are not updated in case of
* hardware operation failure.
*/
int qeth_bridgeport_query_ports(struct qeth_card *card,
enum qeth_sbp_roles *role, enum qeth_sbp_states *state)
enum qeth_sbp_roles *role, enum qeth_sbp_states *state,
bool *os_mismatch)
{
struct qeth_cmd_buffer *iob;
struct _qeth_sbp_cbctl cbctl = {
@ -1762,6 +1779,7 @@ int qeth_bridgeport_query_ports(struct qeth_card *card,
.qports = {
.role = role,
.state = state,
.os_mismatch = os_mismatch,
},
},
};

View File

@ -15,6 +15,7 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev,
{
struct qeth_card *card = dev_get_drvdata(dev);
enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE;
bool os_mismatch = false;
int rc = 0;
char *word;
@ -25,7 +26,7 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev,
if (qeth_card_hw_is_reachable(card) &&
card->options.sbp.supported_funcs)
rc = qeth_bridgeport_query_ports(card,
&card->options.sbp.role, &state);
&card->options.sbp.role, &state, &os_mismatch);
if (!rc) {
if (show_state)
switch (state) {
@ -52,6 +53,10 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev,
if (rc)
QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x",
card->options.sbp.role, state);
else if (!show_state &&
card->options.sbp.role == QETH_SBP_ROLE_NONE &&
os_mismatch)
rc = sysfs_emit(buf, "%s (OS family mismatch)\n", word);
else
rc = sysfs_emit(buf, "%s\n", word);
}