2017-11-15 00:38:04 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2008-02-15 15:19:42 +07:00
|
|
|
/*
|
2009-06-16 15:30:31 +07:00
|
|
|
* Copyright IBM Corp. 2007, 2009
|
2008-02-15 15:19:42 +07:00
|
|
|
* Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
|
|
|
|
* Frank Pavlic <fpavlic@de.ibm.com>,
|
|
|
|
* Thomas Spatzier <tspat@de.ibm.com>,
|
|
|
|
* Frank Blaschka <frank.blaschka@de.ibm.com>
|
|
|
|
*/
|
|
|
|
|
2008-12-25 19:39:49 +07:00
|
|
|
#define KMSG_COMPONENT "qeth"
|
|
|
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/moduleparam.h>
|
2011-07-20 11:54:41 +07:00
|
|
|
#include <linux/bitops.h>
|
2008-02-15 15:19:42 +07:00
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/ip.h>
|
2017-12-21 02:11:04 +07:00
|
|
|
#include <linux/in.h>
|
2009-03-25 03:57:16 +07:00
|
|
|
#include <linux/ipv6.h>
|
2008-02-15 15:19:42 +07:00
|
|
|
#include <linux/inetdevice.h>
|
|
|
|
#include <linux/igmp.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 15:04:11 +07:00
|
|
|
#include <linux/slab.h>
|
2017-12-21 02:11:07 +07:00
|
|
|
#include <linux/if_ether.h>
|
2011-07-20 11:54:41 +07:00
|
|
|
#include <linux/if_vlan.h>
|
2017-12-21 02:11:07 +07:00
|
|
|
#include <linux/skbuff.h>
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
#include <net/ip.h>
|
|
|
|
#include <net/arp.h>
|
2012-02-01 17:49:17 +07:00
|
|
|
#include <net/route.h>
|
2017-12-21 02:11:04 +07:00
|
|
|
#include <net/ipv6.h>
|
2017-12-21 02:11:07 +07:00
|
|
|
#include <net/ip6_route.h>
|
2011-08-08 08:33:59 +07:00
|
|
|
#include <net/iucv/af_iucv.h>
|
2016-06-16 21:18:58 +07:00
|
|
|
#include <linux/hashtable.h>
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
#include "qeth_l3.h"
|
|
|
|
|
|
|
|
static int qeth_l3_register_addr_entry(struct qeth_card *,
|
|
|
|
struct qeth_ipaddr *);
|
|
|
|
static int qeth_l3_deregister_addr_entry(struct qeth_card *,
|
|
|
|
struct qeth_ipaddr *);
|
|
|
|
|
2019-12-18 23:34:44 +07:00
|
|
|
int qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const u8 *addr,
|
|
|
|
char *buf)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
|
|
|
if (proto == QETH_PROT_IPV4)
|
2019-12-18 23:34:44 +07:00
|
|
|
return sprintf(buf, "%pI4", addr);
|
|
|
|
else
|
|
|
|
return sprintf(buf, "%pI6", addr);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2018-02-28 00:58:16 +07:00
|
|
|
static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card,
|
|
|
|
struct qeth_ipaddr *query)
|
|
|
|
{
|
2019-10-31 19:42:20 +07:00
|
|
|
u32 key = qeth_l3_ipaddr_hash(query);
|
2018-02-28 00:58:16 +07:00
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
|
|
|
|
if (query->is_multicast) {
|
|
|
|
hash_for_each_possible(card->ip_mc_htable, addr, hnode, key)
|
|
|
|
if (qeth_l3_addr_match_ip(addr, query))
|
|
|
|
return addr;
|
|
|
|
} else {
|
|
|
|
hash_for_each_possible(card->ip_htable, addr, hnode, key)
|
|
|
|
if (qeth_l3_addr_match_ip(addr, query))
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
static void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
u8 octet;
|
|
|
|
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
|
|
octet = addr[i];
|
|
|
|
for (j = 7; j >= 0; --j) {
|
|
|
|
bits[i*8 + j] = octet & 1;
|
|
|
|
octet >>= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-14 00:56:32 +07:00
|
|
|
static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
|
|
|
|
struct qeth_ipaddr *addr)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
|
|
|
struct qeth_ipato_entry *ipatoe;
|
|
|
|
u8 addr_bits[128] = {0, };
|
|
|
|
u8 ipatoe_bits[128] = {0, };
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (!card->ipato.enabled)
|
2018-08-09 19:48:04 +07:00
|
|
|
return false;
|
2017-12-14 00:56:30 +07:00
|
|
|
if (addr->type != QETH_IP_TYPE_NORMAL)
|
2018-08-09 19:48:04 +07:00
|
|
|
return false;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
qeth_l3_convert_addr_to_bits((u8 *) &addr->u, addr_bits,
|
|
|
|
(addr->proto == QETH_PROT_IPV4)? 4:16);
|
|
|
|
list_for_each_entry(ipatoe, &card->ipato.entries, entry) {
|
|
|
|
if (addr->proto != ipatoe->proto)
|
|
|
|
continue;
|
|
|
|
qeth_l3_convert_addr_to_bits(ipatoe->addr, ipatoe_bits,
|
|
|
|
(ipatoe->proto == QETH_PROT_IPV4) ?
|
|
|
|
4 : 16);
|
|
|
|
if (addr->proto == QETH_PROT_IPV4)
|
|
|
|
rc = !memcmp(addr_bits, ipatoe_bits,
|
|
|
|
min(32, ipatoe->mask_bits));
|
|
|
|
else
|
|
|
|
rc = !memcmp(addr_bits, ipatoe_bits,
|
|
|
|
min(128, ipatoe->mask_bits));
|
|
|
|
if (rc)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* invert? */
|
|
|
|
if ((addr->proto == QETH_PROT_IPV4) && card->ipato.invert4)
|
|
|
|
rc = !rc;
|
|
|
|
else if ((addr->proto == QETH_PROT_IPV6) && card->ipato.invert6)
|
|
|
|
rc = !rc;
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-03-10 00:13:02 +07:00
|
|
|
static int qeth_l3_delete_ip(struct qeth_card *card,
|
|
|
|
struct qeth_ipaddr *tmp_addr)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
|
|
|
int rc = 0;
|
2016-06-16 21:18:58 +07:00
|
|
|
struct qeth_ipaddr *addr;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2018-03-10 00:13:02 +07:00
|
|
|
if (tmp_addr->type == QETH_IP_TYPE_RXIP)
|
|
|
|
QETH_CARD_TEXT(card, 2, "delrxip");
|
|
|
|
else if (tmp_addr->type == QETH_IP_TYPE_VIPA)
|
|
|
|
QETH_CARD_TEXT(card, 2, "delvipa");
|
|
|
|
else
|
|
|
|
QETH_CARD_TEXT(card, 2, "delip");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2016-06-16 21:18:58 +07:00
|
|
|
if (tmp_addr->proto == QETH_PROT_IPV4)
|
|
|
|
QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4);
|
2008-02-15 15:19:42 +07:00
|
|
|
else {
|
2016-06-16 21:18:58 +07:00
|
|
|
QETH_CARD_HEX(card, 4, &tmp_addr->u.a6.addr, 8);
|
|
|
|
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2018-02-28 00:58:16 +07:00
|
|
|
addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
|
|
|
|
if (!addr || !qeth_l3_addr_match_all(addr, tmp_addr))
|
2016-06-16 21:18:58 +07:00
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
addr->ref_counter--;
|
2018-02-28 00:58:15 +07:00
|
|
|
if (addr->type == QETH_IP_TYPE_NORMAL && addr->ref_counter > 0)
|
2016-06-16 21:18:58 +07:00
|
|
|
return rc;
|
|
|
|
|
2018-02-28 00:58:13 +07:00
|
|
|
if (qeth_card_hw_is_reachable(card))
|
|
|
|
rc = qeth_l3_deregister_addr_entry(card, addr);
|
2016-06-16 21:18:58 +07:00
|
|
|
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-03-10 00:13:02 +07:00
|
|
|
static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
|
|
|
int rc = 0;
|
2016-06-16 21:18:58 +07:00
|
|
|
struct qeth_ipaddr *addr;
|
2018-02-28 00:58:16 +07:00
|
|
|
char buf[40];
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2018-03-10 00:13:02 +07:00
|
|
|
if (tmp_addr->type == QETH_IP_TYPE_RXIP)
|
|
|
|
QETH_CARD_TEXT(card, 2, "addrxip");
|
|
|
|
else if (tmp_addr->type == QETH_IP_TYPE_VIPA)
|
|
|
|
QETH_CARD_TEXT(card, 2, "addvipa");
|
|
|
|
else
|
|
|
|
QETH_CARD_TEXT(card, 2, "addip");
|
2016-06-16 21:18:58 +07:00
|
|
|
|
|
|
|
if (tmp_addr->proto == QETH_PROT_IPV4)
|
|
|
|
QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4);
|
2008-02-15 15:19:42 +07:00
|
|
|
else {
|
2016-06-16 21:18:58 +07:00
|
|
|
QETH_CARD_HEX(card, 4, &tmp_addr->u.a6.addr, 8);
|
|
|
|
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
|
|
|
|
}
|
|
|
|
|
2018-02-28 00:58:16 +07:00
|
|
|
addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
|
|
|
|
if (addr) {
|
|
|
|
if (tmp_addr->type != QETH_IP_TYPE_NORMAL)
|
|
|
|
return -EADDRINUSE;
|
|
|
|
if (qeth_l3_addr_match_all(addr, tmp_addr)) {
|
|
|
|
addr->ref_counter++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
qeth_l3_ipaddr_to_string(tmp_addr->proto, (u8 *)&tmp_addr->u,
|
|
|
|
buf);
|
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Registering IP address %s failed\n", buf);
|
|
|
|
return -EADDRINUSE;
|
|
|
|
} else {
|
2019-11-14 17:19:23 +07:00
|
|
|
addr = kmemdup(tmp_addr, sizeof(*tmp_addr), GFP_KERNEL);
|
2016-06-16 21:18:58 +07:00
|
|
|
if (!addr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2017-12-14 00:56:30 +07:00
|
|
|
if (qeth_l3_is_addr_covered_by_ipato(card, addr)) {
|
2016-06-16 21:18:58 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "tkovaddr");
|
2018-03-10 00:13:03 +07:00
|
|
|
addr->ipato = 1;
|
2016-06-16 21:18:58 +07:00
|
|
|
}
|
|
|
|
hash_add(card->ip_htable, &addr->hnode,
|
|
|
|
qeth_l3_ipaddr_hash(addr));
|
|
|
|
|
2016-09-15 19:39:23 +07:00
|
|
|
if (!qeth_card_hw_is_reachable(card)) {
|
|
|
|
addr->disp_flag = QETH_DISP_ADDR_ADD;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-18 23:34:48 +07:00
|
|
|
rc = qeth_l3_register_addr_entry(card, addr);
|
2016-06-16 21:18:58 +07:00
|
|
|
|
s390/qeth: allow cmd callbacks to return errnos
Error propagation from cmd callbacks currently works in a way where
qeth_send_control_data_cb() picks the raw HW code from the response,
and the cmd's originator later translates this into an errno.
The callback itself only returns 0 ("done") or 1 ("expect more data").
This is
1. limiting, as the only means for the callback to report an internal
error is to invent pseudo HW codes (such as IPA_RC_ENOMEM), that
the originator then needs to understand. For non-IPA callbacks, we
even provide a separate field in the IO buffer metadata (iob->rc) so
the callback can pass back a return value.
2. fragile, as the originator must take care to not translate any errno
that is returned by qeth's own IO code paths (eg -ENOMEM). Also, any
originator that forgets to translate the HW codes potentially passes
garbage back to its caller. For instance, see
commit 2aa4867198c2 ("s390/qeth: translate SETVLAN/DELVLAN errors").
Introduce a new model where all HW error translation is done within the
callback, and the callback returns
> 0, if it expects more data (as before)
== 0, on success
< 0, with an errno
Start off with converting all callbacks to the new model that either
a) pass back pseudo HW codes, or b) have a dependency on a specific
HW error code. Also convert c) the one callback that uses iob->rc, and
d) qeth_setadpparms_change_macaddr_cb() so that it can pass back an
error back to qeth_l2_request_initial_mac() even when the cmd itself
was successful.
The old model remains supported: if the callback returns 0, we still
propagate the response's HW error code back to the originator.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-13 00:33:23 +07:00
|
|
|
if (!rc || rc == -EADDRINUSE || rc == -ENETDOWN) {
|
2016-06-16 21:18:58 +07:00
|
|
|
addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
|
|
|
} else {
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
}
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-03-28 22:39:21 +07:00
|
|
|
static int qeth_l3_modify_ip(struct qeth_card *card, struct qeth_ipaddr *addr,
|
|
|
|
bool add)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_lock(&card->ip_lock);
|
2019-03-28 22:39:21 +07:00
|
|
|
rc = add ? qeth_l3_add_ip(card, addr) : qeth_l3_delete_ip(card, addr);
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_unlock(&card->ip_lock);
|
2019-03-28 22:39:21 +07:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-03-28 22:39:19 +07:00
|
|
|
static void qeth_l3_drain_rx_mode_cache(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
struct hlist_node *tmp;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) {
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-16 21:18:58 +07:00
|
|
|
static void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
|
|
|
struct qeth_ipaddr *addr;
|
2016-06-16 21:18:58 +07:00
|
|
|
struct hlist_node *tmp;
|
|
|
|
int i;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2016-06-16 21:18:58 +07:00
|
|
|
QETH_CARD_TEXT(card, 4, "clearip");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_lock(&card->ip_lock);
|
2016-06-16 21:18:58 +07:00
|
|
|
|
|
|
|
hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
|
|
|
|
if (!recover) {
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
2008-02-15 15:19:42 +07:00
|
|
|
continue;
|
|
|
|
}
|
2016-06-16 21:18:58 +07:00
|
|
|
addr->disp_flag = QETH_DISP_ADDR_ADD;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_unlock(&card->ip_lock);
|
2016-06-16 21:18:58 +07:00
|
|
|
}
|
2019-03-28 22:39:19 +07:00
|
|
|
|
2016-06-16 21:18:58 +07:00
|
|
|
static void qeth_l3_recover_ip(struct qeth_card *card)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2016-06-16 21:18:58 +07:00
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
struct hlist_node *tmp;
|
|
|
|
int i;
|
|
|
|
int rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2016-09-15 19:39:23 +07:00
|
|
|
QETH_CARD_TEXT(card, 4, "recovrip");
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_lock(&card->ip_lock);
|
2016-06-16 21:18:58 +07:00
|
|
|
|
|
|
|
hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
|
2018-02-28 00:58:13 +07:00
|
|
|
if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
|
2019-12-18 23:34:48 +07:00
|
|
|
rc = qeth_l3_register_addr_entry(card, addr);
|
2016-06-16 21:18:58 +07:00
|
|
|
|
|
|
|
if (!rc) {
|
|
|
|
addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
|
|
|
} else {
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_unlock(&card->ip_lock);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
s390/qeth: allow cmd callbacks to return errnos
Error propagation from cmd callbacks currently works in a way where
qeth_send_control_data_cb() picks the raw HW code from the response,
and the cmd's originator later translates this into an errno.
The callback itself only returns 0 ("done") or 1 ("expect more data").
This is
1. limiting, as the only means for the callback to report an internal
error is to invent pseudo HW codes (such as IPA_RC_ENOMEM), that
the originator then needs to understand. For non-IPA callbacks, we
even provide a separate field in the IO buffer metadata (iob->rc) so
the callback can pass back a return value.
2. fragile, as the originator must take care to not translate any errno
that is returned by qeth's own IO code paths (eg -ENOMEM). Also, any
originator that forgets to translate the HW codes potentially passes
garbage back to its caller. For instance, see
commit 2aa4867198c2 ("s390/qeth: translate SETVLAN/DELVLAN errors").
Introduce a new model where all HW error translation is done within the
callback, and the callback returns
> 0, if it expects more data (as before)
== 0, on success
< 0, with an errno
Start off with converting all callbacks to the new model that either
a) pass back pseudo HW codes, or b) have a dependency on a specific
HW error code. Also convert c) the one callback that uses iob->rc, and
d) qeth_setadpparms_change_macaddr_cb() so that it can pass back an
error back to qeth_l2_request_initial_mac() even when the cmd itself
was successful.
The old model remains supported: if the callback returns 0, we still
propagate the response's HW error code back to the originator.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-13 00:33:23 +07:00
|
|
|
static int qeth_l3_setdelip_cb(struct qeth_card *card, struct qeth_reply *reply,
|
|
|
|
unsigned long data)
|
|
|
|
{
|
|
|
|
struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
|
|
|
|
|
|
|
|
switch (cmd->hdr.return_code) {
|
|
|
|
case IPA_RC_SUCCESS:
|
|
|
|
return 0;
|
|
|
|
case IPA_RC_DUPLICATE_IP_ADDRESS:
|
|
|
|
return -EADDRINUSE;
|
|
|
|
case IPA_RC_MC_ADDR_NOT_FOUND:
|
|
|
|
return -ENOENT;
|
|
|
|
case IPA_RC_LAN_OFFLINE:
|
|
|
|
return -ENETDOWN;
|
|
|
|
default:
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
static int qeth_l3_send_setdelmc(struct qeth_card *card,
|
|
|
|
struct qeth_ipaddr *addr, int ipacmd)
|
|
|
|
{
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 4, "setdelmc");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-06-27 22:01:22 +07:00
|
|
|
iob = qeth_ipa_alloc_cmd(card, ipacmd, addr->proto,
|
|
|
|
IPA_DATA_SIZEOF(setdelipm));
|
2015-01-21 19:39:10 +07:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2018-03-10 00:12:52 +07:00
|
|
|
cmd = __ipa_cmd(iob);
|
2019-10-31 19:42:21 +07:00
|
|
|
if (addr->proto == QETH_PROT_IPV6) {
|
|
|
|
cmd->data.setdelipm.ip = addr->u.a6.addr;
|
|
|
|
ipv6_eth_mc_map(&addr->u.a6.addr, cmd->data.setdelipm.mac);
|
|
|
|
} else {
|
|
|
|
cmd->data.setdelipm.ip.s6_addr32[3] = addr->u.a4.addr;
|
|
|
|
ip_eth_mc_map(addr->u.a4.addr, cmd->data.setdelipm.mac);
|
|
|
|
}
|
2008-02-15 15:19:42 +07:00
|
|
|
|
s390/qeth: allow cmd callbacks to return errnos
Error propagation from cmd callbacks currently works in a way where
qeth_send_control_data_cb() picks the raw HW code from the response,
and the cmd's originator later translates this into an errno.
The callback itself only returns 0 ("done") or 1 ("expect more data").
This is
1. limiting, as the only means for the callback to report an internal
error is to invent pseudo HW codes (such as IPA_RC_ENOMEM), that
the originator then needs to understand. For non-IPA callbacks, we
even provide a separate field in the IO buffer metadata (iob->rc) so
the callback can pass back a return value.
2. fragile, as the originator must take care to not translate any errno
that is returned by qeth's own IO code paths (eg -ENOMEM). Also, any
originator that forgets to translate the HW codes potentially passes
garbage back to its caller. For instance, see
commit 2aa4867198c2 ("s390/qeth: translate SETVLAN/DELVLAN errors").
Introduce a new model where all HW error translation is done within the
callback, and the callback returns
> 0, if it expects more data (as before)
== 0, on success
< 0, with an errno
Start off with converting all callbacks to the new model that either
a) pass back pseudo HW codes, or b) have a dependency on a specific
HW error code. Also convert c) the one callback that uses iob->rc, and
d) qeth_setadpparms_change_macaddr_cb() so that it can pass back an
error back to qeth_l2_request_initial_mac() even when the cmd itself
was successful.
The old model remains supported: if the callback returns 0, we still
propagate the response's HW error code back to the originator.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-13 00:33:23 +07:00
|
|
|
return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2019-12-18 23:34:46 +07:00
|
|
|
static void qeth_l3_set_ipv6_prefix(struct in6_addr *prefix, unsigned int len)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2019-12-18 23:34:46 +07:00
|
|
|
unsigned int i = 0;
|
|
|
|
|
|
|
|
while (len && i < 4) {
|
|
|
|
int mask_len = min_t(int, len, 32);
|
|
|
|
|
|
|
|
prefix->s6_addr32[i] = inet_make_mask(mask_len);
|
|
|
|
len -= mask_len;
|
|
|
|
i++;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-10 00:13:03 +07:00
|
|
|
static u32 qeth_l3_get_setdelip_flags(struct qeth_ipaddr *addr, bool set)
|
|
|
|
{
|
|
|
|
switch (addr->type) {
|
|
|
|
case QETH_IP_TYPE_RXIP:
|
|
|
|
return (set) ? QETH_IPA_SETIP_TAKEOVER_FLAG : 0;
|
|
|
|
case QETH_IP_TYPE_VIPA:
|
|
|
|
return (set) ? QETH_IPA_SETIP_VIPA_FLAG :
|
|
|
|
QETH_IPA_DELIP_VIPA_FLAG;
|
|
|
|
default:
|
|
|
|
return (set && addr->ipato) ? QETH_IPA_SETIP_TAKEOVER_FLAG : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
static int qeth_l3_send_setdelip(struct qeth_card *card,
|
2018-03-10 00:13:03 +07:00
|
|
|
struct qeth_ipaddr *addr,
|
|
|
|
enum qeth_ipa_cmds ipacmd)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
2018-03-10 00:13:03 +07:00
|
|
|
u32 flags;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 4, "setdelip");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-06-27 22:01:22 +07:00
|
|
|
iob = qeth_ipa_alloc_cmd(card, ipacmd, addr->proto,
|
|
|
|
IPA_DATA_SIZEOF(setdelip6));
|
2015-01-21 19:39:10 +07:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2018-03-10 00:12:52 +07:00
|
|
|
cmd = __ipa_cmd(iob);
|
2018-03-10 00:13:03 +07:00
|
|
|
|
|
|
|
flags = qeth_l3_get_setdelip_flags(addr, ipacmd == IPA_CMD_SETIP);
|
|
|
|
QETH_CARD_TEXT_(card, 4, "flags%02X", flags);
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
if (addr->proto == QETH_PROT_IPV6) {
|
2019-12-18 23:34:46 +07:00
|
|
|
cmd->data.setdelip6.addr = addr->u.a6.addr;
|
|
|
|
qeth_l3_set_ipv6_prefix(&cmd->data.setdelip6.prefix,
|
|
|
|
addr->u.a6.pfxlen);
|
2008-02-15 15:19:42 +07:00
|
|
|
cmd->data.setdelip6.flags = flags;
|
|
|
|
} else {
|
2019-12-18 23:34:46 +07:00
|
|
|
cmd->data.setdelip4.addr = addr->u.a4.addr;
|
|
|
|
cmd->data.setdelip4.mask = addr->u.a4.mask;
|
2008-02-15 15:19:42 +07:00
|
|
|
cmd->data.setdelip4.flags = flags;
|
|
|
|
}
|
|
|
|
|
s390/qeth: allow cmd callbacks to return errnos
Error propagation from cmd callbacks currently works in a way where
qeth_send_control_data_cb() picks the raw HW code from the response,
and the cmd's originator later translates this into an errno.
The callback itself only returns 0 ("done") or 1 ("expect more data").
This is
1. limiting, as the only means for the callback to report an internal
error is to invent pseudo HW codes (such as IPA_RC_ENOMEM), that
the originator then needs to understand. For non-IPA callbacks, we
even provide a separate field in the IO buffer metadata (iob->rc) so
the callback can pass back a return value.
2. fragile, as the originator must take care to not translate any errno
that is returned by qeth's own IO code paths (eg -ENOMEM). Also, any
originator that forgets to translate the HW codes potentially passes
garbage back to its caller. For instance, see
commit 2aa4867198c2 ("s390/qeth: translate SETVLAN/DELVLAN errors").
Introduce a new model where all HW error translation is done within the
callback, and the callback returns
> 0, if it expects more data (as before)
== 0, on success
< 0, with an errno
Start off with converting all callbacks to the new model that either
a) pass back pseudo HW codes, or b) have a dependency on a specific
HW error code. Also convert c) the one callback that uses iob->rc, and
d) qeth_setadpparms_change_macaddr_cb() so that it can pass back an
error back to qeth_l2_request_initial_mac() even when the cmd itself
was successful.
The old model remains supported: if the callback returns 0, we still
propagate the response's HW error code back to the originator.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-13 00:33:23 +07:00
|
|
|
return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_send_setrouting(struct qeth_card *card,
|
|
|
|
enum qeth_routing_types type, enum qeth_prot_versions prot)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 4, "setroutg");
|
2019-06-27 22:01:22 +07:00
|
|
|
iob = qeth_ipa_alloc_cmd(card, IPA_CMD_SETRTG, prot,
|
|
|
|
IPA_DATA_SIZEOF(setrtg));
|
2015-01-21 19:39:10 +07:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2018-03-10 00:12:52 +07:00
|
|
|
cmd = __ipa_cmd(iob);
|
2008-02-15 15:19:42 +07:00
|
|
|
cmd->data.setrtg.type = (type);
|
|
|
|
rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-03-19 03:04:43 +07:00
|
|
|
static int qeth_l3_correct_routing_type(struct qeth_card *card,
|
2008-02-15 15:19:42 +07:00
|
|
|
enum qeth_routing_types *type, enum qeth_prot_versions prot)
|
|
|
|
{
|
2019-04-25 23:25:57 +07:00
|
|
|
if (IS_IQD(card)) {
|
2008-02-15 15:19:42 +07:00
|
|
|
switch (*type) {
|
|
|
|
case NO_ROUTER:
|
|
|
|
case PRIMARY_CONNECTOR:
|
|
|
|
case SECONDARY_CONNECTOR:
|
|
|
|
case MULTICAST_ROUTER:
|
2013-03-19 03:04:43 +07:00
|
|
|
return 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
default:
|
|
|
|
goto out_inval;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (*type) {
|
|
|
|
case NO_ROUTER:
|
|
|
|
case PRIMARY_ROUTER:
|
|
|
|
case SECONDARY_ROUTER:
|
2013-03-19 03:04:43 +07:00
|
|
|
return 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
case MULTICAST_ROUTER:
|
|
|
|
if (qeth_is_ipafunc_supported(card, prot,
|
|
|
|
IPA_OSA_MC_ROUTER))
|
2013-03-19 03:04:43 +07:00
|
|
|
return 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
default:
|
|
|
|
goto out_inval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out_inval:
|
|
|
|
*type = NO_ROUTER;
|
2013-03-19 03:04:43 +07:00
|
|
|
return -EINVAL;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
int qeth_l3_setrouting_v4(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "setrtg4");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2013-03-19 03:04:43 +07:00
|
|
|
rc = qeth_l3_correct_routing_type(card, &card->options.route4.type,
|
2008-02-15 15:19:42 +07:00
|
|
|
QETH_PROT_IPV4);
|
2013-03-19 03:04:43 +07:00
|
|
|
if (rc)
|
|
|
|
return rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
rc = qeth_l3_send_setrouting(card, card->options.route4.type,
|
|
|
|
QETH_PROT_IPV4);
|
|
|
|
if (rc) {
|
|
|
|
card->options.route4.type = NO_ROUTER;
|
2018-11-03 01:04:08 +07:00
|
|
|
QETH_DBF_MESSAGE(2, "Error (%#06x) while setting routing type on device %x. Type set to 'no router'.\n",
|
|
|
|
rc, CARD_DEVID(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qeth_l3_setrouting_v6(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "setrtg6");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_IPV6))
|
|
|
|
return 0;
|
2013-03-19 03:04:43 +07:00
|
|
|
rc = qeth_l3_correct_routing_type(card, &card->options.route6.type,
|
2008-02-15 15:19:42 +07:00
|
|
|
QETH_PROT_IPV6);
|
2013-03-19 03:04:43 +07:00
|
|
|
if (rc)
|
|
|
|
return rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
rc = qeth_l3_send_setrouting(card, card->options.route6.type,
|
|
|
|
QETH_PROT_IPV6);
|
|
|
|
if (rc) {
|
|
|
|
card->options.route6.type = NO_ROUTER;
|
2018-11-03 01:04:08 +07:00
|
|
|
QETH_DBF_MESSAGE(2, "Error (%#06x) while setting routing type on device %x. Type set to 'no router'.\n",
|
|
|
|
rc, CARD_DEVID(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IP address takeover related functions
|
|
|
|
*/
|
2017-12-14 00:56:32 +07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* qeth_l3_update_ipato() - Update 'takeover' property, for all NORMAL IPs.
|
|
|
|
*
|
|
|
|
* Caller must hold ip_lock.
|
|
|
|
*/
|
|
|
|
void qeth_l3_update_ipato(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
hash_for_each(card->ip_htable, i, addr, hnode) {
|
|
|
|
if (addr->type != QETH_IP_TYPE_NORMAL)
|
|
|
|
continue;
|
2018-03-10 00:13:03 +07:00
|
|
|
addr->ipato = qeth_l3_is_addr_covered_by_ipato(card, addr);
|
2017-12-14 00:56:32 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
static void qeth_l3_clear_ipato_list(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct qeth_ipato_entry *ipatoe, *tmp;
|
|
|
|
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_lock(&card->ip_lock);
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) {
|
|
|
|
list_del(&ipatoe->entry);
|
|
|
|
kfree(ipatoe);
|
|
|
|
}
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2017-12-14 00:56:32 +07:00
|
|
|
qeth_l3_update_ipato(card);
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_unlock(&card->ip_lock);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
int qeth_l3_add_ipato_entry(struct qeth_card *card,
|
|
|
|
struct qeth_ipato_entry *new)
|
|
|
|
{
|
|
|
|
struct qeth_ipato_entry *ipatoe;
|
|
|
|
int rc = 0;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "addipato");
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-12-18 23:34:45 +07:00
|
|
|
mutex_lock(&card->conf_mutex);
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_lock(&card->ip_lock);
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
list_for_each_entry(ipatoe, &card->ipato.entries, entry) {
|
|
|
|
if (ipatoe->proto != new->proto)
|
|
|
|
continue;
|
|
|
|
if (!memcmp(ipatoe->addr, new->addr,
|
|
|
|
(ipatoe->proto == QETH_PROT_IPV4)? 4:16) &&
|
|
|
|
(ipatoe->mask_bits == new->mask_bits)) {
|
|
|
|
rc = -EEXIST;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2017-12-14 00:56:32 +07:00
|
|
|
if (!rc) {
|
2008-02-15 15:19:42 +07:00
|
|
|
list_add_tail(&new->entry, &card->ipato.entries);
|
2017-12-14 00:56:32 +07:00
|
|
|
qeth_l3_update_ipato(card);
|
|
|
|
}
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_unlock(&card->ip_lock);
|
2019-12-18 23:34:45 +07:00
|
|
|
mutex_unlock(&card->conf_mutex);
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-12-27 23:44:28 +07:00
|
|
|
int qeth_l3_del_ipato_entry(struct qeth_card *card,
|
|
|
|
enum qeth_prot_versions proto, u8 *addr,
|
|
|
|
int mask_bits)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
|
|
|
struct qeth_ipato_entry *ipatoe, *tmp;
|
2017-12-27 23:44:28 +07:00
|
|
|
int rc = -ENOENT;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "delipato");
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-12-18 23:34:45 +07:00
|
|
|
mutex_lock(&card->conf_mutex);
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_lock(&card->ip_lock);
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) {
|
|
|
|
if (ipatoe->proto != proto)
|
|
|
|
continue;
|
|
|
|
if (!memcmp(ipatoe->addr, addr,
|
|
|
|
(proto == QETH_PROT_IPV4)? 4:16) &&
|
|
|
|
(ipatoe->mask_bits == mask_bits)) {
|
|
|
|
list_del(&ipatoe->entry);
|
2017-12-14 00:56:32 +07:00
|
|
|
qeth_l3_update_ipato(card);
|
2008-02-15 15:19:42 +07:00
|
|
|
kfree(ipatoe);
|
2017-12-27 23:44:28 +07:00
|
|
|
rc = 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
}
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_unlock(&card->ip_lock);
|
2019-12-18 23:34:45 +07:00
|
|
|
mutex_unlock(&card->conf_mutex);
|
|
|
|
|
2017-12-27 23:44:28 +07:00
|
|
|
return rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2018-03-10 00:13:02 +07:00
|
|
|
int qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip,
|
|
|
|
enum qeth_ip_types type,
|
|
|
|
enum qeth_prot_versions proto)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2018-03-10 00:13:02 +07:00
|
|
|
struct qeth_ipaddr addr;
|
2019-12-18 23:34:45 +07:00
|
|
|
int rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2018-03-10 00:13:02 +07:00
|
|
|
qeth_l3_init_ipaddr(&addr, type, proto);
|
|
|
|
if (proto == QETH_PROT_IPV4)
|
|
|
|
memcpy(&addr.u.a4.addr, ip, 4);
|
|
|
|
else
|
|
|
|
memcpy(&addr.u.a6.addr, ip, 16);
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-12-18 23:34:45 +07:00
|
|
|
mutex_lock(&card->conf_mutex);
|
|
|
|
rc = qeth_l3_modify_ip(card, &addr, add);
|
|
|
|
mutex_unlock(&card->conf_mutex);
|
|
|
|
|
|
|
|
return rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2018-03-10 00:13:02 +07:00
|
|
|
int qeth_l3_modify_hsuid(struct qeth_card *card, bool add)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2018-03-10 00:13:02 +07:00
|
|
|
struct qeth_ipaddr addr;
|
2019-03-28 22:39:21 +07:00
|
|
|
unsigned int i;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2018-03-10 00:13:02 +07:00
|
|
|
qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6);
|
|
|
|
addr.u.a6.addr.s6_addr[0] = 0xfe;
|
|
|
|
addr.u.a6.addr.s6_addr[1] = 0x80;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
addr.u.a6.addr.s6_addr[8+i] = card->options.hsuid[i];
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-03-28 22:39:21 +07:00
|
|
|
return qeth_l3_modify_ip(card, &addr, add);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_register_addr_entry(struct qeth_card *card,
|
|
|
|
struct qeth_ipaddr *addr)
|
|
|
|
{
|
|
|
|
char buf[50];
|
|
|
|
int rc = 0;
|
|
|
|
int cnt = 3;
|
|
|
|
|
2018-11-03 01:04:09 +07:00
|
|
|
if (card->options.sniffer)
|
|
|
|
return 0;
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
if (addr->proto == QETH_PROT_IPV4) {
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "setaddr4");
|
|
|
|
QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int));
|
2008-02-15 15:19:42 +07:00
|
|
|
} else if (addr->proto == QETH_PROT_IPV6) {
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "setaddr6");
|
|
|
|
QETH_CARD_HEX(card, 3, &addr->u.a6.addr, 8);
|
|
|
|
QETH_CARD_HEX(card, 3, ((char *)&addr->u.a6.addr) + 8, 8);
|
2008-02-15 15:19:42 +07:00
|
|
|
} else {
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "setaddr?");
|
|
|
|
QETH_CARD_HEX(card, 3, addr, sizeof(struct qeth_ipaddr));
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
do {
|
|
|
|
if (addr->is_multicast)
|
|
|
|
rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_SETIPM);
|
|
|
|
else
|
2018-03-10 00:13:03 +07:00
|
|
|
rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_SETIP);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc)
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "failed");
|
2008-02-15 15:19:42 +07:00
|
|
|
} while ((--cnt > 0) && rc);
|
|
|
|
if (rc) {
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "FAILED");
|
2008-02-15 15:19:42 +07:00
|
|
|
qeth_l3_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Registering IP address %s failed\n", buf);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_deregister_addr_entry(struct qeth_card *card,
|
|
|
|
struct qeth_ipaddr *addr)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
2018-11-03 01:04:09 +07:00
|
|
|
if (card->options.sniffer)
|
|
|
|
return 0;
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
if (addr->proto == QETH_PROT_IPV4) {
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "deladdr4");
|
|
|
|
QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int));
|
2008-02-15 15:19:42 +07:00
|
|
|
} else if (addr->proto == QETH_PROT_IPV6) {
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "deladdr6");
|
|
|
|
QETH_CARD_HEX(card, 3, &addr->u.a6.addr, 8);
|
|
|
|
QETH_CARD_HEX(card, 3, ((char *)&addr->u.a6.addr) + 8, 8);
|
2008-02-15 15:19:42 +07:00
|
|
|
} else {
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "deladdr?");
|
|
|
|
QETH_CARD_HEX(card, 3, addr, sizeof(struct qeth_ipaddr));
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
if (addr->is_multicast)
|
|
|
|
rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_DELIPM);
|
|
|
|
else
|
2018-03-10 00:13:03 +07:00
|
|
|
rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP);
|
2008-07-14 14:59:31 +07:00
|
|
|
if (rc)
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "failed");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_setadapter_parms(struct qeth_card *card)
|
|
|
|
{
|
2017-08-15 22:02:41 +07:00
|
|
|
int rc = 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-06-11 23:37:55 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "setadprm");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
if (qeth_adp_supported(card, IPA_SETADP_ALTER_MAC_ADDRESS)) {
|
|
|
|
rc = qeth_setadpparms_change_macaddr(card);
|
|
|
|
if (rc)
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_warn(&card->gdev->dev, "Reading the adapter MAC"
|
2009-01-05 08:36:32 +07:00
|
|
|
" address failed\n");
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "ipaarp");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_info(&card->gdev->dev,
|
|
|
|
"ARP processing not supported on %s!\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
return 0;
|
|
|
|
}
|
2015-09-18 21:06:51 +07:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
|
2019-06-27 22:01:23 +07:00
|
|
|
IPA_CMD_ASS_START, NULL);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Starting ARP processing support for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_source_mac(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "stsrcmac");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_SOURCE_MAC)) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_info(&card->gdev->dev,
|
2009-01-05 08:34:52 +07:00
|
|
|
"Inbound source MAC-address not supported on %s\n",
|
2008-12-25 19:39:49 +07:00
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2015-09-18 21:06:51 +07:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC,
|
2019-06-27 22:01:23 +07:00
|
|
|
IPA_CMD_ASS_START, NULL);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc)
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_warn(&card->gdev->dev,
|
2009-01-05 08:34:52 +07:00
|
|
|
"Starting source MAC-address support for %s failed\n",
|
2008-12-25 19:39:49 +07:00
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_vlan(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "strtvlan");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_FULL_VLAN)) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_info(&card->gdev->dev,
|
|
|
|
"VLAN not supported on %s\n", QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2015-09-18 21:06:51 +07:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO,
|
2019-06-27 22:01:23 +07:00
|
|
|
IPA_CMD_ASS_START, NULL);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Starting VLAN support for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
} else {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_info(&card->gdev->dev, "VLAN enabled\n");
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_multicast(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "stmcast");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_MULTICASTING)) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_info(&card->gdev->dev,
|
|
|
|
"Multicast not supported on %s\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2015-09-18 21:06:51 +07:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING,
|
2019-06-27 22:01:23 +07:00
|
|
|
IPA_CMD_ASS_START, NULL);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Starting multicast support for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
} else {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_info(&card->gdev->dev, "Multicast enabled\n");
|
2008-02-15 15:19:42 +07:00
|
|
|
card->dev->flags |= IFF_MULTICAST;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_softsetup_ipv6(struct qeth_card *card)
|
|
|
|
{
|
2019-06-27 22:01:23 +07:00
|
|
|
u32 ipv6_data = 3;
|
2008-02-15 15:19:42 +07:00
|
|
|
int rc;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "softipv6");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-04-25 23:25:57 +07:00
|
|
|
if (IS_IQD(card))
|
2017-06-06 19:33:44 +07:00
|
|
|
goto out;
|
|
|
|
|
2019-06-27 22:01:23 +07:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_IPV6, IPA_CMD_ASS_START,
|
|
|
|
&ipv6_data);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_err(&card->gdev->dev,
|
|
|
|
"Activating IPv6 support for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
return rc;
|
|
|
|
}
|
2019-06-27 22:01:23 +07:00
|
|
|
rc = qeth_send_simple_setassparms_v6(card, IPA_IPV6, IPA_CMD_ASS_START,
|
|
|
|
NULL);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_err(&card->gdev->dev,
|
|
|
|
"Activating IPv6 support for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
return rc;
|
|
|
|
}
|
2018-04-26 14:42:21 +07:00
|
|
|
rc = qeth_send_simple_setassparms_v6(card, IPA_PASSTHRU,
|
2019-06-27 22:01:23 +07:00
|
|
|
IPA_CMD_ASS_START, NULL);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Enabling the passthrough mode for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
out:
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_info(&card->gdev->dev, "IPV6 enabled\n");
|
2008-02-15 15:19:42 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_ipv6(struct qeth_card *card)
|
|
|
|
{
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "strtipv6");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_IPV6)) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_info(&card->gdev->dev,
|
|
|
|
"IPv6 not supported on %s\n", QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
return 0;
|
|
|
|
}
|
2017-12-21 02:10:58 +07:00
|
|
|
return qeth_l3_softsetup_ipv6(card);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
|
|
|
|
{
|
2019-06-27 22:01:23 +07:00
|
|
|
u32 filter_data = 1;
|
2008-02-15 15:19:42 +07:00
|
|
|
int rc;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "stbrdcst");
|
2008-02-15 15:19:42 +07:00
|
|
|
card->info.broadcast_capable = 0;
|
|
|
|
if (!qeth_is_supported(card, IPA_FILTERING)) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_info(&card->gdev->dev,
|
|
|
|
"Broadcast not supported on %s\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
rc = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-09-18 21:06:51 +07:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
|
2019-06-27 22:01:23 +07:00
|
|
|
IPA_CMD_ASS_START, NULL);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_warn(&card->gdev->dev, "Enabling broadcast filtering for "
|
|
|
|
"%s failed\n", QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-09-18 21:06:51 +07:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
|
2019-06-27 22:01:23 +07:00
|
|
|
IPA_CMD_ASS_CONFIGURE, &filter_data);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Setting up broadcast filtering for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_info(&card->gdev->dev, "Broadcast enabled\n");
|
2015-09-18 21:06:51 +07:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
|
2019-06-27 22:01:23 +07:00
|
|
|
IPA_CMD_ASS_ENABLE, &filter_data);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc) {
|
2008-12-25 19:39:49 +07:00
|
|
|
dev_warn(&card->gdev->dev, "Setting up broadcast echo "
|
|
|
|
"filtering for %s failed\n", QETH_CARD_IFNAME(card));
|
2008-02-15 15:19:42 +07:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO;
|
|
|
|
out:
|
|
|
|
if (card->info.broadcast_capable)
|
|
|
|
card->dev->flags |= IFF_BROADCAST;
|
|
|
|
else
|
|
|
|
card->dev->flags &= ~IFF_BROADCAST;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2020-01-25 22:53:03 +07:00
|
|
|
static void qeth_l3_start_ipassists(struct qeth_card *card)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "strtipas");
|
2009-11-12 07:11:41 +07:00
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
qeth_l3_start_ipa_arp_processing(card); /* go on*/
|
|
|
|
qeth_l3_start_ipa_source_mac(card); /* go on*/
|
|
|
|
qeth_l3_start_ipa_vlan(card); /* go on*/
|
|
|
|
qeth_l3_start_ipa_multicast(card); /* go on*/
|
|
|
|
qeth_l3_start_ipa_ipv6(card); /* go on*/
|
|
|
|
qeth_l3_start_ipa_broadcast(card); /* go on*/
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_iqd_read_initial_mac_cb(struct qeth_card *card,
|
|
|
|
struct qeth_reply *reply, unsigned long data)
|
|
|
|
{
|
2019-02-13 00:33:25 +07:00
|
|
|
struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-02-13 00:33:25 +07:00
|
|
|
if (cmd->hdr.return_code)
|
|
|
|
return -EIO;
|
2020-02-28 00:08:11 +07:00
|
|
|
if (!is_valid_ether_addr(cmd->data.create_destroy_addr.mac_addr))
|
|
|
|
return -EADDRNOTAVAIL;
|
2019-02-13 00:33:25 +07:00
|
|
|
|
|
|
|
ether_addr_copy(card->dev->dev_addr,
|
2020-02-28 00:08:10 +07:00
|
|
|
cmd->data.create_destroy_addr.mac_addr);
|
2008-02-15 15:19:42 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_iqd_read_initial_mac(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
|
2019-06-11 23:37:55 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "hsrmac");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-06-27 22:01:22 +07:00
|
|
|
iob = qeth_ipa_alloc_cmd(card, IPA_CMD_CREATE_ADDR, QETH_PROT_IPV6,
|
|
|
|
IPA_DATA_SIZEOF(create_destroy_addr));
|
2015-01-21 19:39:10 +07:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
rc = qeth_send_ipa_cmd(card, iob, qeth_l3_iqd_read_initial_mac_cb,
|
|
|
|
NULL);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_get_unique_id_cb(struct qeth_card *card,
|
|
|
|
struct qeth_reply *reply, unsigned long data)
|
|
|
|
{
|
2019-02-13 00:33:25 +07:00
|
|
|
struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-02-13 00:33:25 +07:00
|
|
|
if (cmd->hdr.return_code == 0) {
|
2020-02-28 00:08:10 +07:00
|
|
|
card->info.unique_id = cmd->data.create_destroy_addr.uid;
|
2019-02-13 00:33:25 +07:00
|
|
|
return 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
2019-02-13 00:33:25 +07:00
|
|
|
|
|
|
|
card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
|
|
|
|
UNIQUE_ID_NOT_BY_CARD;
|
|
|
|
dev_warn(&card->gdev->dev, "The network adapter failed to generate a unique ID\n");
|
|
|
|
return -EIO;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_get_unique_id(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
|
2019-06-11 23:37:55 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "guniqeid");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_IPV6)) {
|
|
|
|
card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
|
|
|
|
UNIQUE_ID_NOT_BY_CARD;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-27 22:01:22 +07:00
|
|
|
iob = qeth_ipa_alloc_cmd(card, IPA_CMD_CREATE_ADDR, QETH_PROT_IPV6,
|
|
|
|
IPA_DATA_SIZEOF(create_destroy_addr));
|
2015-01-21 19:39:10 +07:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2020-02-28 00:08:10 +07:00
|
|
|
__ipa_cmd(iob)->data.create_destroy_addr.uid = card->info.unique_id;
|
2008-02-15 15:19:42 +07:00
|
|
|
rc = qeth_send_ipa_cmd(card, iob, qeth_l3_get_unique_id_cb, NULL);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2010-01-11 09:50:50 +07:00
|
|
|
static int
|
|
|
|
qeth_diags_trace_cb(struct qeth_card *card, struct qeth_reply *reply,
|
|
|
|
unsigned long data)
|
|
|
|
{
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
|
|
|
__u16 rc;
|
|
|
|
|
2019-06-11 23:37:55 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "diastrcb");
|
2010-01-11 09:50:50 +07:00
|
|
|
|
|
|
|
cmd = (struct qeth_ipa_cmd *)data;
|
|
|
|
rc = cmd->hdr.return_code;
|
2010-03-09 03:36:55 +07:00
|
|
|
if (rc)
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT_(card, 2, "dxter%x", rc);
|
2010-01-11 09:50:50 +07:00
|
|
|
switch (cmd->data.diagass.action) {
|
|
|
|
case QETH_DIAGS_CMD_TRACE_QUERY:
|
|
|
|
break;
|
|
|
|
case QETH_DIAGS_CMD_TRACE_DISABLE:
|
2010-03-09 03:36:55 +07:00
|
|
|
switch (rc) {
|
|
|
|
case 0:
|
|
|
|
case IPA_RC_INVALID_SUBCMD:
|
|
|
|
card->info.promisc_mode = SET_PROMISC_MODE_OFF;
|
|
|
|
dev_info(&card->gdev->dev, "The HiperSockets network "
|
|
|
|
"traffic analyzer is deactivated\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2010-01-11 09:50:50 +07:00
|
|
|
break;
|
|
|
|
case QETH_DIAGS_CMD_TRACE_ENABLE:
|
2010-03-09 03:36:55 +07:00
|
|
|
switch (rc) {
|
|
|
|
case 0:
|
|
|
|
card->info.promisc_mode = SET_PROMISC_MODE_ON;
|
|
|
|
dev_info(&card->gdev->dev, "The HiperSockets network "
|
|
|
|
"traffic analyzer is activated\n");
|
|
|
|
break;
|
|
|
|
case IPA_RC_HARDWARE_AUTH_ERROR:
|
|
|
|
dev_warn(&card->gdev->dev, "The device is not "
|
|
|
|
"authorized to run as a HiperSockets network "
|
|
|
|
"traffic analyzer\n");
|
|
|
|
break;
|
|
|
|
case IPA_RC_TRACE_ALREADY_ACTIVE:
|
|
|
|
dev_warn(&card->gdev->dev, "A HiperSockets "
|
|
|
|
"network traffic analyzer is already "
|
|
|
|
"active in the HiperSockets LAN\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2010-01-11 09:50:50 +07:00
|
|
|
break;
|
|
|
|
default:
|
2018-11-03 01:04:08 +07:00
|
|
|
QETH_DBF_MESSAGE(2, "Unknown sniffer action (%#06x) on device %x\n",
|
|
|
|
cmd->data.diagass.action, CARD_DEVID(card));
|
2010-01-11 09:50:50 +07:00
|
|
|
}
|
|
|
|
|
2019-02-13 00:33:25 +07:00
|
|
|
return rc ? -EIO : 0;
|
2010-01-11 09:50:50 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd)
|
|
|
|
{
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
|
|
|
|
2019-06-11 23:37:55 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "diagtrac");
|
2010-01-11 09:50:50 +07:00
|
|
|
|
2019-06-27 22:01:25 +07:00
|
|
|
iob = qeth_get_diag_cmd(card, QETH_DIAGS_CMD_TRACE, 0);
|
2015-01-21 19:39:10 +07:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2018-03-10 00:12:52 +07:00
|
|
|
cmd = __ipa_cmd(iob);
|
2010-01-11 09:50:50 +07:00
|
|
|
cmd->data.diagass.type = QETH_DIAGS_TYPE_HIPERSOCKET;
|
|
|
|
cmd->data.diagass.action = diags_cmd;
|
|
|
|
return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL);
|
|
|
|
}
|
|
|
|
|
2019-11-14 17:19:22 +07:00
|
|
|
static int qeth_l3_add_mcast_rtnl(struct net_device *dev, int vid, void *arg)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2019-11-14 17:19:22 +07:00
|
|
|
struct qeth_card *card = arg;
|
2019-11-14 17:19:21 +07:00
|
|
|
struct inet6_dev *in6_dev;
|
|
|
|
struct in_device *in4_dev;
|
|
|
|
struct qeth_ipaddr *ipm;
|
2019-11-14 17:19:23 +07:00
|
|
|
struct qeth_ipaddr tmp;
|
2008-02-15 15:19:42 +07:00
|
|
|
struct ip_mc_list *im4;
|
2019-11-14 17:19:21 +07:00
|
|
|
struct ifmcaddr6 *im6;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 4, "addmc");
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-11-14 17:19:21 +07:00
|
|
|
if (!dev || !(dev->flags & IFF_UP))
|
|
|
|
goto out;
|
|
|
|
|
2019-11-14 17:19:22 +07:00
|
|
|
in4_dev = __in_dev_get_rtnl(dev);
|
2019-11-14 17:19:21 +07:00
|
|
|
if (!in4_dev)
|
|
|
|
goto walk_ipv6;
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-11-14 17:19:23 +07:00
|
|
|
qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4);
|
|
|
|
tmp.disp_flag = QETH_DISP_ADDR_ADD;
|
|
|
|
tmp.is_multicast = 1;
|
|
|
|
|
2019-11-14 17:19:22 +07:00
|
|
|
for (im4 = rtnl_dereference(in4_dev->mc_list); im4 != NULL;
|
|
|
|
im4 = rtnl_dereference(im4->next_rcu)) {
|
2019-11-14 17:19:23 +07:00
|
|
|
tmp.u.a4.addr = im4->multiaddr;
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-11-14 17:19:23 +07:00
|
|
|
ipm = qeth_l3_find_addr_by_ip(card, &tmp);
|
2016-06-16 21:18:58 +07:00
|
|
|
if (ipm) {
|
2018-02-28 00:58:16 +07:00
|
|
|
/* for mcast, by-IP match means full match */
|
2016-06-16 21:18:58 +07:00
|
|
|
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
2019-11-14 17:19:23 +07:00
|
|
|
continue;
|
2016-06-16 21:18:58 +07:00
|
|
|
}
|
2019-11-14 17:19:23 +07:00
|
|
|
|
|
|
|
ipm = kmemdup(&tmp, sizeof(tmp), GFP_KERNEL);
|
|
|
|
if (!ipm)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
hash_add(card->ip_mc_htable, &ipm->hnode,
|
|
|
|
qeth_l3_ipaddr_hash(ipm));
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-11-14 17:19:21 +07:00
|
|
|
walk_ipv6:
|
|
|
|
if (!qeth_is_supported(card, IPA_IPV6))
|
|
|
|
goto out;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-11-14 17:19:21 +07:00
|
|
|
in6_dev = __in6_dev_get(dev);
|
|
|
|
if (!in6_dev)
|
|
|
|
goto out;
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-11-14 17:19:23 +07:00
|
|
|
qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6);
|
|
|
|
tmp.disp_flag = QETH_DISP_ADDR_ADD;
|
|
|
|
tmp.is_multicast = 1;
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-11-14 17:19:19 +07:00
|
|
|
read_lock_bh(&in6_dev->lock);
|
2008-02-15 15:19:42 +07:00
|
|
|
for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) {
|
2019-11-14 17:19:23 +07:00
|
|
|
tmp.u.a6.addr = im6->mca_addr;
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-11-14 17:19:23 +07:00
|
|
|
ipm = qeth_l3_find_addr_by_ip(card, &tmp);
|
2016-06-16 21:18:58 +07:00
|
|
|
if (ipm) {
|
2018-02-28 00:58:16 +07:00
|
|
|
/* for mcast, by-IP match means full match */
|
2016-06-16 21:18:58 +07:00
|
|
|
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-11-14 17:19:23 +07:00
|
|
|
ipm = kmemdup(&tmp, sizeof(tmp), GFP_ATOMIC);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (!ipm)
|
|
|
|
continue;
|
2016-06-16 21:18:58 +07:00
|
|
|
|
|
|
|
hash_add(card->ip_mc_htable,
|
|
|
|
&ipm->hnode, qeth_l3_ipaddr_hash(ipm));
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
2019-11-14 17:19:19 +07:00
|
|
|
read_unlock_bh(&in6_dev->lock);
|
|
|
|
|
2019-11-14 17:19:21 +07:00
|
|
|
out:
|
2019-11-14 17:19:22 +07:00
|
|
|
return 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2013-04-19 09:04:28 +07:00
|
|
|
static int qeth_l3_vlan_rx_add_vid(struct net_device *dev,
|
|
|
|
__be16 proto, u16 vid)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2008-07-26 16:24:10 +07:00
|
|
|
struct qeth_card *card = dev->ml_priv;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-11-14 17:19:22 +07:00
|
|
|
QETH_CARD_TEXT_(card, 4, "aid:%d", vid);
|
2011-12-09 07:52:37 +07:00
|
|
|
return 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2013-04-19 09:04:28 +07:00
|
|
|
static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev,
|
|
|
|
__be16 proto, u16 vid)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2008-07-26 16:24:10 +07:00
|
|
|
struct qeth_card *card = dev->ml_priv;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT_(card, 4, "kid:%d", vid);
|
2011-12-09 07:52:37 +07:00
|
|
|
return 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
s390/qeth: call dev_close() during recovery
When resetting an interface ("recovery"), qeth currently attempts to
elide the call to dev_close(). We initially only call .ndo_close to
quiesce the data path, and then offline & online the ccwgroup device.
If the reset succeeded, a call to .ndo_open then resumes the data path
along with some internal setup (dev_addr validation, RX modeset) that
dev_open() would have usually triggered.
dev_close() only gets called (via the close_dev worker) if the reset
action fails.
It's unclear whether this was initially done due to locking concerns, or
rather to execute the reset transparently. Either way, temporarily
closing the interface without dev_close() is fragile, and means we're
susceptible to various races and unexpected behaviour. For instance:
- Bypassing dev_deactivate_many() means that the qdiscs are not set to
__QDISC_STATE_DEACTIVATED. Consequently any intermittent TX completion
can wake up the txq, resulting in calls to .ndo_start_xmit while the
data path is down. We have custom state checking to detect this case
and drop such packets.
- Because the IFF_UP flag doesn't reflect the interface's actual state
during a reset, we have custom state checking in .ndo_open and
.ndo_close to guard against invalid calls.
- Considering that the reset might take a considerable amount of time
(in particular if an IO fails and we end up waiting for its timeout), we
_do_ want NETDEV_GOING_DOWN and NETDEV_DOWN events so that components
like bonding, team, bridge, macvlan, vlan, ... can take appropriate
action.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-03-01 00:59:39 +07:00
|
|
|
static void qeth_l3_stop_card(struct qeth_card *card)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2019-06-11 23:37:55 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "stopcard");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
qeth_set_allowed_threads(card, 0, 1);
|
2019-03-01 00:59:44 +07:00
|
|
|
|
2019-03-28 22:39:19 +07:00
|
|
|
cancel_work_sync(&card->rx_mode_work);
|
|
|
|
qeth_l3_drain_rx_mode_cache(card);
|
|
|
|
|
2010-01-11 09:50:50 +07:00
|
|
|
if (card->options.sniffer &&
|
|
|
|
(card->info.promisc_mode == SET_PROMISC_MODE_ON))
|
|
|
|
qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
|
2019-03-01 00:59:44 +07:00
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
if (card->state == CARD_STATE_SOFTSETUP) {
|
2016-06-16 21:18:58 +07:00
|
|
|
qeth_l3_clear_ip_htable(card, 1);
|
2008-02-15 15:19:42 +07:00
|
|
|
qeth_clear_ipacmd_list(card);
|
2019-04-17 23:17:28 +07:00
|
|
|
qeth_drain_output_queues(card);
|
2020-03-11 00:38:03 +07:00
|
|
|
cancel_delayed_work_sync(&card->buffer_reclaim_work);
|
2008-02-15 15:19:42 +07:00
|
|
|
card->state = CARD_STATE_DOWN;
|
|
|
|
}
|
2019-02-04 23:40:09 +07:00
|
|
|
|
2019-12-23 21:03:21 +07:00
|
|
|
qeth_qdio_clear_card(card, 0);
|
2020-01-25 22:53:00 +07:00
|
|
|
qeth_clear_working_pool_list(card);
|
2019-02-04 23:40:09 +07:00
|
|
|
flush_workqueue(card->event_wq);
|
2019-12-18 22:32:27 +07:00
|
|
|
card->info.promisc_mode = 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2019-08-20 21:46:41 +07:00
|
|
|
static void qeth_l3_set_promisc_mode(struct qeth_card *card)
|
2010-01-11 09:50:50 +07:00
|
|
|
{
|
2019-08-20 21:46:41 +07:00
|
|
|
bool enable = card->dev->flags & IFF_PROMISC;
|
2010-01-11 09:50:50 +07:00
|
|
|
|
2019-08-20 21:46:41 +07:00
|
|
|
if (card->info.promisc_mode == enable)
|
2010-01-11 09:50:50 +07:00
|
|
|
return;
|
|
|
|
|
2019-04-25 23:25:57 +07:00
|
|
|
if (IS_VM_NIC(card)) { /* Guestlan trace */
|
2010-01-11 09:50:50 +07:00
|
|
|
if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
|
2019-08-20 21:46:41 +07:00
|
|
|
qeth_setadp_promisc_mode(card, enable);
|
2010-01-11 09:50:50 +07:00
|
|
|
} else if (card->options.sniffer && /* HiperSockets trace */
|
|
|
|
qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) {
|
2019-08-20 21:46:41 +07:00
|
|
|
if (enable) {
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "+promisc");
|
2010-01-11 09:50:50 +07:00
|
|
|
qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_ENABLE);
|
|
|
|
} else {
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "-promisc");
|
2010-01-11 09:50:50 +07:00
|
|
|
qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-28 22:39:19 +07:00
|
|
|
static void qeth_l3_rx_mode_work(struct work_struct *work)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2019-03-28 22:39:19 +07:00
|
|
|
struct qeth_card *card = container_of(work, struct qeth_card,
|
|
|
|
rx_mode_work);
|
2017-12-21 02:11:02 +07:00
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
struct hlist_node *tmp;
|
|
|
|
int i, rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "setmulti");
|
2019-01-25 21:44:22 +07:00
|
|
|
|
2010-01-11 09:50:50 +07:00
|
|
|
if (!card->options.sniffer) {
|
2019-11-14 17:19:22 +07:00
|
|
|
rtnl_lock();
|
|
|
|
qeth_l3_add_mcast_rtnl(card->dev, 0, card);
|
2019-11-14 17:19:21 +07:00
|
|
|
if (qeth_is_supported(card, IPA_FULL_VLAN))
|
2019-11-14 17:19:22 +07:00
|
|
|
vlan_for_each(card->dev, qeth_l3_add_mcast_rtnl, card);
|
|
|
|
rtnl_unlock();
|
2017-12-21 02:11:02 +07:00
|
|
|
|
|
|
|
hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) {
|
|
|
|
switch (addr->disp_flag) {
|
|
|
|
case QETH_DISP_ADDR_DELETE:
|
|
|
|
rc = qeth_l3_deregister_addr_entry(card, addr);
|
s390/qeth: allow cmd callbacks to return errnos
Error propagation from cmd callbacks currently works in a way where
qeth_send_control_data_cb() picks the raw HW code from the response,
and the cmd's originator later translates this into an errno.
The callback itself only returns 0 ("done") or 1 ("expect more data").
This is
1. limiting, as the only means for the callback to report an internal
error is to invent pseudo HW codes (such as IPA_RC_ENOMEM), that
the originator then needs to understand. For non-IPA callbacks, we
even provide a separate field in the IO buffer metadata (iob->rc) so
the callback can pass back a return value.
2. fragile, as the originator must take care to not translate any errno
that is returned by qeth's own IO code paths (eg -ENOMEM). Also, any
originator that forgets to translate the HW codes potentially passes
garbage back to its caller. For instance, see
commit 2aa4867198c2 ("s390/qeth: translate SETVLAN/DELVLAN errors").
Introduce a new model where all HW error translation is done within the
callback, and the callback returns
> 0, if it expects more data (as before)
== 0, on success
< 0, with an errno
Start off with converting all callbacks to the new model that either
a) pass back pseudo HW codes, or b) have a dependency on a specific
HW error code. Also convert c) the one callback that uses iob->rc, and
d) qeth_setadpparms_change_macaddr_cb() so that it can pass back an
error back to qeth_l2_request_initial_mac() even when the cmd itself
was successful.
The old model remains supported: if the callback returns 0, we still
propagate the response's HW error code back to the originator.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-13 00:33:23 +07:00
|
|
|
if (!rc || rc == -ENOENT) {
|
2017-12-21 02:11:02 +07:00
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case QETH_DISP_ADDR_ADD:
|
|
|
|
rc = qeth_l3_register_addr_entry(card, addr);
|
s390/qeth: allow cmd callbacks to return errnos
Error propagation from cmd callbacks currently works in a way where
qeth_send_control_data_cb() picks the raw HW code from the response,
and the cmd's originator later translates this into an errno.
The callback itself only returns 0 ("done") or 1 ("expect more data").
This is
1. limiting, as the only means for the callback to report an internal
error is to invent pseudo HW codes (such as IPA_RC_ENOMEM), that
the originator then needs to understand. For non-IPA callbacks, we
even provide a separate field in the IO buffer metadata (iob->rc) so
the callback can pass back a return value.
2. fragile, as the originator must take care to not translate any errno
that is returned by qeth's own IO code paths (eg -ENOMEM). Also, any
originator that forgets to translate the HW codes potentially passes
garbage back to its caller. For instance, see
commit 2aa4867198c2 ("s390/qeth: translate SETVLAN/DELVLAN errors").
Introduce a new model where all HW error translation is done within the
callback, and the callback returns
> 0, if it expects more data (as before)
== 0, on success
< 0, with an errno
Start off with converting all callbacks to the new model that either
a) pass back pseudo HW codes, or b) have a dependency on a specific
HW error code. Also convert c) the one callback that uses iob->rc, and
d) qeth_setadpparms_change_macaddr_cb() so that it can pass back an
error back to qeth_l2_request_initial_mac() even when the cmd itself
was successful.
The old model remains supported: if the callback returns 0, we still
propagate the response's HW error code back to the originator.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-13 00:33:23 +07:00
|
|
|
if (rc && rc != -ENETDOWN) {
|
2017-12-21 02:11:02 +07:00
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
addr->ref_counter = 1;
|
|
|
|
/* fall through */
|
|
|
|
default:
|
|
|
|
/* for next call to set_rx_mode(): */
|
|
|
|
addr->disp_flag = QETH_DISP_ADDR_DELETE;
|
|
|
|
}
|
|
|
|
}
|
2010-01-11 09:50:50 +07:00
|
|
|
}
|
2019-08-20 21:46:41 +07:00
|
|
|
|
|
|
|
qeth_l3_set_promisc_mode(card);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2019-02-13 00:33:25 +07:00
|
|
|
static int qeth_l3_arp_makerc(u16 rc)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2018-11-03 01:04:08 +07:00
|
|
|
switch (rc) {
|
|
|
|
case IPA_RC_SUCCESS:
|
|
|
|
return 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
case QETH_IPA_ARP_RC_NOTSUPP:
|
|
|
|
case QETH_IPA_ARP_RC_Q_NOTSUPP:
|
2018-11-03 01:04:08 +07:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
case QETH_IPA_ARP_RC_OUT_OF_RANGE:
|
|
|
|
return -EINVAL;
|
2008-02-15 15:19:42 +07:00
|
|
|
case QETH_IPA_ARP_RC_Q_NO_DATA:
|
2018-11-03 01:04:08 +07:00
|
|
|
return -ENOENT;
|
2008-02-15 15:19:42 +07:00
|
|
|
default:
|
2018-11-03 01:04:08 +07:00
|
|
|
return -EIO;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-13 00:33:25 +07:00
|
|
|
static int qeth_l3_arp_cmd_cb(struct qeth_card *card, struct qeth_reply *reply,
|
|
|
|
unsigned long data)
|
|
|
|
{
|
|
|
|
struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
|
|
|
|
|
|
|
|
qeth_setassparms_cb(card, reply, data);
|
|
|
|
return qeth_l3_arp_makerc(cmd->hdr.return_code);
|
|
|
|
}
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
|
|
|
|
{
|
2019-02-13 00:33:25 +07:00
|
|
|
struct qeth_cmd_buffer *iob;
|
2008-02-15 15:19:42 +07:00
|
|
|
int rc;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "arpstnoe");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* currently GuestLAN only supports the ARP assist function
|
|
|
|
* IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_SET_NO_ENTRIES;
|
|
|
|
* thus we say EOPNOTSUPP for this ARP function
|
|
|
|
*/
|
2019-04-25 23:25:57 +07:00
|
|
|
if (IS_VM_NIC(card))
|
2008-02-15 15:19:42 +07:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2019-02-13 00:33:25 +07:00
|
|
|
|
|
|
|
iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
|
2019-06-27 22:01:24 +07:00
|
|
|
IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
|
|
|
|
SETASS_DATA_SIZEOF(flags_32bit),
|
2019-02-13 00:33:25 +07:00
|
|
|
QETH_PROT_IPV4);
|
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
__ipa_cmd(iob)->data.setassparms.data.flags_32bit = (u32) no_entries;
|
|
|
|
rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL);
|
2018-11-03 01:04:08 +07:00
|
|
|
if (rc)
|
|
|
|
QETH_DBF_MESSAGE(2, "Could not set number of ARP entries on device %x: %#x\n",
|
|
|
|
CARD_DEVID(card), rc);
|
2019-02-13 00:33:25 +07:00
|
|
|
return rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2010-12-08 09:57:58 +07:00
|
|
|
static __u32 get_arp_entry_size(struct qeth_card *card,
|
|
|
|
struct qeth_arp_query_data *qdata,
|
|
|
|
struct qeth_arp_entrytype *type, __u8 strip_entries)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2010-12-08 09:57:58 +07:00
|
|
|
__u32 rc;
|
|
|
|
__u8 is_hsi;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2010-12-08 09:57:58 +07:00
|
|
|
is_hsi = qdata->reply_bits == 5;
|
|
|
|
if (type->ip == QETHARP_IP_ADDR_V4) {
|
|
|
|
QETH_CARD_TEXT(card, 4, "arpev4");
|
|
|
|
if (strip_entries) {
|
|
|
|
rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5_short) :
|
|
|
|
sizeof(struct qeth_arp_qi_entry7_short);
|
|
|
|
} else {
|
|
|
|
rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5) :
|
|
|
|
sizeof(struct qeth_arp_qi_entry7);
|
|
|
|
}
|
|
|
|
} else if (type->ip == QETHARP_IP_ADDR_V6) {
|
|
|
|
QETH_CARD_TEXT(card, 4, "arpev6");
|
|
|
|
if (strip_entries) {
|
|
|
|
rc = is_hsi ?
|
|
|
|
sizeof(struct qeth_arp_qi_entry5_short_ipv6) :
|
|
|
|
sizeof(struct qeth_arp_qi_entry7_short_ipv6);
|
|
|
|
} else {
|
|
|
|
rc = is_hsi ?
|
|
|
|
sizeof(struct qeth_arp_qi_entry5_ipv6) :
|
|
|
|
sizeof(struct qeth_arp_qi_entry7_ipv6);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
QETH_CARD_TEXT(card, 4, "arpinv");
|
|
|
|
rc = 0;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
2010-12-08 09:57:58 +07:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int arpentry_matches_prot(struct qeth_arp_entrytype *type, __u16 prot)
|
|
|
|
{
|
|
|
|
return (type->ip == QETHARP_IP_ADDR_V4 && prot == QETH_PROT_IPV4) ||
|
|
|
|
(type->ip == QETHARP_IP_ADDR_V6 && prot == QETH_PROT_IPV6);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_arp_query_cb(struct qeth_card *card,
|
|
|
|
struct qeth_reply *reply, unsigned long data)
|
|
|
|
{
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
|
|
|
struct qeth_arp_query_data *qdata;
|
|
|
|
struct qeth_arp_query_info *qinfo;
|
2010-12-08 09:57:58 +07:00
|
|
|
int e;
|
|
|
|
int entrybytes_done;
|
|
|
|
int stripped_bytes;
|
|
|
|
__u8 do_strip_entries;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2010-12-08 09:57:58 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "arpquecb");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
qinfo = (struct qeth_arp_query_info *) reply->param;
|
|
|
|
cmd = (struct qeth_ipa_cmd *) data;
|
2010-12-08 09:57:58 +07:00
|
|
|
QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.prot_version);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (cmd->hdr.return_code) {
|
2010-12-08 09:57:58 +07:00
|
|
|
QETH_CARD_TEXT(card, 4, "arpcberr");
|
|
|
|
QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
|
s390/qeth: allow cmd callbacks to return errnos
Error propagation from cmd callbacks currently works in a way where
qeth_send_control_data_cb() picks the raw HW code from the response,
and the cmd's originator later translates this into an errno.
The callback itself only returns 0 ("done") or 1 ("expect more data").
This is
1. limiting, as the only means for the callback to report an internal
error is to invent pseudo HW codes (such as IPA_RC_ENOMEM), that
the originator then needs to understand. For non-IPA callbacks, we
even provide a separate field in the IO buffer metadata (iob->rc) so
the callback can pass back a return value.
2. fragile, as the originator must take care to not translate any errno
that is returned by qeth's own IO code paths (eg -ENOMEM). Also, any
originator that forgets to translate the HW codes potentially passes
garbage back to its caller. For instance, see
commit 2aa4867198c2 ("s390/qeth: translate SETVLAN/DELVLAN errors").
Introduce a new model where all HW error translation is done within the
callback, and the callback returns
> 0, if it expects more data (as before)
== 0, on success
< 0, with an errno
Start off with converting all callbacks to the new model that either
a) pass back pseudo HW codes, or b) have a dependency on a specific
HW error code. Also convert c) the one callback that uses iob->rc, and
d) qeth_setadpparms_change_macaddr_cb() so that it can pass back an
error back to qeth_l2_request_initial_mac() even when the cmd itself
was successful.
The old model remains supported: if the callback returns 0, we still
propagate the response's HW error code back to the originator.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-13 00:33:23 +07:00
|
|
|
return qeth_l3_arp_makerc(cmd->hdr.return_code);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
if (cmd->data.setassparms.hdr.return_code) {
|
|
|
|
cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
|
2010-12-08 09:57:58 +07:00
|
|
|
QETH_CARD_TEXT(card, 4, "setaperr");
|
|
|
|
QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
|
s390/qeth: allow cmd callbacks to return errnos
Error propagation from cmd callbacks currently works in a way where
qeth_send_control_data_cb() picks the raw HW code from the response,
and the cmd's originator later translates this into an errno.
The callback itself only returns 0 ("done") or 1 ("expect more data").
This is
1. limiting, as the only means for the callback to report an internal
error is to invent pseudo HW codes (such as IPA_RC_ENOMEM), that
the originator then needs to understand. For non-IPA callbacks, we
even provide a separate field in the IO buffer metadata (iob->rc) so
the callback can pass back a return value.
2. fragile, as the originator must take care to not translate any errno
that is returned by qeth's own IO code paths (eg -ENOMEM). Also, any
originator that forgets to translate the HW codes potentially passes
garbage back to its caller. For instance, see
commit 2aa4867198c2 ("s390/qeth: translate SETVLAN/DELVLAN errors").
Introduce a new model where all HW error translation is done within the
callback, and the callback returns
> 0, if it expects more data (as before)
== 0, on success
< 0, with an errno
Start off with converting all callbacks to the new model that either
a) pass back pseudo HW codes, or b) have a dependency on a specific
HW error code. Also convert c) the one callback that uses iob->rc, and
d) qeth_setadpparms_change_macaddr_cb() so that it can pass back an
error back to qeth_l2_request_initial_mac() even when the cmd itself
was successful.
The old model remains supported: if the callback returns 0, we still
propagate the response's HW error code back to the originator.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-13 00:33:23 +07:00
|
|
|
return qeth_l3_arp_makerc(cmd->hdr.return_code);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
qdata = &cmd->data.setassparms.data.query_arp;
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries);
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2010-12-08 09:57:58 +07:00
|
|
|
do_strip_entries = (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) > 0;
|
|
|
|
stripped_bytes = do_strip_entries ? QETH_QARP_MEDIASPECIFIC_BYTES : 0;
|
|
|
|
entrybytes_done = 0;
|
|
|
|
for (e = 0; e < qdata->no_entries; ++e) {
|
|
|
|
char *cur_entry;
|
|
|
|
__u32 esize;
|
|
|
|
struct qeth_arp_entrytype *etype;
|
|
|
|
|
|
|
|
cur_entry = &qdata->data + entrybytes_done;
|
|
|
|
etype = &((struct qeth_arp_qi_entry5 *) cur_entry)->type;
|
|
|
|
if (!arpentry_matches_prot(etype, cmd->hdr.prot_version)) {
|
|
|
|
QETH_CARD_TEXT(card, 4, "pmis");
|
|
|
|
QETH_CARD_TEXT_(card, 4, "%i", etype->ip);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
esize = get_arp_entry_size(card, qdata, etype,
|
|
|
|
do_strip_entries);
|
|
|
|
QETH_CARD_TEXT_(card, 5, "esz%i", esize);
|
|
|
|
if (!esize)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if ((qinfo->udata_len - qinfo->udata_offset) < esize) {
|
s390/qeth: allow cmd callbacks to return errnos
Error propagation from cmd callbacks currently works in a way where
qeth_send_control_data_cb() picks the raw HW code from the response,
and the cmd's originator later translates this into an errno.
The callback itself only returns 0 ("done") or 1 ("expect more data").
This is
1. limiting, as the only means for the callback to report an internal
error is to invent pseudo HW codes (such as IPA_RC_ENOMEM), that
the originator then needs to understand. For non-IPA callbacks, we
even provide a separate field in the IO buffer metadata (iob->rc) so
the callback can pass back a return value.
2. fragile, as the originator must take care to not translate any errno
that is returned by qeth's own IO code paths (eg -ENOMEM). Also, any
originator that forgets to translate the HW codes potentially passes
garbage back to its caller. For instance, see
commit 2aa4867198c2 ("s390/qeth: translate SETVLAN/DELVLAN errors").
Introduce a new model where all HW error translation is done within the
callback, and the callback returns
> 0, if it expects more data (as before)
== 0, on success
< 0, with an errno
Start off with converting all callbacks to the new model that either
a) pass back pseudo HW codes, or b) have a dependency on a specific
HW error code. Also convert c) the one callback that uses iob->rc, and
d) qeth_setadpparms_change_macaddr_cb() so that it can pass back an
error back to qeth_l2_request_initial_mac() even when the cmd itself
was successful.
The old model remains supported: if the callback returns 0, we still
propagate the response's HW error code back to the originator.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-13 00:33:23 +07:00
|
|
|
QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOSPC);
|
|
|
|
memset(qinfo->udata, 0, 4);
|
|
|
|
return -ENOSPC;
|
2010-12-08 09:57:58 +07:00
|
|
|
}
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2010-12-08 09:57:58 +07:00
|
|
|
memcpy(qinfo->udata + qinfo->udata_offset,
|
|
|
|
&qdata->data + entrybytes_done + stripped_bytes,
|
|
|
|
esize);
|
|
|
|
entrybytes_done += esize + stripped_bytes;
|
|
|
|
qinfo->udata_offset += esize;
|
|
|
|
++qinfo->no_entries;
|
|
|
|
}
|
2008-02-15 15:19:42 +07:00
|
|
|
/* check if all replies received ... */
|
|
|
|
if (cmd->data.setassparms.hdr.seq_no <
|
|
|
|
cmd->data.setassparms.hdr.number_of_replies)
|
|
|
|
return 1;
|
2010-12-08 09:57:58 +07:00
|
|
|
QETH_CARD_TEXT_(card, 4, "nove%i", qinfo->no_entries);
|
2008-02-15 15:19:42 +07:00
|
|
|
memcpy(qinfo->udata, &qinfo->no_entries, 4);
|
|
|
|
/* keep STRIP_ENTRIES flag so the user program can distinguish
|
|
|
|
* stripped entries from normal ones */
|
|
|
|
if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
|
|
|
|
qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES;
|
|
|
|
memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2);
|
2010-12-08 09:57:58 +07:00
|
|
|
QETH_CARD_TEXT_(card, 4, "rc%i", 0);
|
2008-02-15 15:19:42 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-08 09:57:58 +07:00
|
|
|
static int qeth_l3_query_arp_cache_info(struct qeth_card *card,
|
|
|
|
enum qeth_prot_versions prot,
|
|
|
|
struct qeth_arp_query_info *qinfo)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
|
|
|
struct qeth_cmd_buffer *iob;
|
2010-12-08 09:57:58 +07:00
|
|
|
struct qeth_ipa_cmd *cmd;
|
2008-02-15 15:19:42 +07:00
|
|
|
int rc;
|
|
|
|
|
2010-12-08 09:57:58 +07:00
|
|
|
QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot);
|
|
|
|
|
2015-12-11 18:27:54 +07:00
|
|
|
iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
|
|
|
|
IPA_CMD_ASS_ARP_QUERY_INFO,
|
2019-06-27 22:01:24 +07:00
|
|
|
SETASS_DATA_SIZEOF(query_arp), prot);
|
2015-01-21 19:39:10 +07:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2018-03-10 00:12:52 +07:00
|
|
|
cmd = __ipa_cmd(iob);
|
2010-12-08 09:57:58 +07:00
|
|
|
cmd->data.setassparms.data.query_arp.request_bits = 0x000F;
|
2019-02-13 00:33:16 +07:00
|
|
|
rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_query_cb, qinfo);
|
2018-11-03 01:04:08 +07:00
|
|
|
if (rc)
|
|
|
|
QETH_DBF_MESSAGE(2, "Error while querying ARP cache on device %x: %#x\n",
|
|
|
|
CARD_DEVID(card), rc);
|
s390/qeth: allow cmd callbacks to return errnos
Error propagation from cmd callbacks currently works in a way where
qeth_send_control_data_cb() picks the raw HW code from the response,
and the cmd's originator later translates this into an errno.
The callback itself only returns 0 ("done") or 1 ("expect more data").
This is
1. limiting, as the only means for the callback to report an internal
error is to invent pseudo HW codes (such as IPA_RC_ENOMEM), that
the originator then needs to understand. For non-IPA callbacks, we
even provide a separate field in the IO buffer metadata (iob->rc) so
the callback can pass back a return value.
2. fragile, as the originator must take care to not translate any errno
that is returned by qeth's own IO code paths (eg -ENOMEM). Also, any
originator that forgets to translate the HW codes potentially passes
garbage back to its caller. For instance, see
commit 2aa4867198c2 ("s390/qeth: translate SETVLAN/DELVLAN errors").
Introduce a new model where all HW error translation is done within the
callback, and the callback returns
> 0, if it expects more data (as before)
== 0, on success
< 0, with an errno
Start off with converting all callbacks to the new model that either
a) pass back pseudo HW codes, or b) have a dependency on a specific
HW error code. Also convert c) the one callback that uses iob->rc, and
d) qeth_setadpparms_change_macaddr_cb() so that it can pass back an
error back to qeth_l2_request_initial_mac() even when the cmd itself
was successful.
The old model remains supported: if the callback returns 0, we still
propagate the response's HW error code back to the originator.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-02-13 00:33:23 +07:00
|
|
|
return rc;
|
2010-12-08 09:57:58 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata)
|
|
|
|
{
|
|
|
|
struct qeth_arp_query_info qinfo = {0, };
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "arpquery");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/
|
|
|
|
IPA_ARP_PROCESSING)) {
|
2010-12-08 09:57:58 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "arpqnsup");
|
|
|
|
rc = -EOPNOTSUPP;
|
|
|
|
goto out;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
/* get size of userspace buffer and mask_bits -> 6 bytes */
|
2010-12-08 09:57:58 +07:00
|
|
|
if (copy_from_user(&qinfo, udata, 6)) {
|
|
|
|
rc = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-02-15 15:19:42 +07:00
|
|
|
qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL);
|
2010-12-08 09:57:58 +07:00
|
|
|
if (!qinfo.udata) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-02-15 15:19:42 +07:00
|
|
|
qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET;
|
2010-12-08 09:57:58 +07:00
|
|
|
rc = qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV4, &qinfo);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc) {
|
|
|
|
if (copy_to_user(udata, qinfo.udata, 4))
|
|
|
|
rc = -EFAULT;
|
2016-06-16 21:19:03 +07:00
|
|
|
goto free_and_out;
|
|
|
|
}
|
|
|
|
if (qinfo.mask_bits & QETH_QARP_WITH_IPV6) {
|
|
|
|
/* fails in case of GuestLAN QDIO mode */
|
|
|
|
qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV6, &qinfo);
|
|
|
|
}
|
|
|
|
if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) {
|
|
|
|
QETH_CARD_TEXT(card, 4, "qactf");
|
|
|
|
rc = -EFAULT;
|
|
|
|
goto free_and_out;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
2016-06-16 21:19:03 +07:00
|
|
|
QETH_CARD_TEXT(card, 4, "qacts");
|
|
|
|
|
2010-12-08 09:57:58 +07:00
|
|
|
free_and_out:
|
2008-02-15 15:19:42 +07:00
|
|
|
kfree(qinfo.udata);
|
2010-12-08 09:57:58 +07:00
|
|
|
out:
|
2008-02-15 15:19:42 +07:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-11-03 01:04:12 +07:00
|
|
|
static int qeth_l3_arp_modify_entry(struct qeth_card *card,
|
|
|
|
struct qeth_arp_cache_entry *entry,
|
|
|
|
enum qeth_arp_process_subcmds arp_cmd)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2018-11-03 01:04:12 +07:00
|
|
|
struct qeth_arp_cache_entry *cmd_entry;
|
2008-02-15 15:19:42 +07:00
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
int rc;
|
|
|
|
|
2018-11-03 01:04:12 +07:00
|
|
|
if (arp_cmd == IPA_CMD_ASS_ARP_ADD_ENTRY)
|
|
|
|
QETH_CARD_TEXT(card, 3, "arpadd");
|
|
|
|
else
|
|
|
|
QETH_CARD_TEXT(card, 3, "arpdel");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* currently GuestLAN only supports the ARP assist function
|
|
|
|
* IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_ADD_ENTRY;
|
|
|
|
* thus we say EOPNOTSUPP for this ARP function
|
|
|
|
*/
|
2019-04-25 23:25:57 +07:00
|
|
|
if (IS_VM_NIC(card))
|
2008-02-15 15:19:42 +07:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2018-11-03 01:04:12 +07:00
|
|
|
iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, arp_cmd,
|
2019-06-27 22:01:24 +07:00
|
|
|
SETASS_DATA_SIZEOF(arp_entry),
|
|
|
|
QETH_PROT_IPV4);
|
2015-01-21 19:39:10 +07:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2018-11-03 01:04:12 +07:00
|
|
|
cmd_entry = &__ipa_cmd(iob)->data.setassparms.data.arp_entry;
|
|
|
|
ether_addr_copy(cmd_entry->macaddr, entry->macaddr);
|
|
|
|
memcpy(cmd_entry->ipaddr, entry->ipaddr, 4);
|
2019-02-13 00:33:25 +07:00
|
|
|
rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL);
|
2018-11-03 01:04:08 +07:00
|
|
|
if (rc)
|
2018-11-03 01:04:12 +07:00
|
|
|
QETH_DBF_MESSAGE(2, "Could not modify (cmd: %#x) ARP entry on device %x: %#x\n",
|
|
|
|
arp_cmd, CARD_DEVID(card), rc);
|
2019-02-13 00:33:25 +07:00
|
|
|
return rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_arp_flush_cache(struct qeth_card *card)
|
|
|
|
{
|
2019-02-13 00:33:25 +07:00
|
|
|
struct qeth_cmd_buffer *iob;
|
2008-02-15 15:19:42 +07:00
|
|
|
int rc;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "arpflush");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* currently GuestLAN only supports the ARP assist function
|
|
|
|
* IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_FLUSH_CACHE;
|
|
|
|
* thus we say EOPNOTSUPP for this ARP function
|
|
|
|
*/
|
2019-04-25 23:25:57 +07:00
|
|
|
if (IS_VM_NIC(card) || IS_IQD(card))
|
2008-02-15 15:19:42 +07:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2019-02-13 00:33:25 +07:00
|
|
|
|
|
|
|
iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
|
|
|
|
IPA_CMD_ASS_ARP_FLUSH_CACHE, 0,
|
|
|
|
QETH_PROT_IPV4);
|
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL);
|
2018-11-03 01:04:08 +07:00
|
|
|
if (rc)
|
|
|
|
QETH_DBF_MESSAGE(2, "Could not flush ARP cache on device %x: %#x\n",
|
|
|
|
CARD_DEVID(card), rc);
|
2019-02-13 00:33:25 +07:00
|
|
|
return rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
|
|
|
{
|
2008-07-26 16:24:10 +07:00
|
|
|
struct qeth_card *card = dev->ml_priv;
|
2008-02-15 15:19:42 +07:00
|
|
|
struct qeth_arp_cache_entry arp_entry;
|
2018-11-03 01:04:12 +07:00
|
|
|
enum qeth_arp_process_subcmds arp_cmd;
|
2008-02-15 15:19:42 +07:00
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SIOC_QETH_ARP_SET_NO_ENTRIES:
|
|
|
|
if (!capable(CAP_NET_ADMIN)) {
|
|
|
|
rc = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rc = qeth_l3_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue);
|
|
|
|
break;
|
|
|
|
case SIOC_QETH_ARP_QUERY_INFO:
|
|
|
|
if (!capable(CAP_NET_ADMIN)) {
|
|
|
|
rc = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rc = qeth_l3_arp_query(card, rq->ifr_ifru.ifru_data);
|
|
|
|
break;
|
|
|
|
case SIOC_QETH_ARP_ADD_ENTRY:
|
|
|
|
case SIOC_QETH_ARP_REMOVE_ENTRY:
|
2018-11-03 01:04:12 +07:00
|
|
|
if (!capable(CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
if (copy_from_user(&arp_entry, rq->ifr_data, sizeof(arp_entry)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
arp_cmd = (cmd == SIOC_QETH_ARP_ADD_ENTRY) ?
|
|
|
|
IPA_CMD_ASS_ARP_ADD_ENTRY :
|
|
|
|
IPA_CMD_ASS_ARP_REMOVE_ENTRY;
|
|
|
|
return qeth_l3_arp_modify_entry(card, &arp_entry, arp_cmd);
|
2008-02-15 15:19:42 +07:00
|
|
|
case SIOC_QETH_ARP_FLUSH_CACHE:
|
|
|
|
if (!capable(CAP_NET_ADMIN)) {
|
|
|
|
rc = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rc = qeth_l3_arp_flush_cache(card);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rc = -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-06-27 22:01:32 +07:00
|
|
|
static int qeth_l3_get_cast_type_rcu(struct sk_buff *skb, struct dst_entry *dst,
|
|
|
|
int ipv)
|
2009-08-26 09:01:08 +07:00
|
|
|
{
|
2011-07-18 13:09:49 +07:00
|
|
|
struct neighbour *n = NULL;
|
|
|
|
|
2019-06-27 22:01:32 +07:00
|
|
|
if (dst)
|
|
|
|
n = dst_neigh_lookup_skb(dst, skb);
|
2019-06-05 18:48:49 +07:00
|
|
|
|
2011-07-18 13:09:49 +07:00
|
|
|
if (n) {
|
2017-12-21 02:11:04 +07:00
|
|
|
int cast_type = n->type;
|
|
|
|
|
2012-07-03 12:02:33 +07:00
|
|
|
neigh_release(n);
|
2009-08-26 09:01:08 +07:00
|
|
|
if ((cast_type == RTN_BROADCAST) ||
|
|
|
|
(cast_type == RTN_MULTICAST) ||
|
|
|
|
(cast_type == RTN_ANYCAST))
|
|
|
|
return cast_type;
|
2018-07-11 22:42:41 +07:00
|
|
|
return RTN_UNICAST;
|
2009-08-26 09:01:08 +07:00
|
|
|
}
|
2011-11-15 09:31:15 +07:00
|
|
|
|
2017-12-21 02:11:04 +07:00
|
|
|
/* no neighbour (eg AF_PACKET), fall back to target's IP address ... */
|
2019-06-05 18:48:49 +07:00
|
|
|
switch (ipv) {
|
2018-09-17 22:35:56 +07:00
|
|
|
case 4:
|
2019-06-05 18:48:48 +07:00
|
|
|
if (ipv4_is_lbcast(ip_hdr(skb)->daddr))
|
|
|
|
return RTN_BROADCAST;
|
2017-12-21 02:11:04 +07:00
|
|
|
return ipv4_is_multicast(ip_hdr(skb)->daddr) ?
|
2018-07-11 22:42:41 +07:00
|
|
|
RTN_MULTICAST : RTN_UNICAST;
|
2018-09-17 22:35:56 +07:00
|
|
|
case 6:
|
|
|
|
return ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ?
|
|
|
|
RTN_MULTICAST : RTN_UNICAST;
|
|
|
|
default:
|
|
|
|
/* ... and MAC address */
|
2019-04-25 23:26:00 +07:00
|
|
|
return qeth_get_ether_cast_type(skb);
|
2018-09-17 22:35:56 +07:00
|
|
|
}
|
2009-08-26 09:01:08 +07:00
|
|
|
}
|
|
|
|
|
2019-06-27 22:01:32 +07:00
|
|
|
static int qeth_l3_get_cast_type(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
int ipv = qeth_get_ip_version(skb);
|
|
|
|
struct dst_entry *dst;
|
|
|
|
int cast_type;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
dst = qeth_dst_check_rcu(skb, ipv);
|
|
|
|
cast_type = qeth_l3_get_cast_type_rcu(skb, dst, ipv);
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return cast_type;
|
|
|
|
}
|
|
|
|
|
2017-12-21 02:11:07 +07:00
|
|
|
static u8 qeth_l3_cast_type_to_flag(int cast_type)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2017-12-21 02:11:07 +07:00
|
|
|
if (cast_type == RTN_MULTICAST)
|
|
|
|
return QETH_CAST_MULTICAST;
|
|
|
|
if (cast_type == RTN_ANYCAST)
|
|
|
|
return QETH_CAST_ANYCAST;
|
|
|
|
if (cast_type == RTN_BROADCAST)
|
|
|
|
return QETH_CAST_BROADCAST;
|
|
|
|
return QETH_CAST_UNICAST;
|
|
|
|
}
|
2011-07-18 13:09:49 +07:00
|
|
|
|
2019-02-16 01:22:29 +07:00
|
|
|
static void qeth_l3_fill_header(struct qeth_qdio_out_q *queue,
|
|
|
|
struct qeth_hdr *hdr, struct sk_buff *skb,
|
2019-06-27 22:01:33 +07:00
|
|
|
int ipv, unsigned int data_len)
|
2017-12-21 02:11:07 +07:00
|
|
|
{
|
2018-11-08 21:06:16 +07:00
|
|
|
struct qeth_hdr_layer3 *l3_hdr = &hdr->hdr.l3;
|
2018-09-17 22:35:56 +07:00
|
|
|
struct vlan_ethhdr *veth = vlan_eth_hdr(skb);
|
2019-02-16 01:22:29 +07:00
|
|
|
struct qeth_card *card = queue->card;
|
2019-06-05 18:48:49 +07:00
|
|
|
struct dst_entry *dst;
|
2019-06-27 22:01:33 +07:00
|
|
|
int cast_type;
|
2018-09-17 22:35:56 +07:00
|
|
|
|
2017-12-21 02:11:08 +07:00
|
|
|
hdr->hdr.l3.length = data_len;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2018-09-17 22:36:02 +07:00
|
|
|
if (skb_is_gso(skb)) {
|
2018-10-12 22:27:15 +07:00
|
|
|
hdr->hdr.l3.id = QETH_HEADER_TYPE_L3_TSO;
|
2018-09-17 22:36:02 +07:00
|
|
|
} else {
|
|
|
|
hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
|
2018-11-08 21:06:16 +07:00
|
|
|
|
|
|
|
if (skb->protocol == htons(ETH_P_AF_IUCV)) {
|
|
|
|
l3_hdr->flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST;
|
|
|
|
l3_hdr->next_hop.ipv6_addr.s6_addr16[0] = htons(0xfe80);
|
|
|
|
memcpy(&l3_hdr->next_hop.ipv6_addr.s6_addr32[2],
|
|
|
|
iucv_trans_hdr(skb)->destUserID, 8);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-09-17 22:36:02 +07:00
|
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
|
|
|
qeth_tx_csum(skb, &hdr->hdr.l3.ext_flags, ipv);
|
|
|
|
/* some HW requires combined L3+L4 csum offload: */
|
|
|
|
if (ipv == 4)
|
|
|
|
hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_HDR_REQ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-17 22:35:56 +07:00
|
|
|
if (ipv == 4 || IS_IQD(card)) {
|
|
|
|
/* NETIF_F_HW_VLAN_CTAG_TX */
|
|
|
|
if (skb_vlan_tag_present(skb)) {
|
|
|
|
hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_VLAN_FRAME;
|
|
|
|
hdr->hdr.l3.vlan_id = skb_vlan_tag_get(skb);
|
|
|
|
}
|
|
|
|
} else if (veth->h_vlan_proto == htons(ETH_P_8021Q)) {
|
|
|
|
hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_INCLUDE_VLAN_TAG;
|
|
|
|
hdr->hdr.l3.vlan_id = ntohs(veth->h_vlan_TCI);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2011-11-15 09:31:15 +07:00
|
|
|
rcu_read_lock();
|
2019-06-27 22:01:32 +07:00
|
|
|
dst = qeth_dst_check_rcu(skb, ipv);
|
2019-06-05 18:48:49 +07:00
|
|
|
|
2019-06-27 22:01:33 +07:00
|
|
|
if (IS_IQD(card) && skb_get_queue_mapping(skb) != QETH_IQD_MCAST_TXQ)
|
|
|
|
cast_type = RTN_UNICAST;
|
|
|
|
else
|
|
|
|
cast_type = qeth_l3_get_cast_type_rcu(skb, dst, ipv);
|
|
|
|
l3_hdr->flags |= qeth_l3_cast_type_to_flag(cast_type);
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
if (ipv == 4) {
|
2019-06-27 22:01:32 +07:00
|
|
|
struct rtable *rt = (struct rtable *) dst;
|
2012-02-01 17:49:17 +07:00
|
|
|
|
2017-12-21 02:11:07 +07:00
|
|
|
*((__be32 *) &hdr->hdr.l3.next_hop.ipv4.addr) = (rt) ?
|
|
|
|
rt_nexthop(rt, ip_hdr(skb)->daddr) :
|
|
|
|
ip_hdr(skb)->daddr;
|
2019-06-27 22:01:33 +07:00
|
|
|
} else if (ipv == 6) {
|
2019-06-27 22:01:32 +07:00
|
|
|
struct rt6_info *rt = (struct rt6_info *) dst;
|
2012-02-01 17:49:17 +07:00
|
|
|
|
2017-08-07 18:28:39 +07:00
|
|
|
if (rt && !ipv6_addr_any(&rt->rt6i_gateway))
|
2018-11-08 21:06:16 +07:00
|
|
|
l3_hdr->next_hop.ipv6_addr = rt->rt6i_gateway;
|
2017-12-21 02:11:07 +07:00
|
|
|
else
|
2018-11-08 21:06:16 +07:00
|
|
|
l3_hdr->next_hop.ipv6_addr = ipv6_hdr(skb)->daddr;
|
2012-02-01 17:49:17 +07:00
|
|
|
|
2017-12-21 02:11:07 +07:00
|
|
|
hdr->hdr.l3.flags |= QETH_HDR_IPV6;
|
2019-04-25 23:25:57 +07:00
|
|
|
if (!IS_IQD(card))
|
2017-12-21 02:11:07 +07:00
|
|
|
hdr->hdr.l3.flags |= QETH_HDR_PASSTHRU;
|
2019-06-27 22:01:33 +07:00
|
|
|
} else {
|
|
|
|
/* OSA only: */
|
|
|
|
l3_hdr->flags |= QETH_HDR_PASSTHRU;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
2011-11-15 09:31:15 +07:00
|
|
|
rcu_read_unlock();
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2018-09-17 22:36:00 +07:00
|
|
|
static void qeth_l3_fixup_headers(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct iphdr *iph = ip_hdr(skb);
|
|
|
|
|
|
|
|
/* this is safe, IPv6 traffic takes a different path */
|
|
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL)
|
|
|
|
iph->check = 0;
|
2018-09-17 22:36:02 +07:00
|
|
|
if (skb_is_gso(skb)) {
|
|
|
|
iph->tot_len = 0;
|
|
|
|
tcp_hdr(skb)->check = ~tcp_v4_check(0, iph->saddr,
|
|
|
|
iph->daddr, 0);
|
|
|
|
}
|
2018-09-17 22:36:00 +07:00
|
|
|
}
|
|
|
|
|
2018-09-17 22:36:03 +07:00
|
|
|
static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb,
|
2019-06-27 22:01:33 +07:00
|
|
|
struct qeth_qdio_out_q *queue, int ipv)
|
2018-07-11 22:42:46 +07:00
|
|
|
{
|
2018-11-08 21:06:17 +07:00
|
|
|
unsigned int hw_hdr_len;
|
|
|
|
int rc;
|
2018-09-17 22:36:02 +07:00
|
|
|
|
2018-07-11 22:42:46 +07:00
|
|
|
/* re-use the L2 header area for the HW header: */
|
2018-11-08 21:06:17 +07:00
|
|
|
hw_hdr_len = skb_is_gso(skb) ? sizeof(struct qeth_hdr_tso) :
|
|
|
|
sizeof(struct qeth_hdr);
|
2018-07-11 22:42:46 +07:00
|
|
|
rc = skb_cow_head(skb, hw_hdr_len - ETH_HLEN);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
skb_pull(skb, ETH_HLEN);
|
|
|
|
|
2018-09-17 22:36:00 +07:00
|
|
|
qeth_l3_fixup_headers(skb);
|
2019-06-27 22:01:33 +07:00
|
|
|
return qeth_xmit(card, skb, queue, ipv, qeth_l3_fill_header);
|
2018-07-11 22:42:46 +07:00
|
|
|
}
|
|
|
|
|
2018-07-11 22:42:45 +07:00
|
|
|
static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
|
|
|
|
struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = dev->ml_priv;
|
2019-04-17 23:17:32 +07:00
|
|
|
u16 txq = skb_get_queue_mapping(skb);
|
2018-07-11 22:42:45 +07:00
|
|
|
int ipv = qeth_get_ip_version(skb);
|
|
|
|
struct qeth_qdio_out_q *queue;
|
2019-06-27 22:01:33 +07:00
|
|
|
int rc;
|
2019-02-16 01:22:29 +07:00
|
|
|
|
2019-08-23 16:48:49 +07:00
|
|
|
if (!skb_is_gso(skb))
|
|
|
|
qdisc_skb_cb(skb)->pkt_len = skb->len;
|
2018-07-11 22:42:45 +07:00
|
|
|
if (IS_IQD(card)) {
|
2019-04-17 23:17:32 +07:00
|
|
|
queue = card->qdio.out_qs[qeth_iqd_translate_txq(dev, txq)];
|
|
|
|
|
2018-07-11 22:42:45 +07:00
|
|
|
if (card->options.sniffer)
|
|
|
|
goto tx_drop;
|
|
|
|
if ((card->options.cq != QETH_CQ_ENABLED && !ipv) ||
|
|
|
|
(card->options.cq == QETH_CQ_ENABLED &&
|
|
|
|
skb->protocol != htons(ETH_P_AF_IUCV)))
|
2008-02-15 15:19:42 +07:00
|
|
|
goto tx_drop;
|
2019-04-17 23:17:32 +07:00
|
|
|
} else {
|
2019-04-17 23:17:33 +07:00
|
|
|
queue = card->qdio.out_qs[txq];
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2019-06-27 22:01:33 +07:00
|
|
|
if (!(dev->flags & IFF_BROADCAST) &&
|
|
|
|
qeth_l3_get_cast_type(skb) == RTN_BROADCAST)
|
2018-07-11 22:42:45 +07:00
|
|
|
goto tx_drop;
|
|
|
|
|
2018-09-17 22:36:03 +07:00
|
|
|
if (ipv == 4 || IS_IQD(card))
|
2019-06-27 22:01:33 +07:00
|
|
|
rc = qeth_l3_xmit(card, skb, queue, ipv);
|
2018-09-17 22:35:56 +07:00
|
|
|
else
|
2019-06-27 22:01:33 +07:00
|
|
|
rc = qeth_xmit(card, skb, queue, ipv, qeth_l3_fill_header);
|
2018-07-11 22:42:45 +07:00
|
|
|
|
2019-08-23 16:48:49 +07:00
|
|
|
if (!rc)
|
2018-07-11 22:42:45 +07:00
|
|
|
return NETDEV_TX_OK;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
tx_drop:
|
2019-02-16 01:22:29 +07:00
|
|
|
QETH_TXQ_STAT_INC(queue, tx_dropped);
|
2019-03-18 22:40:56 +07:00
|
|
|
kfree_skb(skb);
|
2008-02-15 15:19:42 +07:00
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
|
2019-03-28 22:39:19 +07:00
|
|
|
static void qeth_l3_set_rx_mode(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = dev->ml_priv;
|
|
|
|
|
|
|
|
schedule_work(&card->rx_mode_work);
|
|
|
|
}
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
/*
|
|
|
|
* we need NOARP for IPv4 but we want neighbor solicitation for IPv6. Setting
|
|
|
|
* NOARP on the netdevice is no option because it also turns off neighbor
|
|
|
|
* solicitation. For IPv4 we install a neighbor_setup function. We don't want
|
|
|
|
* arp resolution but we want the hard header (packet socket will work
|
|
|
|
* e.g. tcpdump)
|
|
|
|
*/
|
|
|
|
static int qeth_l3_neigh_setup_noarp(struct neighbour *n)
|
|
|
|
{
|
|
|
|
n->nud_state = NUD_NOARP;
|
|
|
|
memcpy(n->ha, "FAKELL", 6);
|
|
|
|
n->output = n->ops->connected_output;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
qeth_l3_neigh_setup(struct net_device *dev, struct neigh_parms *np)
|
|
|
|
{
|
|
|
|
if (np->tbl->family == AF_INET)
|
|
|
|
np->neigh_setup = qeth_l3_neigh_setup_noarp;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-17 22:35:56 +07:00
|
|
|
static netdev_features_t qeth_l3_osa_features_check(struct sk_buff *skb,
|
|
|
|
struct net_device *dev,
|
|
|
|
netdev_features_t features)
|
|
|
|
{
|
|
|
|
if (qeth_get_ip_version(skb) != 4)
|
|
|
|
features &= ~NETIF_F_HW_VLAN_CTAG_TX;
|
|
|
|
return qeth_features_check(skb, dev, features);
|
|
|
|
}
|
|
|
|
|
2019-04-17 23:17:32 +07:00
|
|
|
static u16 qeth_l3_iqd_select_queue(struct net_device *dev, struct sk_buff *skb,
|
|
|
|
struct net_device *sb_dev)
|
|
|
|
{
|
|
|
|
return qeth_iqd_select_queue(dev, skb, qeth_l3_get_cast_type(skb),
|
|
|
|
sb_dev);
|
|
|
|
}
|
|
|
|
|
2019-04-17 23:17:33 +07:00
|
|
|
static u16 qeth_l3_osa_select_queue(struct net_device *dev, struct sk_buff *skb,
|
|
|
|
struct net_device *sb_dev)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = dev->ml_priv;
|
|
|
|
|
2020-03-18 19:54:47 +07:00
|
|
|
return IS_VM_NIC(card) ? netdev_pick_tx(dev, skb, sb_dev) :
|
|
|
|
qeth_get_priority_queue(card, skb);
|
2019-04-17 23:17:33 +07:00
|
|
|
}
|
|
|
|
|
2009-01-09 10:44:00 +07:00
|
|
|
static const struct net_device_ops qeth_l3_netdev_ops = {
|
2019-01-25 21:44:18 +07:00
|
|
|
.ndo_open = qeth_open,
|
|
|
|
.ndo_stop = qeth_stop,
|
2019-02-16 01:22:29 +07:00
|
|
|
.ndo_get_stats64 = qeth_get_stats64,
|
2009-01-09 01:50:55 +07:00
|
|
|
.ndo_start_xmit = qeth_l3_hard_start_xmit,
|
2019-04-17 23:17:32 +07:00
|
|
|
.ndo_select_queue = qeth_l3_iqd_select_queue,
|
2009-01-09 01:50:55 +07:00
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
2017-12-21 02:11:02 +07:00
|
|
|
.ndo_set_rx_mode = qeth_l3_set_rx_mode,
|
2017-04-11 21:11:10 +07:00
|
|
|
.ndo_do_ioctl = qeth_do_ioctl,
|
2016-06-16 21:18:59 +07:00
|
|
|
.ndo_fix_features = qeth_fix_features,
|
|
|
|
.ndo_set_features = qeth_set_features,
|
2009-01-09 01:50:55 +07:00
|
|
|
.ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid,
|
|
|
|
.ndo_vlan_rx_kill_vid = qeth_l3_vlan_rx_kill_vid,
|
2011-07-20 11:54:41 +07:00
|
|
|
.ndo_tx_timeout = qeth_tx_timeout,
|
2009-01-09 01:50:55 +07:00
|
|
|
};
|
|
|
|
|
2009-01-09 10:44:00 +07:00
|
|
|
static const struct net_device_ops qeth_l3_osa_netdev_ops = {
|
2019-01-25 21:44:18 +07:00
|
|
|
.ndo_open = qeth_open,
|
|
|
|
.ndo_stop = qeth_stop,
|
2019-02-16 01:22:29 +07:00
|
|
|
.ndo_get_stats64 = qeth_get_stats64,
|
2009-01-09 10:44:00 +07:00
|
|
|
.ndo_start_xmit = qeth_l3_hard_start_xmit,
|
2018-09-17 22:35:56 +07:00
|
|
|
.ndo_features_check = qeth_l3_osa_features_check,
|
2019-04-17 23:17:33 +07:00
|
|
|
.ndo_select_queue = qeth_l3_osa_select_queue,
|
2009-01-09 10:44:00 +07:00
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
2017-12-21 02:11:02 +07:00
|
|
|
.ndo_set_rx_mode = qeth_l3_set_rx_mode,
|
2017-04-11 21:11:10 +07:00
|
|
|
.ndo_do_ioctl = qeth_do_ioctl,
|
2016-06-16 21:18:59 +07:00
|
|
|
.ndo_fix_features = qeth_fix_features,
|
|
|
|
.ndo_set_features = qeth_set_features,
|
2009-01-09 10:44:00 +07:00
|
|
|
.ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid,
|
|
|
|
.ndo_vlan_rx_kill_vid = qeth_l3_vlan_rx_kill_vid,
|
2011-07-20 11:54:41 +07:00
|
|
|
.ndo_tx_timeout = qeth_tx_timeout,
|
2009-01-09 10:44:00 +07:00
|
|
|
.ndo_neigh_setup = qeth_l3_neigh_setup,
|
|
|
|
};
|
|
|
|
|
2018-11-03 01:04:11 +07:00
|
|
|
static int qeth_l3_setup_netdev(struct qeth_card *card, bool carrier_ok)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2018-10-12 22:27:14 +07:00
|
|
|
unsigned int headroom;
|
2015-01-21 19:39:10 +07:00
|
|
|
int rc;
|
|
|
|
|
2020-03-18 19:54:47 +07:00
|
|
|
rc = qeth_setup_netdev(card);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
2019-04-25 23:25:57 +07:00
|
|
|
if (IS_OSD(card) || IS_OSX(card)) {
|
2008-02-15 15:19:42 +07:00
|
|
|
if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) ||
|
|
|
|
(card->info.link_type == QETH_LINK_TYPE_HSTR)) {
|
2012-05-11 02:50:52 +07:00
|
|
|
pr_info("qeth_l3: ignoring TR device\n");
|
|
|
|
return -ENODEV;
|
2018-04-26 14:42:16 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
card->dev->netdev_ops = &qeth_l3_osa_netdev_ops;
|
|
|
|
|
|
|
|
/*IPv6 address autoconfiguration stuff*/
|
|
|
|
qeth_l3_get_unique_id(card);
|
|
|
|
if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD))
|
|
|
|
card->dev->dev_id = card->info.unique_id & 0xffff;
|
|
|
|
|
2019-04-25 23:25:57 +07:00
|
|
|
if (!IS_VM_NIC(card)) {
|
2018-04-26 14:42:16 +07:00
|
|
|
card->dev->features |= NETIF_F_SG;
|
|
|
|
card->dev->hw_features |= NETIF_F_TSO |
|
|
|
|
NETIF_F_RXCSUM | NETIF_F_IP_CSUM;
|
|
|
|
card->dev->vlan_features |= NETIF_F_TSO |
|
|
|
|
NETIF_F_RXCSUM | NETIF_F_IP_CSUM;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
2018-04-26 14:42:22 +07:00
|
|
|
|
|
|
|
if (qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) {
|
|
|
|
card->dev->hw_features |= NETIF_F_IPV6_CSUM;
|
|
|
|
card->dev->vlan_features |= NETIF_F_IPV6_CSUM;
|
|
|
|
}
|
2018-10-12 22:27:14 +07:00
|
|
|
if (qeth_is_supported6(card, IPA_OUTBOUND_TSO)) {
|
|
|
|
card->dev->hw_features |= NETIF_F_TSO6;
|
|
|
|
card->dev->vlan_features |= NETIF_F_TSO6;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allow for de-acceleration of NETIF_F_HW_VLAN_CTAG_TX: */
|
|
|
|
if (card->dev->hw_features & NETIF_F_TSO6)
|
|
|
|
headroom = sizeof(struct qeth_hdr_tso) + VLAN_HLEN;
|
|
|
|
else if (card->dev->hw_features & NETIF_F_TSO)
|
|
|
|
headroom = sizeof(struct qeth_hdr_tso);
|
|
|
|
else
|
|
|
|
headroom = sizeof(struct qeth_hdr) + VLAN_HLEN;
|
2019-04-25 23:25:57 +07:00
|
|
|
} else if (IS_IQD(card)) {
|
2008-02-15 15:19:42 +07:00
|
|
|
card->dev->flags |= IFF_NOARP;
|
2009-01-09 10:44:00 +07:00
|
|
|
card->dev->netdev_ops = &qeth_l3_netdev_ops;
|
2018-10-12 22:27:14 +07:00
|
|
|
headroom = sizeof(struct qeth_hdr) - ETH_HLEN;
|
2018-07-11 22:42:46 +07:00
|
|
|
|
2015-01-21 19:39:10 +07:00
|
|
|
rc = qeth_l3_iqd_read_initial_mac(card);
|
|
|
|
if (rc)
|
2018-07-19 17:43:51 +07:00
|
|
|
goto out;
|
2008-02-15 15:19:42 +07:00
|
|
|
} else
|
|
|
|
return -ENODEV;
|
|
|
|
|
2018-10-12 22:27:14 +07:00
|
|
|
card->dev->needed_headroom = headroom;
|
2013-04-19 09:04:27 +07:00
|
|
|
card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX |
|
|
|
|
NETIF_F_HW_VLAN_CTAG_RX |
|
|
|
|
NETIF_F_HW_VLAN_CTAG_FILTER;
|
2018-07-11 22:42:46 +07:00
|
|
|
|
2014-10-06 08:38:35 +07:00
|
|
|
netif_keep_dst(card->dev);
|
2018-10-12 22:27:14 +07:00
|
|
|
if (card->dev->hw_features & (NETIF_F_TSO | NETIF_F_TSO6))
|
2018-07-11 22:42:44 +07:00
|
|
|
netif_set_gso_max_size(card->dev,
|
|
|
|
PAGE_SIZE * (QETH_MAX_BUFFER_ELEMENTS(card) - 1));
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2017-04-11 21:11:11 +07:00
|
|
|
netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT);
|
2018-07-19 17:43:51 +07:00
|
|
|
rc = register_netdev(card->dev);
|
2018-11-03 01:04:11 +07:00
|
|
|
if (!rc && carrier_ok)
|
|
|
|
netif_carrier_on(card->dev);
|
|
|
|
|
2018-07-19 17:43:51 +07:00
|
|
|
out:
|
|
|
|
if (rc)
|
|
|
|
card->dev->netdev_ops = NULL;
|
|
|
|
return rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2017-06-06 19:33:49 +07:00
|
|
|
static const struct device_type qeth_l3_devtype = {
|
|
|
|
.name = "qeth_layer3",
|
|
|
|
.groups = qeth_l3_attr_groups,
|
|
|
|
};
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
static int qeth_l3_probe_device(struct ccwgroup_device *gdev)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
|
2017-05-11 00:07:51 +07:00
|
|
|
int rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-03-18 22:40:55 +07:00
|
|
|
hash_init(card->ip_htable);
|
2019-03-28 22:39:23 +07:00
|
|
|
mutex_init(&card->ip_lock);
|
2019-03-28 22:39:22 +07:00
|
|
|
card->cmd_wq = alloc_ordered_workqueue("%s_cmd", 0,
|
|
|
|
dev_name(&gdev->dev));
|
|
|
|
if (!card->cmd_wq)
|
|
|
|
return -ENOMEM;
|
2019-03-18 22:40:55 +07:00
|
|
|
|
2017-06-06 19:33:49 +07:00
|
|
|
if (gdev->dev.type == &qeth_generic_devtype) {
|
|
|
|
rc = qeth_l3_create_device_attributes(&gdev->dev);
|
2019-03-28 22:39:22 +07:00
|
|
|
if (rc) {
|
|
|
|
destroy_workqueue(card->cmd_wq);
|
2017-06-06 19:33:49 +07:00
|
|
|
return rc;
|
2019-03-28 22:39:22 +07:00
|
|
|
}
|
2017-06-06 19:33:49 +07:00
|
|
|
}
|
2019-03-18 22:40:55 +07:00
|
|
|
|
2017-05-11 00:07:54 +07:00
|
|
|
hash_init(card->ip_mc_htable);
|
2019-03-28 22:39:19 +07:00
|
|
|
INIT_WORK(&card->rx_mode_work, qeth_l3_rx_mode_work);
|
2008-02-15 15:19:42 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qeth_l3_remove_device(struct ccwgroup_device *cgdev)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
|
|
|
|
|
2017-06-06 19:33:49 +07:00
|
|
|
if (cgdev->dev.type == &qeth_generic_devtype)
|
|
|
|
qeth_l3_remove_device_attributes(&cgdev->dev);
|
2010-07-23 06:15:05 +07:00
|
|
|
|
2009-05-20 04:38:37 +07:00
|
|
|
qeth_set_allowed_threads(card, 0, 1);
|
2008-02-15 15:19:42 +07:00
|
|
|
wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
|
|
|
|
|
2011-02-27 13:41:36 +07:00
|
|
|
if (cgdev->state == CCWGROUP_ONLINE)
|
2020-01-25 22:53:01 +07:00
|
|
|
qeth_set_offline(card, false);
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2019-02-04 23:40:08 +07:00
|
|
|
cancel_work_sync(&card->close_dev_work);
|
2018-11-03 01:04:10 +07:00
|
|
|
if (qeth_netdev_is_registered(card->dev))
|
|
|
|
unregister_netdev(card->dev);
|
2019-03-28 22:39:22 +07:00
|
|
|
|
|
|
|
flush_workqueue(card->cmd_wq);
|
|
|
|
destroy_workqueue(card->cmd_wq);
|
2016-06-16 21:18:58 +07:00
|
|
|
qeth_l3_clear_ip_htable(card, 0);
|
2008-02-15 15:19:42 +07:00
|
|
|
qeth_l3_clear_ipato_list(card);
|
|
|
|
}
|
|
|
|
|
2020-01-25 22:53:01 +07:00
|
|
|
static int qeth_l3_set_online(struct qeth_card *card)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2020-01-25 22:53:01 +07:00
|
|
|
struct ccwgroup_device *gdev = card->gdev;
|
2019-01-25 21:44:21 +07:00
|
|
|
struct net_device *dev = card->dev;
|
2008-02-15 15:19:42 +07:00
|
|
|
int rc = 0;
|
2018-11-03 01:04:11 +07:00
|
|
|
bool carrier_ok;
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2018-11-03 01:04:11 +07:00
|
|
|
rc = qeth_core_hardsetup_card(card, &carrier_ok);
|
2008-02-15 15:19:42 +07:00
|
|
|
if (rc) {
|
2019-06-11 23:37:55 +07:00
|
|
|
QETH_CARD_TEXT_(card, 2, "2err%04x", rc);
|
2009-11-12 07:11:43 +07:00
|
|
|
rc = -ENODEV;
|
2008-02-15 15:19:42 +07:00
|
|
|
goto out_remove;
|
|
|
|
}
|
|
|
|
|
|
|
|
qeth_print_status_message(card);
|
|
|
|
|
|
|
|
/* softsetup */
|
2019-06-11 23:37:55 +07:00
|
|
|
QETH_CARD_TEXT(card, 2, "softsetp");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
rc = qeth_l3_setadapter_parms(card);
|
|
|
|
if (rc)
|
2019-06-11 23:37:55 +07:00
|
|
|
QETH_CARD_TEXT_(card, 2, "2err%04x", rc);
|
2010-01-11 09:50:50 +07:00
|
|
|
if (!card->options.sniffer) {
|
2020-01-25 22:53:03 +07:00
|
|
|
qeth_l3_start_ipassists(card);
|
|
|
|
|
2010-01-11 09:50:50 +07:00
|
|
|
rc = qeth_l3_setrouting_v4(card);
|
|
|
|
if (rc)
|
2019-06-11 23:37:55 +07:00
|
|
|
QETH_CARD_TEXT_(card, 2, "4err%04x", rc);
|
2010-01-11 09:50:50 +07:00
|
|
|
rc = qeth_l3_setrouting_v6(card);
|
|
|
|
if (rc)
|
2019-06-11 23:37:55 +07:00
|
|
|
QETH_CARD_TEXT_(card, 2, "5err%04x", rc);
|
2010-01-11 09:50:50 +07:00
|
|
|
}
|
2008-02-15 15:19:42 +07:00
|
|
|
|
|
|
|
card->state = CARD_STATE_SOFTSETUP;
|
|
|
|
|
|
|
|
qeth_set_allowed_threads(card, 0xffffffff, 0);
|
2016-06-16 21:18:58 +07:00
|
|
|
qeth_l3_recover_ip(card);
|
2018-06-30 00:45:54 +07:00
|
|
|
|
2019-01-25 21:44:21 +07:00
|
|
|
if (!qeth_netdev_is_registered(dev)) {
|
|
|
|
rc = qeth_l3_setup_netdev(card, carrier_ok);
|
|
|
|
if (rc)
|
|
|
|
goto out_remove;
|
|
|
|
} else {
|
2011-12-20 05:56:32 +07:00
|
|
|
rtnl_lock();
|
2019-01-25 21:44:21 +07:00
|
|
|
if (carrier_ok)
|
|
|
|
netif_carrier_on(dev);
|
|
|
|
else
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
|
2019-01-25 21:44:22 +07:00
|
|
|
netif_device_attach(dev);
|
2019-01-25 21:44:21 +07:00
|
|
|
qeth_enable_hw_features(dev);
|
|
|
|
|
2019-03-01 00:59:36 +07:00
|
|
|
if (card->info.open_when_online) {
|
|
|
|
card->info.open_when_online = 0;
|
s390/qeth: call dev_close() during recovery
When resetting an interface ("recovery"), qeth currently attempts to
elide the call to dev_close(). We initially only call .ndo_close to
quiesce the data path, and then offline & online the ccwgroup device.
If the reset succeeded, a call to .ndo_open then resumes the data path
along with some internal setup (dev_addr validation, RX modeset) that
dev_open() would have usually triggered.
dev_close() only gets called (via the close_dev worker) if the reset
action fails.
It's unclear whether this was initially done due to locking concerns, or
rather to execute the reset transparently. Either way, temporarily
closing the interface without dev_close() is fragile, and means we're
susceptible to various races and unexpected behaviour. For instance:
- Bypassing dev_deactivate_many() means that the qdiscs are not set to
__QDISC_STATE_DEACTIVATED. Consequently any intermittent TX completion
can wake up the txq, resulting in calls to .ndo_start_xmit while the
data path is down. We have custom state checking to detect this case
and drop such packets.
- Because the IFF_UP flag doesn't reflect the interface's actual state
during a reset, we have custom state checking in .ndo_open and
.ndo_close to guard against invalid calls.
- Considering that the reset might take a considerable amount of time
(in particular if an IO fails and we end up waiting for its timeout), we
_do_ want NETDEV_GOING_DOWN and NETDEV_DOWN events so that components
like bonding, team, bridge, macvlan, vlan, ... can take appropriate
action.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-03-01 00:59:39 +07:00
|
|
|
dev_open(dev, NULL);
|
2018-07-11 22:42:40 +07:00
|
|
|
}
|
2011-12-20 05:56:32 +07:00
|
|
|
rtnl_unlock();
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
2013-03-19 03:04:42 +07:00
|
|
|
qeth_trace_features(card);
|
2008-02-15 15:19:42 +07:00
|
|
|
/* let user_space know that device is online */
|
|
|
|
kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
|
|
|
|
return 0;
|
|
|
|
out_remove:
|
s390/qeth: call dev_close() during recovery
When resetting an interface ("recovery"), qeth currently attempts to
elide the call to dev_close(). We initially only call .ndo_close to
quiesce the data path, and then offline & online the ccwgroup device.
If the reset succeeded, a call to .ndo_open then resumes the data path
along with some internal setup (dev_addr validation, RX modeset) that
dev_open() would have usually triggered.
dev_close() only gets called (via the close_dev worker) if the reset
action fails.
It's unclear whether this was initially done due to locking concerns, or
rather to execute the reset transparently. Either way, temporarily
closing the interface without dev_close() is fragile, and means we're
susceptible to various races and unexpected behaviour. For instance:
- Bypassing dev_deactivate_many() means that the qdiscs are not set to
__QDISC_STATE_DEACTIVATED. Consequently any intermittent TX completion
can wake up the txq, resulting in calls to .ndo_start_xmit while the
data path is down. We have custom state checking to detect this case
and drop such packets.
- Because the IFF_UP flag doesn't reflect the interface's actual state
during a reset, we have custom state checking in .ndo_open and
.ndo_close to guard against invalid calls.
- Considering that the reset might take a considerable amount of time
(in particular if an IO fails and we end up waiting for its timeout), we
_do_ want NETDEV_GOING_DOWN and NETDEV_DOWN events so that components
like bonding, team, bridge, macvlan, vlan, ... can take appropriate
action.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-03-01 00:59:39 +07:00
|
|
|
qeth_l3_stop_card(card);
|
2019-12-05 20:33:04 +07:00
|
|
|
qeth_stop_channel(&card->data);
|
|
|
|
qeth_stop_channel(&card->write);
|
|
|
|
qeth_stop_channel(&card->read);
|
2014-02-24 19:12:06 +07:00
|
|
|
qdio_free(CARD_DDEV(card));
|
2009-11-12 07:11:43 +07:00
|
|
|
return rc;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2020-01-25 22:53:01 +07:00
|
|
|
static void qeth_l3_set_offline(struct qeth_card *card)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
s390/qeth: call dev_close() during recovery
When resetting an interface ("recovery"), qeth currently attempts to
elide the call to dev_close(). We initially only call .ndo_close to
quiesce the data path, and then offline & online the ccwgroup device.
If the reset succeeded, a call to .ndo_open then resumes the data path
along with some internal setup (dev_addr validation, RX modeset) that
dev_open() would have usually triggered.
dev_close() only gets called (via the close_dev worker) if the reset
action fails.
It's unclear whether this was initially done due to locking concerns, or
rather to execute the reset transparently. Either way, temporarily
closing the interface without dev_close() is fragile, and means we're
susceptible to various races and unexpected behaviour. For instance:
- Bypassing dev_deactivate_many() means that the qdiscs are not set to
__QDISC_STATE_DEACTIVATED. Consequently any intermittent TX completion
can wake up the txq, resulting in calls to .ndo_start_xmit while the
data path is down. We have custom state checking to detect this case
and drop such packets.
- Because the IFF_UP flag doesn't reflect the interface's actual state
during a reset, we have custom state checking in .ndo_open and
.ndo_close to guard against invalid calls.
- Considering that the reset might take a considerable amount of time
(in particular if an IO fails and we end up waiting for its timeout), we
_do_ want NETDEV_GOING_DOWN and NETDEV_DOWN events so that components
like bonding, team, bridge, macvlan, vlan, ... can take appropriate
action.
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-03-01 00:59:39 +07:00
|
|
|
qeth_l3_stop_card(card);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:23:48 +07:00
|
|
|
/* Returns zero if the command is successfully "consumed" */
|
|
|
|
static int qeth_l3_control_event(struct qeth_card *card,
|
|
|
|
struct qeth_ipa_cmd *cmd)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-05-15 23:02:21 +07:00
|
|
|
struct qeth_discipline qeth_l3_discipline = {
|
2017-06-06 19:33:49 +07:00
|
|
|
.devtype = &qeth_l3_devtype,
|
2012-05-15 23:02:21 +07:00
|
|
|
.setup = qeth_l3_probe_device,
|
2008-02-15 15:19:42 +07:00
|
|
|
.remove = qeth_l3_remove_device,
|
|
|
|
.set_online = qeth_l3_set_online,
|
|
|
|
.set_offline = qeth_l3_set_offline,
|
2017-04-11 21:11:10 +07:00
|
|
|
.do_ioctl = qeth_l3_do_ioctl,
|
2014-01-29 15:23:48 +07:00
|
|
|
.control_event_handler = qeth_l3_control_event,
|
2008-02-15 15:19:42 +07:00
|
|
|
};
|
2012-05-15 23:02:21 +07:00
|
|
|
EXPORT_SYMBOL_GPL(qeth_l3_discipline);
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2018-03-10 00:13:00 +07:00
|
|
|
static int qeth_l3_handle_ip_event(struct qeth_card *card,
|
|
|
|
struct qeth_ipaddr *addr,
|
|
|
|
unsigned long event)
|
|
|
|
{
|
|
|
|
switch (event) {
|
|
|
|
case NETDEV_UP:
|
2019-03-28 22:39:21 +07:00
|
|
|
qeth_l3_modify_ip(card, addr, true);
|
2018-03-10 00:13:00 +07:00
|
|
|
return NOTIFY_OK;
|
|
|
|
case NETDEV_DOWN:
|
2019-03-28 22:39:21 +07:00
|
|
|
qeth_l3_modify_ip(card, addr, false);
|
2018-03-10 00:13:00 +07:00
|
|
|
return NOTIFY_OK;
|
|
|
|
default:
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-28 22:39:22 +07:00
|
|
|
struct qeth_l3_ip_event_work {
|
|
|
|
struct work_struct work;
|
|
|
|
struct qeth_card *card;
|
|
|
|
struct qeth_ipaddr addr;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define to_ip_work(w) container_of((w), struct qeth_l3_ip_event_work, work)
|
|
|
|
|
|
|
|
static void qeth_l3_add_ip_worker(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct qeth_l3_ip_event_work *ip_work = to_ip_work(work);
|
|
|
|
|
|
|
|
qeth_l3_modify_ip(ip_work->card, &ip_work->addr, true);
|
|
|
|
kfree(work);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qeth_l3_delete_ip_worker(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct qeth_l3_ip_event_work *ip_work = to_ip_work(work);
|
|
|
|
|
|
|
|
qeth_l3_modify_ip(ip_work->card, &ip_work->addr, false);
|
|
|
|
kfree(work);
|
|
|
|
}
|
|
|
|
|
2018-03-10 00:13:01 +07:00
|
|
|
static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
|
|
|
|
{
|
|
|
|
if (is_vlan_dev(dev))
|
|
|
|
dev = vlan_dev_real_dev(dev);
|
|
|
|
if (dev->netdev_ops == &qeth_l3_osa_netdev_ops ||
|
|
|
|
dev->netdev_ops == &qeth_l3_netdev_ops)
|
|
|
|
return (struct qeth_card *) dev->ml_priv;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
static int qeth_l3_ip_event(struct notifier_block *this,
|
2008-03-23 08:22:42 +07:00
|
|
|
unsigned long event, void *ptr)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
|
2018-03-10 00:13:01 +07:00
|
|
|
struct net_device *dev = ifa->ifa_dev->dev;
|
2018-03-10 00:13:00 +07:00
|
|
|
struct qeth_ipaddr addr;
|
2008-02-15 15:19:42 +07:00
|
|
|
struct qeth_card *card;
|
|
|
|
|
2008-03-25 19:47:49 +07:00
|
|
|
if (dev_net(dev) != &init_net)
|
2008-03-23 08:22:42 +07:00
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
card = qeth_l3_get_card_from_dev(dev);
|
|
|
|
if (!card)
|
|
|
|
return NOTIFY_DONE;
|
2012-05-16 08:28:26 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "ipevent");
|
2008-02-15 15:19:42 +07:00
|
|
|
|
2018-03-10 00:13:00 +07:00
|
|
|
qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4);
|
2019-10-31 19:42:20 +07:00
|
|
|
addr.u.a4.addr = ifa->ifa_address;
|
2019-12-18 23:34:46 +07:00
|
|
|
addr.u.a4.mask = ifa->ifa_mask;
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2018-03-10 00:13:00 +07:00
|
|
|
return qeth_l3_handle_ip_event(card, &addr, event);
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block qeth_l3_ip_notifier = {
|
|
|
|
qeth_l3_ip_event,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int qeth_l3_ip6_event(struct notifier_block *this,
|
2008-03-23 08:22:42 +07:00
|
|
|
unsigned long event, void *ptr)
|
2008-02-15 15:19:42 +07:00
|
|
|
{
|
|
|
|
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
|
2018-03-10 00:13:01 +07:00
|
|
|
struct net_device *dev = ifa->idev->dev;
|
2019-03-28 22:39:22 +07:00
|
|
|
struct qeth_l3_ip_event_work *ip_work;
|
2008-02-15 15:19:42 +07:00
|
|
|
struct qeth_card *card;
|
|
|
|
|
2019-03-28 22:39:22 +07:00
|
|
|
if (event != NETDEV_UP && event != NETDEV_DOWN)
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
2008-02-15 15:19:42 +07:00
|
|
|
card = qeth_l3_get_card_from_dev(dev);
|
|
|
|
if (!card)
|
|
|
|
return NOTIFY_DONE;
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_CARD_TEXT(card, 3, "ip6event");
|
2008-02-15 15:19:42 +07:00
|
|
|
if (!qeth_is_supported(card, IPA_IPV6))
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
2019-03-28 22:39:22 +07:00
|
|
|
ip_work = kmalloc(sizeof(*ip_work), GFP_ATOMIC);
|
|
|
|
if (!ip_work)
|
|
|
|
return NOTIFY_DONE;
|
2016-06-16 21:18:58 +07:00
|
|
|
|
2019-03-28 22:39:22 +07:00
|
|
|
if (event == NETDEV_UP)
|
|
|
|
INIT_WORK(&ip_work->work, qeth_l3_add_ip_worker);
|
|
|
|
else
|
|
|
|
INIT_WORK(&ip_work->work, qeth_l3_delete_ip_worker);
|
|
|
|
|
|
|
|
ip_work->card = card;
|
|
|
|
qeth_l3_init_ipaddr(&ip_work->addr, QETH_IP_TYPE_NORMAL,
|
|
|
|
QETH_PROT_IPV6);
|
|
|
|
ip_work->addr.u.a6.addr = ifa->addr;
|
|
|
|
ip_work->addr.u.a6.pfxlen = ifa->prefix_len;
|
|
|
|
|
|
|
|
queue_work(card->cmd_wq, &ip_work->work);
|
|
|
|
return NOTIFY_OK;
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block qeth_l3_ip6_notifier = {
|
|
|
|
qeth_l3_ip6_event,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int qeth_l3_register_notifiers(void)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_DBF_TEXT(SETUP, 5, "regnotif");
|
2008-02-15 15:19:42 +07:00
|
|
|
rc = register_inetaddr_notifier(&qeth_l3_ip_notifier);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
rc = register_inet6addr_notifier(&qeth_l3_ip6_notifier);
|
|
|
|
if (rc) {
|
|
|
|
unregister_inetaddr_notifier(&qeth_l3_ip_notifier);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qeth_l3_unregister_notifiers(void)
|
|
|
|
{
|
2010-06-22 05:57:05 +07:00
|
|
|
QETH_DBF_TEXT(SETUP, 5, "unregnot");
|
2012-11-19 09:46:50 +07:00
|
|
|
WARN_ON(unregister_inetaddr_notifier(&qeth_l3_ip_notifier));
|
|
|
|
WARN_ON(unregister_inet6addr_notifier(&qeth_l3_ip6_notifier));
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int __init qeth_l3_init(void)
|
|
|
|
{
|
2008-12-25 19:39:49 +07:00
|
|
|
pr_info("register layer 3 discipline\n");
|
2017-12-21 02:10:58 +07:00
|
|
|
return qeth_l3_register_notifiers();
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit qeth_l3_exit(void)
|
|
|
|
{
|
|
|
|
qeth_l3_unregister_notifiers();
|
2008-12-25 19:39:49 +07:00
|
|
|
pr_info("unregister layer 3 discipline\n");
|
2008-02-15 15:19:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(qeth_l3_init);
|
|
|
|
module_exit(qeth_l3_exit);
|
|
|
|
MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>");
|
|
|
|
MODULE_DESCRIPTION("qeth layer 3 discipline");
|
|
|
|
MODULE_LICENSE("GPL");
|