Commit 349d13d5 authored by Hans Wippel's avatar Hans Wippel Committed by David S. Miller

s390/qeth: add VNICC get/set timeout support

HiperSockets allow configuring so called VNIC Characteristics (VNICC)
that influence how the underlying hardware handles packets. For VNICCs,
additional commands for getting and setting timeouts are available.
Currently, the learning VNICC uses these commands.

* 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.

For learning, the timeout specifies the idle period in seconds, after
which the underlying hardware removes a learned MAC address again.

This patch adds support for the IPA commands that are required to get
and set the current timeout values for the learning VNIC characteristic.
Also, it introduces the sysfs interface that allows users to configure
the timeout.
Signed-off-by: default avatarHans Wippel <hwippel@linux.vnet.ibm.com>
Reviewed-by: default avatarJulian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: default avatarJulian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent caa1f0b1
...@@ -188,6 +188,9 @@ struct qeth_vnicc_info { ...@@ -188,6 +188,9 @@ struct qeth_vnicc_info {
u32 cur_chars; u32 cur_chars;
/* supported commands: bitmasks which VNICCs support respective cmd */ /* supported commands: bitmasks which VNICCs support respective cmd */
u32 set_char_sup; u32 set_char_sup;
u32 getset_timeout_sup;
/* timeout value for the learning characteristic */
u32 learning_timeout;
/* characteristics wanted/configured by user */ /* characteristics wanted/configured by user */
u32 wanted_chars; u32 wanted_chars;
/* has user explicitly enabled rx_bcast while online? */ /* has user explicitly enabled rx_bcast while online? */
......
...@@ -562,6 +562,8 @@ struct qeth_ipacmd_diagass { ...@@ -562,6 +562,8 @@ struct qeth_ipacmd_diagass {
#define IPA_VNICC_QUERY_CMDS 0x00000001L #define IPA_VNICC_QUERY_CMDS 0x00000001L
#define IPA_VNICC_ENABLE 0x00000002L #define IPA_VNICC_ENABLE 0x00000002L
#define IPA_VNICC_DISABLE 0x00000004L #define IPA_VNICC_DISABLE 0x00000004L
#define IPA_VNICC_SET_TIMEOUT 0x00000008L
#define IPA_VNICC_GET_TIMEOUT 0x00000010L
/* VNICC flags */ /* VNICC flags */
#define QETH_VNICC_FLOODING 0x80000000 #define QETH_VNICC_FLOODING 0x80000000
...@@ -575,6 +577,8 @@ struct qeth_ipacmd_diagass { ...@@ -575,6 +577,8 @@ struct qeth_ipacmd_diagass {
/* VNICC default values */ /* VNICC default values */
#define QETH_VNICC_ALL 0xff000000 #define QETH_VNICC_ALL 0xff000000
#define QETH_VNICC_DEFAULT QETH_VNICC_RX_BCAST #define QETH_VNICC_DEFAULT QETH_VNICC_RX_BCAST
/* default VNICC timeout in seconds */
#define QETH_VNICC_DEFAULT_TIMEOUT 600
/* VNICC header */ /* VNICC header */
struct qeth_ipacmd_vnicc_hdr { struct qeth_ipacmd_vnicc_hdr {
...@@ -600,6 +604,12 @@ struct qeth_vnicc_set_char { ...@@ -600,6 +604,12 @@ struct qeth_vnicc_set_char {
u32 vnic_char; u32 vnic_char;
}; };
/* get/set timeout for VNIC characteristic */
struct qeth_vnicc_getset_timeout {
u32 vnic_char;
u32 timeout;
};
/* complete VNICC IPA command message */ /* complete VNICC IPA command message */
struct qeth_ipacmd_vnicc { struct qeth_ipacmd_vnicc {
struct qeth_ipacmd_vnicc_hdr hdr; struct qeth_ipacmd_vnicc_hdr hdr;
...@@ -607,6 +617,7 @@ struct qeth_ipacmd_vnicc { ...@@ -607,6 +617,7 @@ struct qeth_ipacmd_vnicc {
union { union {
struct qeth_vnicc_query_cmds query_cmds; struct qeth_vnicc_query_cmds query_cmds;
struct qeth_vnicc_set_char set_char; struct qeth_vnicc_set_char set_char;
struct qeth_vnicc_getset_timeout getset_timeout;
}; };
}; };
......
...@@ -16,6 +16,8 @@ void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); ...@@ -16,6 +16,8 @@ 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_set_state(struct qeth_card *card, u32 vnicc, bool state);
int qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state); int qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state);
int qeth_l2_vnicc_set_timeout(struct qeth_card *card, u32 timeout);
int qeth_l2_vnicc_get_timeout(struct qeth_card *card, u32 *timeout);
bool qeth_l2_vnicc_is_in_use(struct qeth_card *card); bool qeth_l2_vnicc_is_in_use(struct qeth_card *card);
struct qeth_mac { struct qeth_mac {
......
...@@ -35,6 +35,8 @@ static void qeth_bridge_host_event(struct qeth_card *card, ...@@ -35,6 +35,8 @@ static void qeth_bridge_host_event(struct qeth_card *card,
struct qeth_ipa_cmd *cmd); struct qeth_ipa_cmd *cmd);
static void qeth_l2_vnicc_set_defaults(struct qeth_card *card); static void qeth_l2_vnicc_set_defaults(struct qeth_card *card);
static void qeth_l2_vnicc_init(struct qeth_card *card); static void qeth_l2_vnicc_init(struct qeth_card *card);
static bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc,
u32 *timeout);
static int qeth_l2_verify_dev(struct net_device *dev) static int qeth_l2_verify_dev(struct net_device *dev)
{ {
...@@ -2096,9 +2098,13 @@ struct _qeth_l2_vnicc_request_cbctl { ...@@ -2096,9 +2098,13 @@ struct _qeth_l2_vnicc_request_cbctl {
u32 sub_cmd; u32 sub_cmd;
struct { struct {
u32 vnic_char; u32 vnic_char;
u32 timeout;
} param; } param;
struct { struct {
union{
u32 *sup_cmds; u32 *sup_cmds;
u32 *timeout;
};
} result; } result;
}; };
...@@ -2122,6 +2128,9 @@ static int qeth_l2_vnicc_request_cb(struct qeth_card *card, ...@@ -2122,6 +2128,9 @@ static int qeth_l2_vnicc_request_cb(struct qeth_card *card,
if (cbctl->sub_cmd == IPA_VNICC_QUERY_CMDS) if (cbctl->sub_cmd == IPA_VNICC_QUERY_CMDS)
*cbctl->result.sup_cmds = rep->query_cmds.sup_cmds; *cbctl->result.sup_cmds = rep->query_cmds.sup_cmds;
if (cbctl->sub_cmd == IPA_VNICC_GET_TIMEOUT)
*cbctl->result.timeout = rep->getset_timeout.timeout;
return 0; return 0;
} }
...@@ -2162,6 +2171,13 @@ static int qeth_l2_vnicc_request(struct qeth_card *card, ...@@ -2162,6 +2171,13 @@ static int qeth_l2_vnicc_request(struct qeth_card *card,
req->sub_hdr.data_length += sizeof(req->set_char); req->sub_hdr.data_length += sizeof(req->set_char);
req->set_char.vnic_char = cbctl->param.vnic_char; req->set_char.vnic_char = cbctl->param.vnic_char;
break; break;
case IPA_VNICC_SET_TIMEOUT:
req->getset_timeout.timeout = cbctl->param.timeout;
/* fallthrough */
case IPA_VNICC_GET_TIMEOUT:
req->sub_hdr.data_length += sizeof(req->getset_timeout);
req->getset_timeout.vnic_char = cbctl->param.vnic_char;
break;
default: default:
qeth_release_buffer(iob->channel, iob); qeth_release_buffer(iob->channel, iob);
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -2215,6 +2231,24 @@ static int qeth_l2_vnicc_set_char(struct qeth_card *card, u32 vnic_char, ...@@ -2215,6 +2231,24 @@ static int qeth_l2_vnicc_set_char(struct qeth_card *card, u32 vnic_char,
return qeth_l2_vnicc_request(card, &cbctl); return qeth_l2_vnicc_request(card, &cbctl);
} }
/* VNICC get/set timeout for characteristic request */
static int qeth_l2_vnicc_getset_timeout(struct qeth_card *card, u32 vnicc,
u32 cmd, u32 *timeout)
{
struct _qeth_l2_vnicc_request_cbctl cbctl;
/* prepare callback control */
cbctl.sub_cmd = cmd;
cbctl.param.vnic_char = vnicc;
if (cmd == IPA_VNICC_SET_TIMEOUT)
cbctl.param.timeout = *timeout;
if (cmd == IPA_VNICC_GET_TIMEOUT)
cbctl.result.timeout = timeout;
QETH_CARD_TEXT(card, 2, "vniccgst");
return qeth_l2_vnicc_request(card, &cbctl);
}
/* set current VNICC flag state; called from sysfs store function */ /* 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 qeth_l2_vnicc_set_state(struct qeth_card *card, u32 vnicc, bool state)
{ {
...@@ -2258,8 +2292,14 @@ int qeth_l2_vnicc_set_state(struct qeth_card *card, u32 vnicc, bool state) ...@@ -2258,8 +2292,14 @@ int qeth_l2_vnicc_set_state(struct qeth_card *card, u32 vnicc, bool state)
if (rc) if (rc)
card->options.vnicc.wanted_chars = card->options.vnicc.wanted_chars =
card->options.vnicc.cur_chars; card->options.vnicc.cur_chars;
else if (state && vnicc == QETH_VNICC_RX_BCAST) else {
/* successful online VNICC change; handle special cases */
if (state && vnicc == QETH_VNICC_RX_BCAST)
card->options.vnicc.rx_bcast_enabled = true; card->options.vnicc.rx_bcast_enabled = true;
if (!state && vnicc == QETH_VNICC_LEARNING)
qeth_l2_vnicc_recover_timeout(card, vnicc,
&card->options.vnicc.learning_timeout);
}
return rc; return rc;
} }
...@@ -2287,6 +2327,70 @@ int qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state) ...@@ -2287,6 +2327,70 @@ int qeth_l2_vnicc_get_state(struct qeth_card *card, u32 vnicc, bool *state)
return rc; return rc;
} }
/* set VNICC timeout; called from sysfs store function. Currently, only learning
* supports timeout
*/
int qeth_l2_vnicc_set_timeout(struct qeth_card *card, u32 timeout)
{
int rc = 0;
QETH_CARD_TEXT(card, 2, "vniccsto");
/* do not change anything if BridgePort is enabled */
if (qeth_bridgeport_is_in_use(card))
return -EBUSY;
/* check if characteristic and set_timeout are supported */
if (!(card->options.vnicc.sup_chars & QETH_VNICC_LEARNING) ||
!(card->options.vnicc.getset_timeout_sup & QETH_VNICC_LEARNING))
return -EOPNOTSUPP;
/* do we need to do anything? */
if (card->options.vnicc.learning_timeout == timeout)
return rc;
/* if card is not ready, simply store the value internally and return */
if (!qeth_card_hw_is_reachable(card)) {
card->options.vnicc.learning_timeout = timeout;
return rc;
}
/* send timeout value to card; if successful, store value internally */
rc = qeth_l2_vnicc_getset_timeout(card, QETH_VNICC_LEARNING,
IPA_VNICC_SET_TIMEOUT, &timeout);
if (!rc)
card->options.vnicc.learning_timeout = timeout;
return rc;
}
/* get current VNICC timeout; called from sysfs show function. Currently, only
* learning supports timeout
*/
int qeth_l2_vnicc_get_timeout(struct qeth_card *card, u32 *timeout)
{
int rc = 0;
QETH_CARD_TEXT(card, 2, "vniccgto");
/* do not get anything if BridgePort is enabled */
if (qeth_bridgeport_is_in_use(card))
return -EBUSY;
/* check if characteristic and get_timeout are supported */
if (!(card->options.vnicc.sup_chars & QETH_VNICC_LEARNING) ||
!(card->options.vnicc.getset_timeout_sup & QETH_VNICC_LEARNING))
return -EOPNOTSUPP;
/* if card is ready, get timeout. Otherwise, just return stored value */
*timeout = card->options.vnicc.learning_timeout;
if (qeth_card_hw_is_reachable(card))
rc = qeth_l2_vnicc_getset_timeout(card, QETH_VNICC_LEARNING,
IPA_VNICC_GET_TIMEOUT,
timeout);
return rc;
}
/* check if VNICC is currently enabled */ /* check if VNICC is currently enabled */
bool qeth_l2_vnicc_is_in_use(struct qeth_card *card) bool qeth_l2_vnicc_is_in_use(struct qeth_card *card)
{ {
...@@ -2304,6 +2408,19 @@ bool qeth_l2_vnicc_is_in_use(struct qeth_card *card) ...@@ -2304,6 +2408,19 @@ bool qeth_l2_vnicc_is_in_use(struct qeth_card *card)
return true; return true;
} }
/* recover user timeout setting */
static bool qeth_l2_vnicc_recover_timeout(struct qeth_card *card, u32 vnicc,
u32 *timeout)
{
if (card->options.vnicc.sup_chars & vnicc &&
card->options.vnicc.getset_timeout_sup & vnicc &&
!qeth_l2_vnicc_getset_timeout(card, vnicc, IPA_VNICC_SET_TIMEOUT,
timeout))
return false;
*timeout = QETH_VNICC_DEFAULT_TIMEOUT;
return true;
}
/* recover user characteristic setting */ /* recover user characteristic setting */
static bool qeth_l2_vnicc_recover_char(struct qeth_card *card, u32 vnicc, static bool qeth_l2_vnicc_recover_char(struct qeth_card *card, u32 vnicc,
bool enable) bool enable)
...@@ -2322,6 +2439,7 @@ static bool qeth_l2_vnicc_recover_char(struct qeth_card *card, u32 vnicc, ...@@ -2322,6 +2439,7 @@ static bool qeth_l2_vnicc_recover_char(struct qeth_card *card, u32 vnicc,
/* (re-)initialize VNICC */ /* (re-)initialize VNICC */
static void qeth_l2_vnicc_init(struct qeth_card *card) static void qeth_l2_vnicc_init(struct qeth_card *card)
{ {
u32 *timeout = &card->options.vnicc.learning_timeout;
unsigned int chars_len, i; unsigned int chars_len, i;
unsigned long chars_tmp; unsigned long chars_tmp;
u32 sup_cmds, vnicc; u32 sup_cmds, vnicc;
...@@ -2332,7 +2450,8 @@ static void qeth_l2_vnicc_init(struct qeth_card *card) ...@@ -2332,7 +2450,8 @@ static void qeth_l2_vnicc_init(struct qeth_card *card)
card->options.vnicc.rx_bcast_enabled = 0; card->options.vnicc.rx_bcast_enabled = 0;
/* initial query and storage of VNIC characteristics */ /* initial query and storage of VNIC characteristics */
if (qeth_l2_vnicc_query_chars(card)) { if (qeth_l2_vnicc_query_chars(card)) {
if (card->options.vnicc.wanted_chars != QETH_VNICC_DEFAULT) if (card->options.vnicc.wanted_chars != QETH_VNICC_DEFAULT ||
*timeout != QETH_VNICC_DEFAULT_TIMEOUT)
dev_err(&card->gdev->dev, "Configuring the VNIC characteristics failed\n"); dev_err(&card->gdev->dev, "Configuring the VNIC characteristics failed\n");
/* fail quietly if user didn't change the default config */ /* fail quietly if user didn't change the default config */
card->options.vnicc.sup_chars = 0; card->options.vnicc.sup_chars = 0;
...@@ -2346,12 +2465,16 @@ static void qeth_l2_vnicc_init(struct qeth_card *card) ...@@ -2346,12 +2465,16 @@ static void qeth_l2_vnicc_init(struct qeth_card *card)
for_each_set_bit(i, &chars_tmp, chars_len) { for_each_set_bit(i, &chars_tmp, chars_len) {
vnicc = BIT(i); vnicc = BIT(i);
qeth_l2_vnicc_query_cmds(card, vnicc, &sup_cmds); qeth_l2_vnicc_query_cmds(card, vnicc, &sup_cmds);
if (!(sup_cmds & IPA_VNICC_SET_TIMEOUT) ||
!(sup_cmds & IPA_VNICC_GET_TIMEOUT))
card->options.vnicc.getset_timeout_sup &= ~vnicc;
if (!(sup_cmds & IPA_VNICC_ENABLE) || if (!(sup_cmds & IPA_VNICC_ENABLE) ||
!(sup_cmds & IPA_VNICC_DISABLE)) !(sup_cmds & IPA_VNICC_DISABLE))
card->options.vnicc.set_char_sup &= ~vnicc; card->options.vnicc.set_char_sup &= ~vnicc;
} }
/* enforce assumed default values and recover settings, if changed */ /* enforce assumed default values and recover settings, if changed */
error = false; error = qeth_l2_vnicc_recover_timeout(card, QETH_VNICC_LEARNING,
timeout);
chars_tmp = card->options.vnicc.wanted_chars ^ QETH_VNICC_DEFAULT; chars_tmp = card->options.vnicc.wanted_chars ^ QETH_VNICC_DEFAULT;
chars_tmp |= QETH_VNICC_BRIDGE_INVISIBLE; chars_tmp |= QETH_VNICC_BRIDGE_INVISIBLE;
chars_len = sizeof(card->options.vnicc.wanted_chars) * BITS_PER_BYTE; chars_len = sizeof(card->options.vnicc.wanted_chars) * BITS_PER_BYTE;
...@@ -2370,8 +2493,10 @@ static void qeth_l2_vnicc_set_defaults(struct qeth_card *card) ...@@ -2370,8 +2493,10 @@ static void qeth_l2_vnicc_set_defaults(struct qeth_card *card)
/* characteristics values */ /* characteristics values */
card->options.vnicc.sup_chars = QETH_VNICC_ALL; card->options.vnicc.sup_chars = QETH_VNICC_ALL;
card->options.vnicc.cur_chars = QETH_VNICC_DEFAULT; card->options.vnicc.cur_chars = QETH_VNICC_DEFAULT;
card->options.vnicc.learning_timeout = QETH_VNICC_DEFAULT_TIMEOUT;
/* supported commands */ /* supported commands */
card->options.vnicc.set_char_sup = QETH_VNICC_ALL; card->options.vnicc.set_char_sup = QETH_VNICC_ALL;
card->options.vnicc.getset_timeout_sup = QETH_VNICC_LEARNING;
/* settings wanted by users */ /* settings wanted by users */
card->options.vnicc.wanted_chars = QETH_VNICC_DEFAULT; card->options.vnicc.wanted_chars = QETH_VNICC_DEFAULT;
} }
......
...@@ -309,6 +309,47 @@ static u32 qeth_l2_vnicc_sysfs_attr_to_char(const char *attr_name) ...@@ -309,6 +309,47 @@ static u32 qeth_l2_vnicc_sysfs_attr_to_char(const char *attr_name)
return 0; return 0;
} }
/* get current timeout setting */
static ssize_t qeth_vnicc_timeout_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
u32 timeout;
int rc;
if (!card)
return -EINVAL;
rc = qeth_l2_vnicc_get_timeout(card, &timeout);
if (rc == -EBUSY)
return sprintf(buf, "n/a (BridgePort)\n");
if (rc == -EOPNOTSUPP)
return sprintf(buf, "n/a\n");
return rc ? rc : sprintf(buf, "%d\n", timeout);
}
/* change timeout setting */
static ssize_t qeth_vnicc_timeout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
u32 timeout;
int rc;
if (!card)
return -EINVAL;
rc = kstrtou32(buf, 10, &timeout);
if (rc)
return rc;
mutex_lock(&card->conf_mutex);
rc = qeth_l2_vnicc_set_timeout(card, timeout);
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
/* get current setting of characteristic */ /* get current setting of characteristic */
static ssize_t qeth_vnicc_char_show(struct device *dev, static ssize_t qeth_vnicc_char_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
...@@ -359,6 +400,8 @@ static DEVICE_ATTR(flooding, 0644, qeth_vnicc_char_show, qeth_vnicc_char_store); ...@@ -359,6 +400,8 @@ static DEVICE_ATTR(flooding, 0644, qeth_vnicc_char_show, qeth_vnicc_char_store);
static DEVICE_ATTR(mcast_flooding, 0644, qeth_vnicc_char_show, static DEVICE_ATTR(mcast_flooding, 0644, qeth_vnicc_char_show,
qeth_vnicc_char_store); qeth_vnicc_char_store);
static DEVICE_ATTR(learning, 0644, qeth_vnicc_char_show, qeth_vnicc_char_store); static DEVICE_ATTR(learning, 0644, qeth_vnicc_char_show, qeth_vnicc_char_store);
static DEVICE_ATTR(learning_timeout, 0644, qeth_vnicc_timeout_show,
qeth_vnicc_timeout_store);
static DEVICE_ATTR(takeover_setvmac, 0644, qeth_vnicc_char_show, static DEVICE_ATTR(takeover_setvmac, 0644, qeth_vnicc_char_show,
qeth_vnicc_char_store); qeth_vnicc_char_store);
static DEVICE_ATTR(takeover_learning, 0644, qeth_vnicc_char_show, static DEVICE_ATTR(takeover_learning, 0644, qeth_vnicc_char_show,
...@@ -371,6 +414,7 @@ static struct attribute *qeth_l2_vnicc_attrs[] = { ...@@ -371,6 +414,7 @@ static struct attribute *qeth_l2_vnicc_attrs[] = {
&dev_attr_flooding.attr, &dev_attr_flooding.attr,
&dev_attr_mcast_flooding.attr, &dev_attr_mcast_flooding.attr,
&dev_attr_learning.attr, &dev_attr_learning.attr,
&dev_attr_learning_timeout.attr,
&dev_attr_takeover_setvmac.attr, &dev_attr_takeover_setvmac.attr,
&dev_attr_takeover_learning.attr, &dev_attr_takeover_learning.attr,
&dev_attr_bridge_invisible.attr, &dev_attr_bridge_invisible.attr,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment