Skip to content

Commit

Permalink
s390/qeth: add VNICC enable/disable support
Browse files Browse the repository at this point in the history
HiperSocket devices allow enabling and disabling so called VNIC
Characteristics (VNICC) that influence how the underlying hardware
handles packets. These VNICCs are:

* Flooding VNICC: Flooding allows specifying if packets to unknown
  destination MAC addresses are received by the qeth device.

* Multicast flooding VNICC: Multicast flooding allows specifying if
  packets to multicast MAC addresses are received by the qeth device.

* Learning VNICC: If learning is enabled on a qeth device, the device
  learns the source MAC addresses of outgoing packets and incoming
  packets to those learned MAC addresses are received.

* Takeover setvmac VNICC: If takeover setvmac is configured on a qeth
  device, the MAC address of this device can be configured on a
  different qeth device with the setvmac IPA command.

* Takeover by learning VNICC: If takeover learning is enabled on a qeth
  device, the MAC address of this device can be learned (learning VNICC)
  on a different qeth device.

* BridgePort invisible VNICC: If BridgePort invisible is enabled on a
  qeth device, (1) packets from this device are not sent to a BridgePort
  enabled qeth device and (2) packets coming from a BridgePort enabled
  qeth device are not received by this device.

* Receive broadcast VNICC: Receive broadcast allows configuring if a
  qeth device receives packets with the broadcast destination MAC
  address.

This patch adds support for the IPA commands that are required to enable
and disable these VNIC characteristics on qeth devices. As a
prerequisite, it also adds the query commands IPA command.

The query commands IPA command allows requesting the supported commands
for each characteristic from the underlying hardware.

Additionally, this patch provides users with a sysfs user interface to
enable/disable the VNICCs mentioned above.

Signed-off-by: Hans Wippel <hwippel@linux.vnet.ibm.com>
Reviewed-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Hans Wippel authored and David S. Miller committed Sep 18, 2017
1 parent a45b3fa commit caa1f0b
Show file tree
Hide file tree
Showing 5 changed files with 392 additions and 14 deletions.
6 changes: 6 additions & 0 deletions drivers/s390/net/qeth_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ struct qeth_vnicc_info {
/* supported/currently configured VNICCs; updated in IPA exchanges */
u32 sup_chars;
u32 cur_chars;
/* supported commands: bitmasks which VNICCs support respective cmd */
u32 set_char_sup;
/* characteristics wanted/configured by user */
u32 wanted_chars;
/* has user explicitly enabled rx_bcast while online? */
bool rx_bcast_enabled;
};

static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa,
Expand Down
31 changes: 31 additions & 0 deletions drivers/s390/net/qeth_core_mpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,22 @@ struct qeth_ipacmd_diagass {
/* VNIC Characteristics IPA Command: *****************************************/
/* IPA commands/sub commands for VNICC */
#define IPA_VNICC_QUERY_CHARS 0x00000000L
#define IPA_VNICC_QUERY_CMDS 0x00000001L
#define IPA_VNICC_ENABLE 0x00000002L
#define IPA_VNICC_DISABLE 0x00000004L

/* VNICC flags */
#define QETH_VNICC_FLOODING 0x80000000
#define QETH_VNICC_MCAST_FLOODING 0x40000000
#define QETH_VNICC_LEARNING 0x20000000
#define QETH_VNICC_TAKEOVER_SETVMAC 0x10000000
#define QETH_VNICC_TAKEOVER_LEARNING 0x08000000
#define QETH_VNICC_BRIDGE_INVISIBLE 0x04000000
#define QETH_VNICC_RX_BCAST 0x02000000

/* VNICC default values */
#define QETH_VNICC_ALL 0xff000000
#define QETH_VNICC_DEFAULT QETH_VNICC_RX_BCAST

/* VNICC header */
struct qeth_ipacmd_vnicc_hdr {
Expand All @@ -573,10 +589,25 @@ struct qeth_vnicc_sub_hdr {
u32 sub_command;
};

/* query supported commands for VNIC characteristic */
struct qeth_vnicc_query_cmds {
u32 vnic_char;
u32 sup_cmds;
};

/* enable/disable VNIC characteristic */
struct qeth_vnicc_set_char {
u32 vnic_char;
};

/* complete VNICC IPA command message */
struct qeth_ipacmd_vnicc {
struct qeth_ipacmd_vnicc_hdr hdr;
struct qeth_vnicc_sub_hdr sub_hdr;
union {
struct qeth_vnicc_query_cmds query_cmds;
struct qeth_vnicc_set_char set_char;
};
};

/* SETBRIDGEPORT IPA Command: *********************************************/
Expand Down
4 changes: 4 additions & 0 deletions drivers/s390/net/qeth_l2.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ int qeth_l2_create_device_attributes(struct device *);
void qeth_l2_remove_device_attributes(struct device *);
void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card);

int qeth_l2_vnicc_set_state(struct qeth_card *card, u32 vnicc, bool state);
int qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state);
bool qeth_l2_vnicc_is_in_use(struct qeth_card *card);

struct qeth_mac {
u8 mac_addr[OSA_ADDR_LEN];
u8 is_uc:1;
Expand Down
207 changes: 207 additions & 0 deletions drivers/s390/net/qeth_l2_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ static void qeth_bridge_state_change(struct qeth_card *card,
struct qeth_ipa_cmd *cmd);
static void qeth_bridge_host_event(struct qeth_card *card,
struct qeth_ipa_cmd *cmd);
static void qeth_l2_vnicc_set_defaults(struct qeth_card *card);
static void qeth_l2_vnicc_init(struct qeth_card *card);

static int qeth_l2_verify_dev(struct net_device *dev)
Expand Down Expand Up @@ -920,6 +921,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
hash_init(card->mac_htable);
card->options.layer2 = 1;
card->info.hwtrap = 0;
qeth_l2_vnicc_set_defaults(card);
return 0;
}

Expand Down Expand Up @@ -2049,6 +2051,12 @@ int qeth_bridgeport_an_set(struct qeth_card *card, int enable)
}
EXPORT_SYMBOL_GPL(qeth_bridgeport_an_set);

static bool qeth_bridgeport_is_in_use(struct qeth_card *card)
{
return (card->options.sbp.role || card->options.sbp.reflect_promisc ||
card->options.sbp.hostnotification);
}

/* VNIC Characteristics support */

/* handle VNICC IPA command return codes; convert to error codes */
Expand Down Expand Up @@ -2086,13 +2094,21 @@ static int qeth_l2_vnicc_makerc(struct qeth_card *card, int ipa_rc)
/* generic VNICC request call back control */
struct _qeth_l2_vnicc_request_cbctl {
u32 sub_cmd;
struct {
u32 vnic_char;
} param;
struct {
u32 *sup_cmds;
} result;
};

/* generic VNICC request call back */
static int qeth_l2_vnicc_request_cb(struct qeth_card *card,
struct qeth_reply *reply,
unsigned long data)
{
struct _qeth_l2_vnicc_request_cbctl *cbctl =
(struct _qeth_l2_vnicc_request_cbctl *) reply->param;
struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
struct qeth_ipacmd_vnicc *rep = &cmd->data.vnicc;

Expand All @@ -2103,6 +2119,9 @@ static int qeth_l2_vnicc_request_cb(struct qeth_card *card,
card->options.vnicc.sup_chars = rep->hdr.sup;
card->options.vnicc.cur_chars = rep->hdr.cur;

if (cbctl->sub_cmd == IPA_VNICC_QUERY_CMDS)
*cbctl->result.sup_cmds = rep->query_cmds.sup_cmds;

return 0;
}

Expand Down Expand Up @@ -2134,6 +2153,15 @@ static int qeth_l2_vnicc_request(struct qeth_card *card,
switch (cbctl->sub_cmd) {
case IPA_VNICC_QUERY_CHARS:
break;
case IPA_VNICC_QUERY_CMDS:
req->sub_hdr.data_length += sizeof(req->query_cmds);
req->query_cmds.vnic_char = cbctl->param.vnic_char;
break;
case IPA_VNICC_ENABLE:
case IPA_VNICC_DISABLE:
req->sub_hdr.data_length += sizeof(req->set_char);
req->set_char.vnic_char = cbctl->param.vnic_char;
break;
default:
qeth_release_buffer(iob->channel, iob);
return -EOPNOTSUPP;
Expand All @@ -2158,15 +2186,194 @@ static int qeth_l2_vnicc_query_chars(struct qeth_card *card)
return qeth_l2_vnicc_request(card, &cbctl);
}

/* VNICC query sub commands request */
static int qeth_l2_vnicc_query_cmds(struct qeth_card *card, u32 vnic_char,
u32 *sup_cmds)
{
struct _qeth_l2_vnicc_request_cbctl cbctl;

/* prepare callback control */
cbctl.sub_cmd = IPA_VNICC_QUERY_CMDS;
cbctl.param.vnic_char = vnic_char;
cbctl.result.sup_cmds = sup_cmds;

QETH_CARD_TEXT(card, 2, "vniccqcm");
return qeth_l2_vnicc_request(card, &cbctl);
}

/* VNICC enable/disable characteristic request */
static int qeth_l2_vnicc_set_char(struct qeth_card *card, u32 vnic_char,
u32 cmd)
{
struct _qeth_l2_vnicc_request_cbctl cbctl;

/* prepare callback control */
cbctl.sub_cmd = cmd;
cbctl.param.vnic_char = vnic_char;

QETH_CARD_TEXT(card, 2, "vniccedc");
return qeth_l2_vnicc_request(card, &cbctl);
}

/* set current VNICC flag state; called from sysfs store function */
int qeth_l2_vnicc_set_state(struct qeth_card *card, u32 vnicc, bool state)
{
int rc = 0;
u32 cmd;

QETH_CARD_TEXT(card, 2, "vniccsch");

/* do not change anything if BridgePort is enabled */
if (qeth_bridgeport_is_in_use(card))
return -EBUSY;

/* check if characteristic and enable/disable are supported */
if (!(card->options.vnicc.sup_chars & vnicc) ||
!(card->options.vnicc.set_char_sup & vnicc))
return -EOPNOTSUPP;

/* set enable/disable command and store wanted characteristic */
if (state) {
cmd = IPA_VNICC_ENABLE;
card->options.vnicc.wanted_chars |= vnicc;
} else {
cmd = IPA_VNICC_DISABLE;
card->options.vnicc.wanted_chars &= ~vnicc;
}

/* do we need to do anything? */
if (card->options.vnicc.cur_chars == card->options.vnicc.wanted_chars)
return rc;

/* if card is not ready, simply stop here */
if (!qeth_card_hw_is_reachable(card)) {
if (state)
card->options.vnicc.cur_chars |= vnicc;
else
card->options.vnicc.cur_chars &= ~vnicc;
return rc;
}

rc = qeth_l2_vnicc_set_char(card, vnicc, cmd);
if (rc)
card->options.vnicc.wanted_chars =
card->options.vnicc.cur_chars;
else if (state && vnicc == QETH_VNICC_RX_BCAST)
card->options.vnicc.rx_bcast_enabled = true;

return rc;
}

/* get current VNICC flag state; called from sysfs show function */
int qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state)
{
int rc = 0;

QETH_CARD_TEXT(card, 2, "vniccgch");

/* do not get anything if BridgePort is enabled */
if (qeth_bridgeport_is_in_use(card))
return -EBUSY;

/* check if characteristic is supported */
if (!(card->options.vnicc.sup_chars & vnicc))
return -EOPNOTSUPP;

/* if card is ready, query current VNICC state */
if (qeth_card_hw_is_reachable(card))
rc = qeth_l2_vnicc_query_chars(card);

*state = (card->options.vnicc.cur_chars & vnicc) ? true : false;
return rc;
}

/* check if VNICC is currently enabled */
bool qeth_l2_vnicc_is_in_use(struct qeth_card *card)
{
/* if everything is turned off, VNICC is not active */
if (!card->options.vnicc.cur_chars)
return false;
/* default values are only OK if rx_bcast was not enabled by user
* or the card is offline.
*/
if (card->options.vnicc.cur_chars == QETH_VNICC_DEFAULT) {
if (!card->options.vnicc.rx_bcast_enabled ||
!qeth_card_hw_is_reachable(card))
return false;
}
return true;
}

/* recover user characteristic setting */
static bool qeth_l2_vnicc_recover_char(struct qeth_card *card, u32 vnicc,
bool enable)
{
u32 cmd = enable ? IPA_VNICC_ENABLE : IPA_VNICC_DISABLE;

if (card->options.vnicc.sup_chars & vnicc &&
card->options.vnicc.set_char_sup & vnicc &&
!qeth_l2_vnicc_set_char(card, vnicc, cmd))
return false;
card->options.vnicc.wanted_chars &= ~vnicc;
card->options.vnicc.wanted_chars |= QETH_VNICC_DEFAULT & vnicc;
return true;
}

/* (re-)initialize VNICC */
static void qeth_l2_vnicc_init(struct qeth_card *card)
{
unsigned int chars_len, i;
unsigned long chars_tmp;
u32 sup_cmds, vnicc;
bool enable, error;

QETH_CARD_TEXT(card, 2, "vniccini");
/* reset rx_bcast */
card->options.vnicc.rx_bcast_enabled = 0;
/* initial query and storage of VNIC characteristics */
if (qeth_l2_vnicc_query_chars(card)) {
if (card->options.vnicc.wanted_chars != QETH_VNICC_DEFAULT)
dev_err(&card->gdev->dev, "Configuring the VNIC characteristics failed\n");
/* fail quietly if user didn't change the default config */
card->options.vnicc.sup_chars = 0;
card->options.vnicc.cur_chars = 0;
card->options.vnicc.wanted_chars = QETH_VNICC_DEFAULT;
return;
}
/* get supported commands for each supported characteristic */
chars_tmp = card->options.vnicc.sup_chars;
chars_len = sizeof(card->options.vnicc.sup_chars) * BITS_PER_BYTE;
for_each_set_bit(i, &chars_tmp, chars_len) {
vnicc = BIT(i);
qeth_l2_vnicc_query_cmds(card, vnicc, &sup_cmds);
if (!(sup_cmds & IPA_VNICC_ENABLE) ||
!(sup_cmds & IPA_VNICC_DISABLE))
card->options.vnicc.set_char_sup &= ~vnicc;
}
/* enforce assumed default values and recover settings, if changed */
error = false;
chars_tmp = card->options.vnicc.wanted_chars ^ QETH_VNICC_DEFAULT;
chars_tmp |= QETH_VNICC_BRIDGE_INVISIBLE;
chars_len = sizeof(card->options.vnicc.wanted_chars) * BITS_PER_BYTE;
for_each_set_bit(i, &chars_tmp, chars_len) {
vnicc = BIT(i);
enable = card->options.vnicc.wanted_chars & vnicc;
error |= qeth_l2_vnicc_recover_char(card, vnicc, enable);
}
if (error)
dev_err(&card->gdev->dev, "Configuring the VNIC characteristics failed\n");
}

/* configure default values of VNIC characteristics */
static void qeth_l2_vnicc_set_defaults(struct qeth_card *card)
{
/* characteristics values */
card->options.vnicc.sup_chars = QETH_VNICC_ALL;
card->options.vnicc.cur_chars = QETH_VNICC_DEFAULT;
/* supported commands */
card->options.vnicc.set_char_sup = QETH_VNICC_ALL;
/* settings wanted by users */
card->options.vnicc.wanted_chars = QETH_VNICC_DEFAULT;
}

module_init(qeth_l2_init);
Expand Down
Loading

0 comments on commit caa1f0b

Please sign in to comment.