2017-11-15 01:38:04 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2008-02-15 16:19:42 +08:00
|
|
|
/*
|
2009-06-16 16:30:31 +08:00
|
|
|
* Copyright IBM Corp. 2007, 2009
|
2008-02-15 16:19:42 +08: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 20:39:49 +08:00
|
|
|
#define KMSG_COMPONENT "qeth"
|
|
|
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/moduleparam.h>
|
2011-07-20 12:54:41 +08:00
|
|
|
#include <linux/bitops.h>
|
2008-02-15 16:19:42 +08:00
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/ip.h>
|
2009-03-25 04:57:16 +08:00
|
|
|
#include <linux/ipv6.h>
|
2008-02-15 16:19:42 +08: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 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2011-07-20 12:54:41 +08:00
|
|
|
#include <linux/if_vlan.h>
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
#include <net/ip.h>
|
|
|
|
#include <net/arp.h>
|
2012-02-01 18:49:17 +08:00
|
|
|
#include <net/route.h>
|
|
|
|
#include <net/ip6_fib.h>
|
2009-03-25 04:57:16 +08:00
|
|
|
#include <net/ip6_checksum.h>
|
2011-08-08 09:33:59 +08:00
|
|
|
#include <net/iucv/af_iucv.h>
|
2016-06-16 22:18:58 +08:00
|
|
|
#include <linux/hashtable.h>
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
#include "qeth_l3.h"
|
|
|
|
|
2010-12-08 10:57:58 +08:00
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
static int qeth_l3_set_offline(struct ccwgroup_device *);
|
|
|
|
static int qeth_l3_stop(struct net_device *);
|
|
|
|
static void qeth_l3_set_multicast_list(struct net_device *);
|
|
|
|
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 *);
|
|
|
|
|
|
|
|
static int qeth_l3_isxdigit(char *buf)
|
|
|
|
{
|
|
|
|
while (*buf) {
|
|
|
|
if (!isxdigit(*buf++))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-10-22 18:18:04 +08:00
|
|
|
static void qeth_l3_ipaddr4_to_string(const __u8 *addr, char *buf)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
2016-06-16 22:18:58 +08:00
|
|
|
sprintf(buf, "%pI4", addr);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2014-10-22 18:18:04 +08:00
|
|
|
static int qeth_l3_string_to_ipaddr4(const char *buf, __u8 *addr)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
int count = 0, rc = 0;
|
2014-05-28 16:22:31 +08:00
|
|
|
unsigned int in[4];
|
2008-02-15 16:19:42 +08:00
|
|
|
char c;
|
|
|
|
|
|
|
|
rc = sscanf(buf, "%u.%u.%u.%u%c",
|
|
|
|
&in[0], &in[1], &in[2], &in[3], &c);
|
|
|
|
if (rc != 4 && (rc != 5 || c != '\n'))
|
|
|
|
return -EINVAL;
|
|
|
|
for (count = 0; count < 4; count++) {
|
|
|
|
if (in[count] > 255)
|
|
|
|
return -EINVAL;
|
|
|
|
addr[count] = in[count];
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-22 18:18:04 +08:00
|
|
|
static void qeth_l3_ipaddr6_to_string(const __u8 *addr, char *buf)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
2010-09-08 05:14:40 +08:00
|
|
|
sprintf(buf, "%pI6", addr);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2014-10-22 18:18:04 +08:00
|
|
|
static int qeth_l3_string_to_ipaddr6(const char *buf, __u8 *addr)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
const char *end, *end_tmp, *start;
|
|
|
|
__u16 *in;
|
|
|
|
char num[5];
|
|
|
|
int num2, cnt, out, found, save_cnt;
|
|
|
|
unsigned short in_tmp[8] = {0, };
|
|
|
|
|
|
|
|
cnt = out = found = save_cnt = num2 = 0;
|
|
|
|
end = start = buf;
|
|
|
|
in = (__u16 *) addr;
|
|
|
|
memset(in, 0, 16);
|
|
|
|
while (*end) {
|
|
|
|
end = strchr(start, ':');
|
|
|
|
if (end == NULL) {
|
|
|
|
end = buf + strlen(buf);
|
|
|
|
end_tmp = strchr(start, '\n');
|
|
|
|
if (end_tmp != NULL)
|
|
|
|
end = end_tmp;
|
|
|
|
out = 1;
|
|
|
|
}
|
|
|
|
if ((end - start)) {
|
|
|
|
memset(num, 0, 5);
|
|
|
|
if ((end - start) > 4)
|
|
|
|
return -EINVAL;
|
|
|
|
memcpy(num, start, end - start);
|
|
|
|
if (!qeth_l3_isxdigit(num))
|
|
|
|
return -EINVAL;
|
|
|
|
sscanf(start, "%x", &num2);
|
|
|
|
if (found)
|
|
|
|
in_tmp[save_cnt++] = num2;
|
|
|
|
else
|
|
|
|
in[cnt++] = num2;
|
|
|
|
if (out)
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (found)
|
|
|
|
return -EINVAL;
|
|
|
|
found = 1;
|
|
|
|
}
|
|
|
|
start = ++end;
|
|
|
|
}
|
|
|
|
if (cnt + save_cnt > 8)
|
|
|
|
return -EINVAL;
|
|
|
|
cnt = 7;
|
|
|
|
while (save_cnt)
|
|
|
|
in[cnt--] = in_tmp[--save_cnt];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
if (proto == QETH_PROT_IPV4)
|
|
|
|
qeth_l3_ipaddr4_to_string(addr, buf);
|
|
|
|
else if (proto == QETH_PROT_IPV6)
|
|
|
|
qeth_l3_ipaddr6_to_string(addr, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
int qeth_l3_string_to_ipaddr(const char *buf, enum qeth_prot_versions proto,
|
|
|
|
__u8 *addr)
|
|
|
|
{
|
|
|
|
if (proto == QETH_PROT_IPV4)
|
|
|
|
return qeth_l3_string_to_ipaddr4(buf, addr);
|
|
|
|
else if (proto == QETH_PROT_IPV6)
|
|
|
|
return qeth_l3_string_to_ipaddr6(buf, addr);
|
|
|
|
else
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 01:56:32 +08:00
|
|
|
static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
|
|
|
|
struct qeth_ipaddr *addr)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
struct qeth_ipato_entry *ipatoe;
|
|
|
|
u8 addr_bits[128] = {0, };
|
|
|
|
u8 ipatoe_bits[128] = {0, };
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (!card->ipato.enabled)
|
|
|
|
return 0;
|
2017-12-14 01:56:30 +08:00
|
|
|
if (addr->type != QETH_IP_TYPE_NORMAL)
|
|
|
|
return 0;
|
2008-02-15 16:19:42 +08: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;
|
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
inline int
|
|
|
|
qeth_l3_ipaddrs_is_equal(struct qeth_ipaddr *addr1, struct qeth_ipaddr *addr2)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
2016-06-16 22:18:58 +08:00
|
|
|
return addr1->proto == addr2->proto &&
|
|
|
|
!memcmp(&addr1->u, &addr2->u, sizeof(addr1->u)) &&
|
|
|
|
!memcmp(&addr1->mac, &addr2->mac, sizeof(addr1->mac));
|
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
static struct qeth_ipaddr *
|
|
|
|
qeth_l3_ip_from_hash(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
|
|
|
|
{
|
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
|
|
|
|
if (tmp_addr->is_multicast) {
|
|
|
|
hash_for_each_possible(card->ip_mc_htable, addr,
|
|
|
|
hnode, qeth_l3_ipaddr_hash(tmp_addr))
|
|
|
|
if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
|
|
|
|
return addr;
|
2008-02-15 16:19:42 +08:00
|
|
|
} else {
|
2016-06-16 22:18:58 +08:00
|
|
|
hash_for_each_possible(card->ip_htable, addr,
|
|
|
|
hnode, qeth_l3_ipaddr_hash(tmp_addr))
|
|
|
|
if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
|
|
|
|
return addr;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
return NULL;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
int rc = 0;
|
2016-06-16 22:18:58 +08:00
|
|
|
struct qeth_ipaddr *addr;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "delip");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
if (tmp_addr->proto == QETH_PROT_IPV4)
|
|
|
|
QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4);
|
2008-02-15 16:19:42 +08:00
|
|
|
else {
|
2016-06-16 22:18:58 +08: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 16:19:42 +08:00
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
addr = qeth_l3_ip_from_hash(card, tmp_addr);
|
|
|
|
if (!addr)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
addr->ref_counter--;
|
2017-08-15 23:02:50 +08:00
|
|
|
if (addr->ref_counter > 0 && (addr->type == QETH_IP_TYPE_NORMAL ||
|
|
|
|
addr->type == QETH_IP_TYPE_RXIP))
|
2016-06-16 22:18:58 +08:00
|
|
|
return rc;
|
|
|
|
if (addr->in_progress)
|
|
|
|
return -EINPROGRESS;
|
|
|
|
|
2016-09-15 20:39:23 +08:00
|
|
|
if (!qeth_card_hw_is_reachable(card)) {
|
|
|
|
addr->disp_flag = QETH_DISP_ADDR_DELETE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
rc = qeth_l3_deregister_addr_entry(card, addr);
|
|
|
|
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
int rc = 0;
|
2016-06-16 22:18:58 +08:00
|
|
|
struct qeth_ipaddr *addr;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "addip");
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
if (tmp_addr->proto == QETH_PROT_IPV4)
|
|
|
|
QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4);
|
2008-02-15 16:19:42 +08:00
|
|
|
else {
|
2016-06-16 22:18:58 +08: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);
|
|
|
|
}
|
|
|
|
|
|
|
|
addr = qeth_l3_ip_from_hash(card, tmp_addr);
|
|
|
|
if (!addr) {
|
|
|
|
addr = qeth_l3_get_addr_buffer(tmp_addr->proto);
|
|
|
|
if (!addr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
memcpy(addr, tmp_addr, sizeof(struct qeth_ipaddr));
|
|
|
|
addr->ref_counter = 1;
|
|
|
|
|
2017-12-14 01:56:30 +08:00
|
|
|
if (qeth_l3_is_addr_covered_by_ipato(card, addr)) {
|
2016-06-16 22:18:58 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "tkovaddr");
|
|
|
|
addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
|
|
|
|
}
|
|
|
|
hash_add(card->ip_htable, &addr->hnode,
|
|
|
|
qeth_l3_ipaddr_hash(addr));
|
|
|
|
|
2016-09-15 20:39:23 +08:00
|
|
|
if (!qeth_card_hw_is_reachable(card)) {
|
|
|
|
addr->disp_flag = QETH_DISP_ADDR_ADD;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
/* qeth_l3_register_addr_entry can go to sleep
|
|
|
|
* if we add a IPV4 addr. It is caused by the reason
|
|
|
|
* that SETIP ipa cmd starts ARP staff for IPV4 addr.
|
|
|
|
* Thus we should unlock spinlock, and make a protection
|
|
|
|
* using in_progress variable to indicate that there is
|
|
|
|
* an hardware operation with this IPV4 address
|
|
|
|
*/
|
|
|
|
if (addr->proto == QETH_PROT_IPV4) {
|
|
|
|
addr->in_progress = 1;
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
|
|
|
rc = qeth_l3_register_addr_entry(card, addr);
|
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
addr->in_progress = 0;
|
|
|
|
} else
|
|
|
|
rc = qeth_l3_register_addr_entry(card, addr);
|
|
|
|
|
|
|
|
if (!rc || (rc == IPA_RC_DUPLICATE_IP_ADDRESS) ||
|
|
|
|
(rc == IPA_RC_LAN_OFFLINE)) {
|
|
|
|
addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
|
|
|
if (addr->ref_counter < 1) {
|
|
|
|
qeth_l3_delete_ip(card, addr);
|
|
|
|
kfree(addr);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
}
|
|
|
|
} else {
|
2017-08-15 23:02:50 +08:00
|
|
|
if (addr->type == QETH_IP_TYPE_NORMAL ||
|
|
|
|
addr->type == QETH_IP_TYPE_RXIP)
|
|
|
|
addr->ref_counter++;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-08-08 09:33:59 +08:00
|
|
|
struct qeth_ipaddr *qeth_l3_get_addr_buffer(
|
2008-02-15 16:19:42 +08:00
|
|
|
enum qeth_prot_versions prot)
|
|
|
|
{
|
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
|
|
|
|
addr = kzalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC);
|
2016-06-16 22:18:58 +08:00
|
|
|
if (!addr)
|
2008-02-15 16:19:42 +08:00
|
|
|
return NULL;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
addr->type = QETH_IP_TYPE_NORMAL;
|
2016-06-16 22:18:58 +08:00
|
|
|
addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
2008-02-15 16:19:42 +08:00
|
|
|
addr->proto = prot;
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
return addr;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
static void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
struct qeth_ipaddr *addr;
|
2016-06-16 22:18:58 +08:00
|
|
|
struct hlist_node *tmp;
|
|
|
|
int i;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "clearip");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
if (recover && card->options.sniffer)
|
2010-01-11 10:50:50 +08:00
|
|
|
return;
|
2010-12-08 10:57:59 +08:00
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
|
|
|
|
hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
|
|
|
|
if (!recover) {
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
2008-02-15 16:19:42 +08:00
|
|
|
continue;
|
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
addr->disp_flag = QETH_DISP_ADDR_ADD;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_unlock_bh(&card->ip_lock);
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_lock_bh(&card->mclock);
|
|
|
|
|
|
|
|
hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) {
|
|
|
|
hash_del(&addr->hnode);
|
2008-02-15 16:19:42 +08:00
|
|
|
kfree(addr);
|
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_unlock_bh(&card->mclock);
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
}
|
|
|
|
static void qeth_l3_recover_ip(struct qeth_card *card)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
2016-06-16 22:18:58 +08:00
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
struct hlist_node *tmp;
|
|
|
|
int i;
|
|
|
|
int rc;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2016-09-15 20:39:23 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "recovrip");
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
|
|
|
|
hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
|
2016-09-15 20:39:23 +08:00
|
|
|
if (addr->disp_flag == QETH_DISP_ADDR_DELETE) {
|
|
|
|
qeth_l3_deregister_addr_entry(card, addr);
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
} else if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
|
2016-06-16 22:18:58 +08:00
|
|
|
if (addr->proto == QETH_PROT_IPV4) {
|
|
|
|
addr->in_progress = 1;
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
|
|
|
rc = qeth_l3_register_addr_entry(card, addr);
|
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
addr->in_progress = 0;
|
|
|
|
} else
|
|
|
|
rc = qeth_l3_register_addr_entry(card, addr);
|
|
|
|
|
|
|
|
if (!rc) {
|
|
|
|
addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
2016-09-15 20:39:23 +08:00
|
|
|
if (addr->ref_counter < 1)
|
2016-06-16 22:18:58 +08:00
|
|
|
qeth_l3_delete_ip(card, addr);
|
|
|
|
} else {
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_unlock_bh(&card->ip_lock);
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_send_setdelmc(struct qeth_card *card,
|
|
|
|
struct qeth_ipaddr *addr, int ipacmd)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "setdelmc");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto);
|
2015-01-21 20:39:10 +08:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2008-02-15 16:19:42 +08:00
|
|
|
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
|
|
|
memcpy(&cmd->data.setdelipm.mac, addr->mac, OSA_ADDR_LEN);
|
|
|
|
if (addr->proto == QETH_PROT_IPV6)
|
|
|
|
memcpy(cmd->data.setdelipm.ip6, &addr->u.a6.addr,
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
else
|
|
|
|
memcpy(&cmd->data.setdelipm.ip4, &addr->u.a4.addr, 4);
|
|
|
|
|
|
|
|
rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qeth_l3_fill_netmask(u8 *netmask, unsigned int len)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
|
|
j = (len) - (i * 8);
|
|
|
|
if (j >= 8)
|
|
|
|
netmask[i] = 0xff;
|
|
|
|
else if (j > 0)
|
|
|
|
netmask[i] = (u8)(0xFF00 >> j);
|
|
|
|
else
|
|
|
|
netmask[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_send_setdelip(struct qeth_card *card,
|
|
|
|
struct qeth_ipaddr *addr, int ipacmd, unsigned int flags)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
|
|
|
__u8 netmask[16];
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "setdelip");
|
|
|
|
QETH_CARD_TEXT_(card, 4, "flags%02X", flags);
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto);
|
2015-01-21 20:39:10 +08:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2008-02-15 16:19:42 +08:00
|
|
|
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
|
|
|
if (addr->proto == QETH_PROT_IPV6) {
|
|
|
|
memcpy(cmd->data.setdelip6.ip_addr, &addr->u.a6.addr,
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
qeth_l3_fill_netmask(netmask, addr->u.a6.pfxlen);
|
|
|
|
memcpy(cmd->data.setdelip6.mask, netmask,
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
cmd->data.setdelip6.flags = flags;
|
|
|
|
} else {
|
|
|
|
memcpy(cmd->data.setdelip4.ip_addr, &addr->u.a4.addr, 4);
|
|
|
|
memcpy(cmd->data.setdelip4.mask, &addr->u.a4.mask, 4);
|
|
|
|
cmd->data.setdelip4.flags = flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "setroutg");
|
2008-02-15 16:19:42 +08:00
|
|
|
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETRTG, prot);
|
2015-01-21 20:39:10 +08:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2008-02-15 16:19:42 +08:00
|
|
|
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
|
|
|
cmd->data.setrtg.type = (type);
|
|
|
|
rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-03-19 04:04:43 +08:00
|
|
|
static int qeth_l3_correct_routing_type(struct qeth_card *card,
|
2008-02-15 16:19:42 +08:00
|
|
|
enum qeth_routing_types *type, enum qeth_prot_versions prot)
|
|
|
|
{
|
|
|
|
if (card->info.type == QETH_CARD_TYPE_IQD) {
|
|
|
|
switch (*type) {
|
|
|
|
case NO_ROUTER:
|
|
|
|
case PRIMARY_CONNECTOR:
|
|
|
|
case SECONDARY_CONNECTOR:
|
|
|
|
case MULTICAST_ROUTER:
|
2013-03-19 04:04:43 +08:00
|
|
|
return 0;
|
2008-02-15 16:19:42 +08:00
|
|
|
default:
|
|
|
|
goto out_inval;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (*type) {
|
|
|
|
case NO_ROUTER:
|
|
|
|
case PRIMARY_ROUTER:
|
|
|
|
case SECONDARY_ROUTER:
|
2013-03-19 04:04:43 +08:00
|
|
|
return 0;
|
2008-02-15 16:19:42 +08:00
|
|
|
case MULTICAST_ROUTER:
|
|
|
|
if (qeth_is_ipafunc_supported(card, prot,
|
|
|
|
IPA_OSA_MC_ROUTER))
|
2013-03-19 04:04:43 +08:00
|
|
|
return 0;
|
2008-02-15 16:19:42 +08:00
|
|
|
default:
|
|
|
|
goto out_inval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out_inval:
|
|
|
|
*type = NO_ROUTER;
|
2013-03-19 04:04:43 +08:00
|
|
|
return -EINVAL;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int qeth_l3_setrouting_v4(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "setrtg4");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2013-03-19 04:04:43 +08:00
|
|
|
rc = qeth_l3_correct_routing_type(card, &card->options.route4.type,
|
2008-02-15 16:19:42 +08:00
|
|
|
QETH_PROT_IPV4);
|
2013-03-19 04:04:43 +08:00
|
|
|
if (rc)
|
|
|
|
return rc;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
rc = qeth_l3_send_setrouting(card, card->options.route4.type,
|
|
|
|
QETH_PROT_IPV4);
|
|
|
|
if (rc) {
|
|
|
|
card->options.route4.type = NO_ROUTER;
|
2008-06-06 18:37:46 +08:00
|
|
|
QETH_DBF_MESSAGE(2, "Error (0x%04x) while setting routing type"
|
|
|
|
" on %s. Type set to 'no router'.\n", rc,
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qeth_l3_setrouting_v6(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "setrtg6");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_IPV6))
|
|
|
|
return 0;
|
2013-03-19 04:04:43 +08:00
|
|
|
rc = qeth_l3_correct_routing_type(card, &card->options.route6.type,
|
2008-02-15 16:19:42 +08:00
|
|
|
QETH_PROT_IPV6);
|
2013-03-19 04:04:43 +08:00
|
|
|
if (rc)
|
|
|
|
return rc;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
rc = qeth_l3_send_setrouting(card, card->options.route6.type,
|
|
|
|
QETH_PROT_IPV6);
|
|
|
|
if (rc) {
|
|
|
|
card->options.route6.type = NO_ROUTER;
|
2008-06-06 18:37:46 +08:00
|
|
|
QETH_DBF_MESSAGE(2, "Error (0x%04x) while setting routing type"
|
|
|
|
" on %s. Type set to 'no router'.\n", rc,
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IP address takeover related functions
|
|
|
|
*/
|
2017-12-14 01:56:32 +08: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;
|
|
|
|
if (qeth_l3_is_addr_covered_by_ipato(card, addr))
|
|
|
|
addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
|
|
|
|
else
|
|
|
|
addr->set_flags &= ~QETH_IPA_SETIP_TAKEOVER_FLAG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
static void qeth_l3_clear_ipato_list(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct qeth_ipato_entry *ipatoe, *tmp;
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) {
|
|
|
|
list_del(&ipatoe->entry);
|
|
|
|
kfree(ipatoe);
|
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2017-12-14 01:56:32 +08:00
|
|
|
qeth_l3_update_ipato(card);
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_unlock_bh(&card->ip_lock);
|
2008-02-15 16:19:42 +08: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 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "addipato");
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
|
2008-02-15 16:19:42 +08: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 22:18:58 +08:00
|
|
|
|
2017-12-14 01:56:32 +08:00
|
|
|
if (!rc) {
|
2008-02-15 16:19:42 +08:00
|
|
|
list_add_tail(&new->entry, &card->ipato.entries);
|
2017-12-14 01:56:32 +08:00
|
|
|
qeth_l3_update_ipato(card);
|
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_unlock_bh(&card->ip_lock);
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qeth_l3_del_ipato_entry(struct qeth_card *card,
|
|
|
|
enum qeth_prot_versions proto, u8 *addr, int mask_bits)
|
|
|
|
{
|
|
|
|
struct qeth_ipato_entry *ipatoe, *tmp;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "delipato");
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
|
2008-02-15 16:19:42 +08: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 01:56:32 +08:00
|
|
|
qeth_l3_update_ipato(card);
|
2008-02-15 16:19:42 +08:00
|
|
|
kfree(ipatoe);
|
|
|
|
}
|
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* VIPA related functions
|
|
|
|
*/
|
|
|
|
int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
|
|
|
|
const u8 *addr)
|
|
|
|
{
|
|
|
|
struct qeth_ipaddr *ipaddr;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
ipaddr = qeth_l3_get_addr_buffer(proto);
|
|
|
|
if (ipaddr) {
|
|
|
|
if (proto == QETH_PROT_IPV4) {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "addvipa4");
|
2008-02-15 16:19:42 +08:00
|
|
|
memcpy(&ipaddr->u.a4.addr, addr, 4);
|
|
|
|
ipaddr->u.a4.mask = 0;
|
|
|
|
} else if (proto == QETH_PROT_IPV6) {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "addvipa6");
|
2008-02-15 16:19:42 +08:00
|
|
|
memcpy(&ipaddr->u.a6.addr, addr, 16);
|
|
|
|
ipaddr->u.a6.pfxlen = 0;
|
|
|
|
}
|
|
|
|
ipaddr->type = QETH_IP_TYPE_VIPA;
|
|
|
|
ipaddr->set_flags = QETH_IPA_SETIP_VIPA_FLAG;
|
|
|
|
ipaddr->del_flags = QETH_IPA_DELIP_VIPA_FLAG;
|
|
|
|
} else
|
|
|
|
return -ENOMEM;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
|
2016-09-15 20:39:27 +08:00
|
|
|
if (qeth_l3_ip_from_hash(card, ipaddr))
|
2008-02-15 16:19:42 +08:00
|
|
|
rc = -EEXIST;
|
2016-06-16 22:18:58 +08:00
|
|
|
else
|
|
|
|
qeth_l3_add_ip(card, ipaddr);
|
|
|
|
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
|
|
|
|
|
|
|
kfree(ipaddr);
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qeth_l3_del_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
|
|
|
|
const u8 *addr)
|
|
|
|
{
|
|
|
|
struct qeth_ipaddr *ipaddr;
|
|
|
|
|
|
|
|
ipaddr = qeth_l3_get_addr_buffer(proto);
|
|
|
|
if (ipaddr) {
|
|
|
|
if (proto == QETH_PROT_IPV4) {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "delvipa4");
|
2008-02-15 16:19:42 +08:00
|
|
|
memcpy(&ipaddr->u.a4.addr, addr, 4);
|
|
|
|
ipaddr->u.a4.mask = 0;
|
|
|
|
} else if (proto == QETH_PROT_IPV6) {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "delvipa6");
|
2008-02-15 16:19:42 +08:00
|
|
|
memcpy(&ipaddr->u.a6.addr, addr, 16);
|
|
|
|
ipaddr->u.a6.pfxlen = 0;
|
|
|
|
}
|
|
|
|
ipaddr->type = QETH_IP_TYPE_VIPA;
|
|
|
|
} else
|
|
|
|
return;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
qeth_l3_delete_ip(card, ipaddr);
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
|
|
|
|
|
|
|
kfree(ipaddr);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* proxy ARP related functions
|
|
|
|
*/
|
|
|
|
int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
|
|
|
|
const u8 *addr)
|
|
|
|
{
|
|
|
|
struct qeth_ipaddr *ipaddr;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
ipaddr = qeth_l3_get_addr_buffer(proto);
|
|
|
|
if (ipaddr) {
|
|
|
|
if (proto == QETH_PROT_IPV4) {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "addrxip4");
|
2008-02-15 16:19:42 +08:00
|
|
|
memcpy(&ipaddr->u.a4.addr, addr, 4);
|
|
|
|
ipaddr->u.a4.mask = 0;
|
|
|
|
} else if (proto == QETH_PROT_IPV6) {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "addrxip6");
|
2008-02-15 16:19:42 +08:00
|
|
|
memcpy(&ipaddr->u.a6.addr, addr, 16);
|
|
|
|
ipaddr->u.a6.pfxlen = 0;
|
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
ipaddr->type = QETH_IP_TYPE_RXIP;
|
|
|
|
ipaddr->set_flags = QETH_IPA_SETIP_TAKEOVER_FLAG;
|
|
|
|
ipaddr->del_flags = 0;
|
|
|
|
} else
|
|
|
|
return -ENOMEM;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
|
2016-09-15 20:39:27 +08:00
|
|
|
if (qeth_l3_ip_from_hash(card, ipaddr))
|
2008-02-15 16:19:42 +08:00
|
|
|
rc = -EEXIST;
|
2016-06-16 22:18:58 +08:00
|
|
|
else
|
|
|
|
qeth_l3_add_ip(card, ipaddr);
|
|
|
|
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
|
|
|
|
|
|
|
kfree(ipaddr);
|
|
|
|
|
|
|
|
return rc;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
|
|
|
|
const u8 *addr)
|
|
|
|
{
|
|
|
|
struct qeth_ipaddr *ipaddr;
|
|
|
|
|
|
|
|
ipaddr = qeth_l3_get_addr_buffer(proto);
|
|
|
|
if (ipaddr) {
|
|
|
|
if (proto == QETH_PROT_IPV4) {
|
2017-08-15 23:02:49 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "delrxip4");
|
2008-02-15 16:19:42 +08:00
|
|
|
memcpy(&ipaddr->u.a4.addr, addr, 4);
|
|
|
|
ipaddr->u.a4.mask = 0;
|
|
|
|
} else if (proto == QETH_PROT_IPV6) {
|
2017-08-15 23:02:49 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "delrxip6");
|
2008-02-15 16:19:42 +08:00
|
|
|
memcpy(&ipaddr->u.a6.addr, addr, 16);
|
|
|
|
ipaddr->u.a6.pfxlen = 0;
|
|
|
|
}
|
|
|
|
ipaddr->type = QETH_IP_TYPE_RXIP;
|
|
|
|
} else
|
|
|
|
return;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
qeth_l3_delete_ip(card, ipaddr);
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
|
|
|
|
|
|
|
kfree(ipaddr);
|
2008-02-15 16:19:42 +08: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;
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
if (addr->proto == QETH_PROT_IPV4) {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "setaddr4");
|
|
|
|
QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int));
|
2008-02-15 16:19:42 +08:00
|
|
|
} else if (addr->proto == QETH_PROT_IPV6) {
|
2010-06-22 06:57:05 +08: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 16:19:42 +08:00
|
|
|
} else {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "setaddr?");
|
|
|
|
QETH_CARD_HEX(card, 3, addr, sizeof(struct qeth_ipaddr));
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
do {
|
|
|
|
if (addr->is_multicast)
|
|
|
|
rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_SETIPM);
|
|
|
|
else
|
|
|
|
rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_SETIP,
|
|
|
|
addr->set_flags);
|
|
|
|
if (rc)
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "failed");
|
2008-02-15 16:19:42 +08:00
|
|
|
} while ((--cnt > 0) && rc);
|
|
|
|
if (rc) {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "FAILED");
|
2008-02-15 16:19:42 +08:00
|
|
|
qeth_l3_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Registering IP address %s failed\n", buf);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_deregister_addr_entry(struct qeth_card *card,
|
|
|
|
struct qeth_ipaddr *addr)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (addr->proto == QETH_PROT_IPV4) {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "deladdr4");
|
|
|
|
QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int));
|
2008-02-15 16:19:42 +08:00
|
|
|
} else if (addr->proto == QETH_PROT_IPV6) {
|
2010-06-22 06:57:05 +08: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 16:19:42 +08:00
|
|
|
} else {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "deladdr?");
|
|
|
|
QETH_CARD_HEX(card, 3, addr, sizeof(struct qeth_ipaddr));
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
if (addr->is_multicast)
|
|
|
|
rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_DELIPM);
|
|
|
|
else
|
|
|
|
rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP,
|
|
|
|
addr->del_flags);
|
2008-07-14 15:59:31 +08:00
|
|
|
if (rc)
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "failed");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-08-15 23:02:46 +08:00
|
|
|
static u8 qeth_l3_get_qeth_hdr_flags4(int cast_type)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
if (cast_type == RTN_MULTICAST)
|
|
|
|
return QETH_CAST_MULTICAST;
|
|
|
|
if (cast_type == RTN_BROADCAST)
|
|
|
|
return QETH_CAST_BROADCAST;
|
|
|
|
return QETH_CAST_UNICAST;
|
|
|
|
}
|
|
|
|
|
2017-08-15 23:02:46 +08:00
|
|
|
static u8 qeth_l3_get_qeth_hdr_flags6(int cast_type)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
u8 ct = QETH_HDR_PASSTHRU | QETH_HDR_IPV6;
|
|
|
|
if (cast_type == RTN_MULTICAST)
|
|
|
|
return ct | QETH_CAST_MULTICAST;
|
|
|
|
if (cast_type == RTN_ANYCAST)
|
|
|
|
return ct | QETH_CAST_ANYCAST;
|
|
|
|
if (cast_type == RTN_BROADCAST)
|
|
|
|
return ct | QETH_CAST_BROADCAST;
|
|
|
|
return ct | QETH_CAST_UNICAST;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_setadapter_parms(struct qeth_card *card)
|
|
|
|
{
|
2017-08-15 23:02:41 +08:00
|
|
|
int rc = 0;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2008-04-01 16:26:58 +08:00
|
|
|
QETH_DBF_TEXT(SETUP, 2, "setadprm");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
if (qeth_adp_supported(card, IPA_SETADP_ALTER_MAC_ADDRESS)) {
|
|
|
|
rc = qeth_setadpparms_change_macaddr(card);
|
|
|
|
if (rc)
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev, "Reading the adapter MAC"
|
2009-01-05 09:36:32 +08:00
|
|
|
" address failed\n");
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card,
|
|
|
|
enum qeth_ipa_funcs ipa_func, __u16 cmd_code)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "simassp6");
|
2015-12-11 19:27:54 +08:00
|
|
|
iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code,
|
2008-02-15 16:19:42 +08:00
|
|
|
0, QETH_PROT_IPV6);
|
2015-01-21 20:39:10 +08:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_setassparms(card, iob, 0, 0,
|
2016-06-16 22:18:59 +08:00
|
|
|
qeth_setassparms_cb, NULL);
|
2008-02-15 16:19:42 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "ipaarp");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_info(&card->gdev->dev,
|
|
|
|
"ARP processing not supported on %s!\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
|
|
|
|
IPA_CMD_ASS_START, 0);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (rc) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Starting ARP processing support for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_source_mac(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "stsrcmac");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_SOURCE_MAC)) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_info(&card->gdev->dev,
|
2009-01-05 09:34:52 +08:00
|
|
|
"Inbound source MAC-address not supported on %s\n",
|
2008-12-25 20:39:49 +08:00
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC,
|
2008-02-15 16:19:42 +08:00
|
|
|
IPA_CMD_ASS_START, 0);
|
|
|
|
if (rc)
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev,
|
2009-01-05 09:34:52 +08:00
|
|
|
"Starting source MAC-address support for %s failed\n",
|
2008-12-25 20:39:49 +08:00
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_vlan(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "strtvlan");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_FULL_VLAN)) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_info(&card->gdev->dev,
|
|
|
|
"VLAN not supported on %s\n", QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO,
|
2008-02-15 16:19:42 +08:00
|
|
|
IPA_CMD_ASS_START, 0);
|
|
|
|
if (rc) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Starting VLAN support for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
} else {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_info(&card->gdev->dev, "VLAN enabled\n");
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_multicast(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "stmcast");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_MULTICASTING)) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_info(&card->gdev->dev,
|
|
|
|
"Multicast not supported on %s\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING,
|
2008-02-15 16:19:42 +08:00
|
|
|
IPA_CMD_ASS_START, 0);
|
|
|
|
if (rc) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Starting multicast support for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
} else {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_info(&card->gdev->dev, "Multicast enabled\n");
|
2008-02-15 16:19:42 +08:00
|
|
|
card->dev->flags |= IFF_MULTICAST;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_softsetup_ipv6(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "softipv6");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2011-05-13 02:45:02 +08:00
|
|
|
rc = qeth_query_ipassists(card, QETH_PROT_IPV6);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (rc) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_err(&card->gdev->dev,
|
|
|
|
"Activating IPv6 support for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
return rc;
|
|
|
|
}
|
2017-06-06 20:33:44 +08:00
|
|
|
|
|
|
|
if (card->info.type == QETH_CARD_TYPE_IQD)
|
|
|
|
goto out;
|
|
|
|
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_IPV6,
|
2008-02-15 16:19:42 +08:00
|
|
|
IPA_CMD_ASS_START, 3);
|
|
|
|
if (rc) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_err(&card->gdev->dev,
|
|
|
|
"Activating IPv6 support for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_IPV6,
|
|
|
|
IPA_CMD_ASS_START);
|
|
|
|
if (rc) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_err(&card->gdev->dev,
|
|
|
|
"Activating IPv6 support for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_PASSTHRU,
|
|
|
|
IPA_CMD_ASS_START);
|
|
|
|
if (rc) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Enabling the passthrough mode for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
out:
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_info(&card->gdev->dev, "IPV6 enabled\n");
|
2008-02-15 16:19:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_ipv6(struct qeth_card *card)
|
|
|
|
{
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "strtipv6");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card, IPA_IPV6)) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_info(&card->gdev->dev,
|
|
|
|
"IPv6 not supported on %s\n", QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2017-12-21 03:10:58 +08:00
|
|
|
return qeth_l3_softsetup_ipv6(card);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "stbrdcst");
|
2008-02-15 16:19:42 +08:00
|
|
|
card->info.broadcast_capable = 0;
|
|
|
|
if (!qeth_is_supported(card, IPA_FILTERING)) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_info(&card->gdev->dev,
|
|
|
|
"Broadcast not supported on %s\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
rc = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
|
2008-02-15 16:19:42 +08:00
|
|
|
IPA_CMD_ASS_START, 0);
|
|
|
|
if (rc) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev, "Enabling broadcast filtering for "
|
|
|
|
"%s failed\n", QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
|
2008-02-15 16:19:42 +08:00
|
|
|
IPA_CMD_ASS_CONFIGURE, 1);
|
|
|
|
if (rc) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"Setting up broadcast filtering for %s failed\n",
|
|
|
|
QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_info(&card->gdev->dev, "Broadcast enabled\n");
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
|
2008-02-15 16:19:42 +08:00
|
|
|
IPA_CMD_ASS_ENABLE, 1);
|
|
|
|
if (rc) {
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev, "Setting up broadcast echo "
|
|
|
|
"filtering for %s failed\n", QETH_CARD_IFNAME(card));
|
2008-02-15 16:19:42 +08: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_start_ipassists(struct qeth_card *card)
|
|
|
|
{
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "strtipas");
|
2009-11-12 08:11:41 +08:00
|
|
|
|
2013-01-21 10:30:20 +08:00
|
|
|
if (qeth_set_access_ctrl_online(card, 0))
|
|
|
|
return -EIO;
|
2008-02-15 16:19:42 +08: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*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_iqd_read_initial_mac_cb(struct qeth_card *card,
|
|
|
|
struct qeth_reply *reply, unsigned long data)
|
|
|
|
{
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
|
|
|
|
|
|
|
cmd = (struct qeth_ipa_cmd *) data;
|
|
|
|
if (cmd->hdr.return_code == 0)
|
|
|
|
memcpy(card->dev->dev_addr,
|
|
|
|
cmd->data.create_destroy_addr.unique_id, ETH_ALEN);
|
|
|
|
else
|
2012-07-13 13:33:10 +08:00
|
|
|
eth_random_addr(card->dev->dev_addr);
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_iqd_read_initial_mac(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
|
|
|
|
2008-04-01 16:26:58 +08:00
|
|
|
QETH_DBF_TEXT(SETUP, 2, "hsrmac");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_CREATE_ADDR,
|
|
|
|
QETH_PROT_IPV6);
|
2015-01-21 20:39:10 +08:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2008-02-15 16:19:42 +08:00
|
|
|
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
|
|
|
*((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) =
|
|
|
|
card->info.unique_id;
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
|
|
|
|
|
|
|
cmd = (struct qeth_ipa_cmd *) data;
|
|
|
|
if (cmd->hdr.return_code == 0)
|
|
|
|
card->info.unique_id = *((__u16 *)
|
|
|
|
&cmd->data.create_destroy_addr.unique_id[6]);
|
|
|
|
else {
|
|
|
|
card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
|
|
|
|
UNIQUE_ID_NOT_BY_CARD;
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev, "The network adapter failed to "
|
|
|
|
"generate a unique ID\n");
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_get_unique_id(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
struct qeth_ipa_cmd *cmd;
|
|
|
|
|
2008-04-01 16:26:58 +08:00
|
|
|
QETH_DBF_TEXT(SETUP, 2, "guniqeid");
|
2008-02-15 16:19:42 +08: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_CREATE_ADDR,
|
|
|
|
QETH_PROT_IPV6);
|
2015-01-21 20:39:10 +08:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2008-02-15 16:19:42 +08:00
|
|
|
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
|
|
|
*((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) =
|
|
|
|
card->info.unique_id;
|
|
|
|
|
|
|
|
rc = qeth_send_ipa_cmd(card, iob, qeth_l3_get_unique_id_cb, NULL);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2010-01-11 10:50:50 +08: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;
|
|
|
|
|
|
|
|
QETH_DBF_TEXT(SETUP, 2, "diastrcb");
|
|
|
|
|
|
|
|
cmd = (struct qeth_ipa_cmd *)data;
|
|
|
|
rc = cmd->hdr.return_code;
|
2010-03-09 04:36:55 +08:00
|
|
|
if (rc)
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT_(card, 2, "dxter%x", rc);
|
2010-01-11 10:50:50 +08:00
|
|
|
switch (cmd->data.diagass.action) {
|
|
|
|
case QETH_DIAGS_CMD_TRACE_QUERY:
|
|
|
|
break;
|
|
|
|
case QETH_DIAGS_CMD_TRACE_DISABLE:
|
2010-03-09 04:36:55 +08: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 10:50:50 +08:00
|
|
|
break;
|
|
|
|
case QETH_DIAGS_CMD_TRACE_ENABLE:
|
2010-03-09 04:36:55 +08: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 10:50:50 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
QETH_DBF_MESSAGE(2, "Unknown sniffer action (0x%04x) on %s\n",
|
|
|
|
cmd->data.diagass.action, QETH_CARD_IFNAME(card));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
QETH_DBF_TEXT(SETUP, 2, "diagtrac");
|
|
|
|
|
|
|
|
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0);
|
2015-01-21 20:39:10 +08:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2010-01-11 10:50:50 +08:00
|
|
|
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
|
|
|
cmd->data.diagass.subcmd_len = 16;
|
|
|
|
cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRACE;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
static void qeth_l3_mark_all_mc_to_be_deleted(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
hash_for_each(card->ip_mc_htable, i, addr, hnode)
|
|
|
|
addr->disp_flag = QETH_DISP_ADDR_DELETE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qeth_l3_add_all_new_mc(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
struct hlist_node *tmp;
|
|
|
|
int i;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) {
|
|
|
|
if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
|
|
|
|
rc = qeth_l3_register_addr_entry(card, addr);
|
|
|
|
if (!rc || (rc == IPA_RC_LAN_OFFLINE))
|
|
|
|
addr->ref_counter = 1;
|
|
|
|
else {
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qeth_l3_delete_nonused_mc(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
struct hlist_node *tmp;
|
|
|
|
int i;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
hash_for_each_safe(card->ip_mc_htable, i, tmp, addr, hnode) {
|
|
|
|
if (addr->disp_flag == QETH_DISP_ADDR_DELETE) {
|
|
|
|
rc = qeth_l3_deregister_addr_entry(card, addr);
|
|
|
|
if (!rc || (rc == IPA_RC_MC_ADDR_NOT_FOUND)) {
|
|
|
|
hash_del(&addr->hnode);
|
|
|
|
kfree(addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
struct ip_mc_list *im4;
|
2016-06-16 22:18:58 +08:00
|
|
|
struct qeth_ipaddr *tmp, *ipm;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "addmc");
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
tmp = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
|
2017-12-21 03:10:57 +08:00
|
|
|
if (!tmp)
|
|
|
|
return;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2010-11-26 10:41:17 +08:00
|
|
|
for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL;
|
|
|
|
im4 = rcu_dereference(im4->next_rcu)) {
|
2017-12-21 03:10:57 +08:00
|
|
|
ip_eth_mc_map(im4->multiaddr, tmp->mac);
|
2017-04-07 15:15:36 +08:00
|
|
|
tmp->u.a4.addr = be32_to_cpu(im4->multiaddr);
|
2017-12-01 17:14:49 +08:00
|
|
|
tmp->is_multicast = 1;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
ipm = qeth_l3_ip_from_hash(card, tmp);
|
|
|
|
if (ipm) {
|
|
|
|
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
|
|
|
} else {
|
|
|
|
ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
|
|
|
|
if (!ipm)
|
|
|
|
continue;
|
2017-12-21 03:10:57 +08:00
|
|
|
memcpy(ipm->mac, tmp->mac, sizeof(tmp->mac));
|
2017-04-07 15:15:36 +08:00
|
|
|
ipm->u.a4.addr = be32_to_cpu(im4->multiaddr);
|
2016-06-16 22:18:58 +08:00
|
|
|
ipm->is_multicast = 1;
|
|
|
|
ipm->disp_flag = QETH_DISP_ADDR_ADD;
|
|
|
|
hash_add(card->ip_mc_htable,
|
|
|
|
&ipm->hnode, qeth_l3_ipaddr_hash(ipm));
|
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
kfree(tmp);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2013-01-04 06:48:58 +08:00
|
|
|
/* called with rcu_read_lock */
|
2008-02-15 16:19:42 +08:00
|
|
|
static void qeth_l3_add_vlan_mc(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct in_device *in_dev;
|
2011-07-20 12:54:41 +08:00
|
|
|
u16 vid;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "addmcvl");
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2011-07-20 12:54:41 +08:00
|
|
|
if (!qeth_is_supported(card, IPA_FULL_VLAN))
|
2008-02-15 16:19:42 +08:00
|
|
|
return;
|
|
|
|
|
2011-07-20 12:54:41 +08:00
|
|
|
for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
|
|
|
|
struct net_device *netdev;
|
|
|
|
|
2014-05-09 14:58:05 +08:00
|
|
|
netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
|
2013-04-21 08:09:34 +08:00
|
|
|
vid);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (netdev == NULL ||
|
|
|
|
!(netdev->flags & IFF_UP))
|
|
|
|
continue;
|
2013-01-04 06:48:58 +08:00
|
|
|
in_dev = __in_dev_get_rcu(netdev);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (!in_dev)
|
|
|
|
continue;
|
2016-06-16 22:18:58 +08:00
|
|
|
qeth_l3_add_mc_to_hash(card, in_dev);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qeth_l3_add_multicast_ipv4(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct in_device *in4_dev;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "chkmcv4");
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2010-11-26 10:41:17 +08:00
|
|
|
rcu_read_lock();
|
2013-01-04 06:48:58 +08:00
|
|
|
in4_dev = __in_dev_get_rcu(card->dev);
|
|
|
|
if (in4_dev == NULL)
|
|
|
|
goto unlock;
|
2016-06-16 22:18:58 +08:00
|
|
|
qeth_l3_add_mc_to_hash(card, in4_dev);
|
2008-02-15 16:19:42 +08:00
|
|
|
qeth_l3_add_vlan_mc(card);
|
2013-01-04 06:48:58 +08:00
|
|
|
unlock:
|
2010-11-26 10:41:17 +08:00
|
|
|
rcu_read_unlock();
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2017-12-21 03:10:58 +08:00
|
|
|
static void qeth_l3_add_mc6_to_hash(struct qeth_card *card,
|
|
|
|
struct inet6_dev *in6_dev)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
struct qeth_ipaddr *ipm;
|
|
|
|
struct ifmcaddr6 *im6;
|
2016-06-16 22:18:58 +08:00
|
|
|
struct qeth_ipaddr *tmp;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "addmc6");
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
tmp = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
|
2017-12-21 03:10:57 +08:00
|
|
|
if (!tmp)
|
|
|
|
return;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
for (im6 = in6_dev->mc_list; im6 != NULL; im6 = im6->next) {
|
2017-12-21 03:10:57 +08:00
|
|
|
ipv6_eth_mc_map(&im6->mca_addr, tmp->mac);
|
2016-06-16 22:18:58 +08:00
|
|
|
memcpy(&tmp->u.a6.addr, &im6->mca_addr.s6_addr,
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
tmp->is_multicast = 1;
|
|
|
|
|
|
|
|
ipm = qeth_l3_ip_from_hash(card, tmp);
|
|
|
|
if (ipm) {
|
|
|
|
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
|
|
|
|
if (!ipm)
|
|
|
|
continue;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2017-12-21 03:10:57 +08:00
|
|
|
memcpy(ipm->mac, tmp->mac, sizeof(tmp->mac));
|
2008-02-15 16:19:42 +08:00
|
|
|
memcpy(&ipm->u.a6.addr, &im6->mca_addr.s6_addr,
|
|
|
|
sizeof(struct in6_addr));
|
2016-06-16 22:18:58 +08:00
|
|
|
ipm->is_multicast = 1;
|
|
|
|
ipm->disp_flag = QETH_DISP_ADDR_ADD;
|
|
|
|
hash_add(card->ip_mc_htable,
|
|
|
|
&ipm->hnode, qeth_l3_ipaddr_hash(ipm));
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
kfree(tmp);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2013-01-04 06:48:58 +08:00
|
|
|
/* called with rcu_read_lock */
|
2008-02-15 16:19:42 +08:00
|
|
|
static void qeth_l3_add_vlan_mc6(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct inet6_dev *in_dev;
|
2011-07-20 12:54:41 +08:00
|
|
|
u16 vid;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "admc6vl");
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2011-07-20 12:54:41 +08:00
|
|
|
if (!qeth_is_supported(card, IPA_FULL_VLAN))
|
2008-02-15 16:19:42 +08:00
|
|
|
return;
|
|
|
|
|
2011-07-20 12:54:41 +08:00
|
|
|
for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
|
|
|
|
struct net_device *netdev;
|
|
|
|
|
2014-05-09 14:58:05 +08:00
|
|
|
netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
|
2013-04-21 08:09:34 +08:00
|
|
|
vid);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (netdev == NULL ||
|
|
|
|
!(netdev->flags & IFF_UP))
|
|
|
|
continue;
|
|
|
|
in_dev = in6_dev_get(netdev);
|
|
|
|
if (!in_dev)
|
|
|
|
continue;
|
|
|
|
read_lock_bh(&in_dev->lock);
|
2016-06-16 22:18:58 +08:00
|
|
|
qeth_l3_add_mc6_to_hash(card, in_dev);
|
2008-02-15 16:19:42 +08:00
|
|
|
read_unlock_bh(&in_dev->lock);
|
|
|
|
in6_dev_put(in_dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qeth_l3_add_multicast_ipv6(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct inet6_dev *in6_dev;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "chkmcv6");
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
if (!qeth_is_supported(card, IPA_IPV6))
|
|
|
|
return ;
|
|
|
|
in6_dev = in6_dev_get(card->dev);
|
2016-06-16 22:18:58 +08:00
|
|
|
if (!in6_dev)
|
2008-02-15 16:19:42 +08:00
|
|
|
return;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2013-01-04 06:48:58 +08:00
|
|
|
rcu_read_lock();
|
2008-02-15 16:19:42 +08:00
|
|
|
read_lock_bh(&in6_dev->lock);
|
2016-06-16 22:18:58 +08:00
|
|
|
qeth_l3_add_mc6_to_hash(card, in6_dev);
|
2008-02-15 16:19:42 +08:00
|
|
|
qeth_l3_add_vlan_mc6(card);
|
|
|
|
read_unlock_bh(&in6_dev->lock);
|
2013-01-04 06:48:58 +08:00
|
|
|
rcu_read_unlock();
|
2008-02-15 16:19:42 +08:00
|
|
|
in6_dev_put(in6_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qeth_l3_free_vlan_addresses4(struct qeth_card *card,
|
|
|
|
unsigned short vid)
|
|
|
|
{
|
|
|
|
struct in_device *in_dev;
|
|
|
|
struct in_ifaddr *ifa;
|
|
|
|
struct qeth_ipaddr *addr;
|
2011-07-20 12:54:41 +08:00
|
|
|
struct net_device *netdev;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "frvaddr4");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2014-05-09 14:58:05 +08:00
|
|
|
netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid);
|
2012-07-25 06:34:29 +08:00
|
|
|
if (!netdev)
|
|
|
|
return;
|
2011-07-20 12:54:41 +08:00
|
|
|
in_dev = in_dev_get(netdev);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (!in_dev)
|
|
|
|
return;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
|
|
|
|
if (!addr)
|
2017-10-18 23:40:17 +08:00
|
|
|
goto out;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
|
2017-04-07 15:15:36 +08:00
|
|
|
addr->u.a4.addr = be32_to_cpu(ifa->ifa_address);
|
|
|
|
addr->u.a4.mask = be32_to_cpu(ifa->ifa_mask);
|
2016-06-16 22:18:58 +08:00
|
|
|
addr->type = QETH_IP_TYPE_NORMAL;
|
|
|
|
qeth_l3_delete_ip(card, addr);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
|
|
|
|
|
|
|
kfree(addr);
|
2017-10-18 23:40:17 +08:00
|
|
|
out:
|
2008-02-15 16:19:42 +08:00
|
|
|
in_dev_put(in_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qeth_l3_free_vlan_addresses6(struct qeth_card *card,
|
2017-12-21 03:10:58 +08:00
|
|
|
unsigned short vid)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
struct inet6_dev *in6_dev;
|
|
|
|
struct inet6_ifaddr *ifa;
|
|
|
|
struct qeth_ipaddr *addr;
|
2011-07-20 12:54:41 +08:00
|
|
|
struct net_device *netdev;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "frvaddr6");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2014-05-09 14:58:05 +08:00
|
|
|
netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid);
|
2012-07-25 06:34:29 +08:00
|
|
|
if (!netdev)
|
|
|
|
return;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2011-07-20 12:54:41 +08:00
|
|
|
in6_dev = in6_dev_get(netdev);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (!in6_dev)
|
|
|
|
return;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
|
|
|
|
if (!addr)
|
2017-10-18 23:40:17 +08:00
|
|
|
goto out;
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
|
2010-03-23 00:59:48 +08:00
|
|
|
list_for_each_entry(ifa, &in6_dev->addr_list, if_list) {
|
2016-06-16 22:18:58 +08:00
|
|
|
memcpy(&addr->u.a6.addr, &ifa->addr,
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
addr->u.a6.pfxlen = ifa->prefix_len;
|
|
|
|
addr->type = QETH_IP_TYPE_NORMAL;
|
|
|
|
qeth_l3_delete_ip(card, addr);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
|
|
|
|
|
|
|
kfree(addr);
|
2017-10-18 23:40:17 +08:00
|
|
|
out:
|
2008-02-15 16:19:42 +08:00
|
|
|
in6_dev_put(in6_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qeth_l3_free_vlan_addresses(struct qeth_card *card,
|
|
|
|
unsigned short vid)
|
|
|
|
{
|
2013-01-04 06:48:58 +08:00
|
|
|
rcu_read_lock();
|
2008-02-15 16:19:42 +08:00
|
|
|
qeth_l3_free_vlan_addresses4(card, vid);
|
|
|
|
qeth_l3_free_vlan_addresses6(card, vid);
|
2013-01-04 06:48:58 +08:00
|
|
|
rcu_read_unlock();
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2013-04-19 10:04:28 +08:00
|
|
|
static int qeth_l3_vlan_rx_add_vid(struct net_device *dev,
|
|
|
|
__be16 proto, u16 vid)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
2008-07-26 17:24:10 +08:00
|
|
|
struct qeth_card *card = dev->ml_priv;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2011-07-20 12:54:41 +08:00
|
|
|
set_bit(vid, card->active_vlans);
|
2011-12-09 08:52:37 +08:00
|
|
|
return 0;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2013-04-19 10:04:28 +08:00
|
|
|
static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev,
|
|
|
|
__be16 proto, u16 vid)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
2008-07-26 17:24:10 +08:00
|
|
|
struct qeth_card *card = dev->ml_priv;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT_(card, 4, "kid:%d", vid);
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2009-03-25 04:57:18 +08:00
|
|
|
if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "kidREC");
|
2011-12-09 08:52:37 +08:00
|
|
|
return 0;
|
2009-03-25 04:57:18 +08:00
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
/* unregister IP addresses of vlan device */
|
|
|
|
qeth_l3_free_vlan_addresses(card, vid);
|
2011-07-20 12:54:41 +08:00
|
|
|
clear_bit(vid, card->active_vlans);
|
2008-02-15 16:19:42 +08:00
|
|
|
qeth_l3_set_multicast_list(card->dev);
|
2011-12-09 08:52:37 +08:00
|
|
|
return 0;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2017-09-19 03:18:21 +08:00
|
|
|
static void qeth_l3_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
|
|
|
|
struct qeth_hdr *hdr)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
unsigned char tg_addr[MAX_ADDR_LEN];
|
|
|
|
|
|
|
|
if (!(hdr->hdr.l3.flags & QETH_HDR_PASSTHRU)) {
|
2017-12-21 03:10:57 +08:00
|
|
|
u16 prot = (hdr->hdr.l3.flags & QETH_HDR_IPV6) ? ETH_P_IPV6 :
|
|
|
|
ETH_P_IP;
|
|
|
|
|
|
|
|
skb_reset_network_header(skb);
|
2008-02-15 16:19:42 +08:00
|
|
|
switch (hdr->hdr.l3.flags & QETH_HDR_CAST_MASK) {
|
|
|
|
case QETH_CAST_MULTICAST:
|
2017-12-21 03:10:58 +08:00
|
|
|
if (prot == ETH_P_IP)
|
2017-12-21 03:10:57 +08:00
|
|
|
ip_eth_mc_map(ip_hdr(skb)->daddr, tg_addr);
|
2017-12-21 03:10:58 +08:00
|
|
|
else
|
|
|
|
ipv6_eth_mc_map(&ipv6_hdr(skb)->daddr, tg_addr);
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
card->stats.multicast++;
|
|
|
|
skb->pkt_type = PACKET_MULTICAST;
|
|
|
|
break;
|
|
|
|
case QETH_CAST_BROADCAST:
|
|
|
|
memcpy(tg_addr, card->dev->broadcast,
|
|
|
|
card->dev->addr_len);
|
|
|
|
card->stats.multicast++;
|
|
|
|
skb->pkt_type = PACKET_BROADCAST;
|
|
|
|
break;
|
|
|
|
case QETH_CAST_UNICAST:
|
|
|
|
case QETH_CAST_ANYCAST:
|
|
|
|
case QETH_CAST_NOCAST:
|
|
|
|
default:
|
2010-01-11 10:50:50 +08:00
|
|
|
if (card->options.sniffer)
|
|
|
|
skb->pkt_type = PACKET_OTHERHOST;
|
|
|
|
else
|
|
|
|
skb->pkt_type = PACKET_HOST;
|
2008-02-15 16:19:42 +08:00
|
|
|
memcpy(tg_addr, card->dev->dev_addr,
|
|
|
|
card->dev->addr_len);
|
|
|
|
}
|
2009-01-05 09:34:52 +08:00
|
|
|
if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
|
|
|
|
card->dev->header_ops->create(skb, card->dev, prot,
|
|
|
|
tg_addr, &hdr->hdr.l3.dest_addr[2],
|
|
|
|
card->dev->addr_len);
|
|
|
|
else
|
|
|
|
card->dev->header_ops->create(skb, card->dev, prot,
|
|
|
|
tg_addr, "FAKELL", card->dev->addr_len);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2012-05-11 03:50:52 +08:00
|
|
|
skb->protocol = eth_type_trans(skb, card->dev);
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2017-09-19 03:18:21 +08:00
|
|
|
/* copy VLAN tag from hdr into skb */
|
|
|
|
if (!card->options.sniffer &&
|
|
|
|
(hdr->hdr.l3.ext_flags & (QETH_HDR_EXT_VLAN_FRAME |
|
|
|
|
QETH_HDR_EXT_INCLUDE_VLAN_TAG))) {
|
|
|
|
u16 tag = (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_VLAN_FRAME) ?
|
|
|
|
hdr->hdr.l3.vlan_id :
|
|
|
|
*((u16 *)&hdr->hdr.l3.dest_addr[12]);
|
|
|
|
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tag);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2011-05-13 02:45:01 +08:00
|
|
|
if (card->dev->features & NETIF_F_RXCSUM) {
|
2008-02-15 16:19:42 +08:00
|
|
|
if ((hdr->hdr.l3.ext_flags &
|
2009-05-20 05:38:40 +08:00
|
|
|
(QETH_HDR_EXT_CSUM_HDR_REQ |
|
|
|
|
QETH_HDR_EXT_CSUM_TRANSP_REQ)) ==
|
|
|
|
(QETH_HDR_EXT_CSUM_HDR_REQ |
|
|
|
|
QETH_HDR_EXT_CSUM_TRANSP_REQ))
|
2008-02-15 16:19:42 +08:00
|
|
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
|
|
else
|
2009-05-20 05:38:40 +08:00
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
2011-05-13 02:45:01 +08:00
|
|
|
} else
|
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2010-09-08 05:14:42 +08:00
|
|
|
static int qeth_l3_process_inbound_buffer(struct qeth_card *card,
|
|
|
|
int budget, int *done)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
2010-09-08 05:14:42 +08:00
|
|
|
int work_done = 0;
|
2008-02-15 16:19:42 +08:00
|
|
|
struct sk_buff *skb;
|
|
|
|
struct qeth_hdr *hdr;
|
|
|
|
unsigned int len;
|
2011-08-08 09:33:59 +08:00
|
|
|
__u16 magic;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-09-08 05:14:42 +08:00
|
|
|
*done = 0;
|
2012-11-19 10:46:50 +08:00
|
|
|
WARN_ON_ONCE(!budget);
|
2010-09-08 05:14:42 +08:00
|
|
|
while (budget) {
|
|
|
|
skb = qeth_core_get_next_skb(card,
|
2011-08-08 09:33:59 +08:00
|
|
|
&card->qdio.in_q->bufs[card->rx.b_index],
|
2010-09-08 05:14:42 +08:00
|
|
|
&card->rx.b_element, &card->rx.e_offset, &hdr);
|
|
|
|
if (!skb) {
|
|
|
|
*done = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
skb->dev = card->dev;
|
2008-02-15 16:19:42 +08:00
|
|
|
switch (hdr->hdr.l3.id) {
|
|
|
|
case QETH_HEADER_TYPE_LAYER3:
|
2011-08-08 09:33:59 +08:00
|
|
|
magic = *(__u16 *)skb->data;
|
|
|
|
if ((card->info.type == QETH_CARD_TYPE_IQD) &&
|
|
|
|
(magic == ETH_P_AF_IUCV)) {
|
2017-04-07 15:15:36 +08:00
|
|
|
skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
|
2011-08-08 09:33:59 +08:00
|
|
|
skb->pkt_type = PACKET_HOST;
|
|
|
|
skb->mac_header = NET_SKB_PAD;
|
|
|
|
skb->dev = card->dev;
|
|
|
|
len = skb->len;
|
|
|
|
card->dev->header_ops->create(skb, card->dev, 0,
|
|
|
|
card->dev->dev_addr, "FAKELL",
|
|
|
|
card->dev->addr_len);
|
|
|
|
netif_receive_skb(skb);
|
|
|
|
} else {
|
2017-09-19 03:18:21 +08:00
|
|
|
qeth_l3_rebuild_skb(card, skb, hdr);
|
2011-08-08 09:33:59 +08:00
|
|
|
len = skb->len;
|
|
|
|
napi_gro_receive(&card->napi, skb);
|
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
break;
|
2010-01-11 10:50:50 +08:00
|
|
|
case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */
|
|
|
|
skb->pkt_type = PACKET_HOST;
|
|
|
|
skb->protocol = eth_type_trans(skb, skb->dev);
|
|
|
|
len = skb->len;
|
|
|
|
netif_receive_skb(skb);
|
|
|
|
break;
|
2008-02-15 16:19:42 +08:00
|
|
|
default:
|
|
|
|
dev_kfree_skb_any(skb);
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "inbunkno");
|
2008-04-01 16:26:58 +08:00
|
|
|
QETH_DBF_HEX(CTRL, 3, hdr, QETH_DBF_CTRL_LEN);
|
2008-02-15 16:19:42 +08:00
|
|
|
continue;
|
|
|
|
}
|
2010-09-08 05:14:42 +08:00
|
|
|
work_done++;
|
|
|
|
budget--;
|
2008-02-15 16:19:42 +08:00
|
|
|
card->stats.rx_packets++;
|
|
|
|
card->stats.rx_bytes += len;
|
|
|
|
}
|
2010-09-08 05:14:42 +08:00
|
|
|
return work_done;
|
|
|
|
}
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
static int qeth_l3_verify_vlan_dev(struct net_device *dev,
|
|
|
|
struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
2011-07-20 12:54:41 +08:00
|
|
|
u16 vid;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2011-07-20 12:54:41 +08:00
|
|
|
for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
|
|
|
|
struct net_device *netdev;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2011-07-20 12:54:41 +08:00
|
|
|
rcu_read_lock();
|
2014-05-09 14:58:05 +08:00
|
|
|
netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
|
2013-04-21 08:09:34 +08:00
|
|
|
vid);
|
2011-07-20 12:54:41 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
if (netdev == dev) {
|
2008-02-15 16:19:42 +08:00
|
|
|
rc = QETH_VLAN_CARD;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-26 17:24:10 +08:00
|
|
|
if (rc && !(vlan_dev_real_dev(dev)->ml_priv == (void *)card))
|
2008-02-15 16:19:42 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_verify_dev(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct qeth_card *card;
|
|
|
|
int rc = 0;
|
2016-06-16 22:18:58 +08:00
|
|
|
unsigned long flags;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
read_lock_irqsave(&qeth_core_card_list.rwlock, flags);
|
|
|
|
list_for_each_entry(card, &qeth_core_card_list.list, list) {
|
|
|
|
if (card->dev == dev) {
|
|
|
|
rc = QETH_REAL_CARD;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rc = qeth_l3_verify_vlan_dev(dev, card);
|
|
|
|
if (rc)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
read_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = NULL;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = qeth_l3_verify_dev(dev);
|
|
|
|
if (rc == QETH_REAL_CARD)
|
2008-07-26 17:24:10 +08:00
|
|
|
card = dev->ml_priv;
|
2008-02-15 16:19:42 +08:00
|
|
|
else if (rc == QETH_VLAN_CARD)
|
2008-07-26 17:24:10 +08:00
|
|
|
card = vlan_dev_real_dev(dev)->ml_priv;
|
2008-06-06 18:37:44 +08:00
|
|
|
if (card && card->options.layer2)
|
2008-02-15 16:19:42 +08:00
|
|
|
card = NULL;
|
2010-06-22 06:57:05 +08:00
|
|
|
if (card)
|
|
|
|
QETH_CARD_TEXT_(card, 4, "%d", rc);
|
2008-02-15 16:19:42 +08:00
|
|
|
return card ;
|
|
|
|
}
|
|
|
|
|
2015-08-04 23:11:47 +08:00
|
|
|
static void qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
2008-04-01 16:26:58 +08:00
|
|
|
QETH_DBF_TEXT(SETUP, 2, "stopcard");
|
|
|
|
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
qeth_set_allowed_threads(card, 0, 1);
|
2010-01-11 10:50:50 +08:00
|
|
|
if (card->options.sniffer &&
|
|
|
|
(card->info.promisc_mode == SET_PROMISC_MODE_ON))
|
|
|
|
qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (card->read.state == CH_STATE_UP &&
|
|
|
|
card->write.state == CH_STATE_UP &&
|
|
|
|
(card->state == CARD_STATE_UP)) {
|
|
|
|
if (recovery_mode)
|
|
|
|
qeth_l3_stop(card->dev);
|
2008-04-24 16:15:25 +08:00
|
|
|
else {
|
2010-03-09 04:36:56 +08:00
|
|
|
rtnl_lock();
|
|
|
|
dev_close(card->dev);
|
|
|
|
rtnl_unlock();
|
2008-04-24 16:15:25 +08:00
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
card->state = CARD_STATE_SOFTSETUP;
|
|
|
|
}
|
|
|
|
if (card->state == CARD_STATE_SOFTSETUP) {
|
2016-06-16 22:18:58 +08:00
|
|
|
qeth_l3_clear_ip_htable(card, 1);
|
2008-02-15 16:19:42 +08:00
|
|
|
qeth_clear_ipacmd_list(card);
|
|
|
|
card->state = CARD_STATE_HARDSETUP;
|
|
|
|
}
|
|
|
|
if (card->state == CARD_STATE_HARDSETUP) {
|
|
|
|
qeth_qdio_clear_card(card, 0);
|
|
|
|
qeth_clear_qdio_buffers(card);
|
|
|
|
qeth_clear_working_pool_list(card);
|
|
|
|
card->state = CARD_STATE_DOWN;
|
|
|
|
}
|
|
|
|
if (card->state == CARD_STATE_DOWN) {
|
|
|
|
qeth_clear_cmd_buffers(&card->read);
|
|
|
|
qeth_clear_cmd_buffers(&card->write);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-11 10:50:50 +08:00
|
|
|
/*
|
|
|
|
* test for and Switch promiscuous mode (on or off)
|
|
|
|
* either for guestlan or HiperSocket Sniffer
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
qeth_l3_handle_promisc_mode(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
struct net_device *dev = card->dev;
|
|
|
|
|
|
|
|
if (((dev->flags & IFF_PROMISC) &&
|
|
|
|
(card->info.promisc_mode == SET_PROMISC_MODE_ON)) ||
|
|
|
|
(!(dev->flags & IFF_PROMISC) &&
|
|
|
|
(card->info.promisc_mode == SET_PROMISC_MODE_OFF)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (card->info.guestlan) { /* Guestlan trace */
|
|
|
|
if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
|
|
|
|
qeth_setadp_promisc_mode(card);
|
|
|
|
} else if (card->options.sniffer && /* HiperSockets trace */
|
|
|
|
qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) {
|
|
|
|
if (dev->flags & IFF_PROMISC) {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "+promisc");
|
2010-01-11 10:50:50 +08:00
|
|
|
qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_ENABLE);
|
|
|
|
} else {
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "-promisc");
|
2010-01-11 10:50:50 +08:00
|
|
|
qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
static void qeth_l3_set_multicast_list(struct net_device *dev)
|
|
|
|
{
|
2008-07-26 17:24:10 +08:00
|
|
|
struct qeth_card *card = dev->ml_priv;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "setmulti");
|
2009-03-25 04:57:18 +08:00
|
|
|
if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
|
|
|
|
(card->state != CARD_STATE_UP))
|
|
|
|
return;
|
2010-01-11 10:50:50 +08:00
|
|
|
if (!card->options.sniffer) {
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
spin_lock_bh(&card->mclock);
|
|
|
|
|
|
|
|
qeth_l3_mark_all_mc_to_be_deleted(card);
|
|
|
|
|
2010-01-11 10:50:50 +08:00
|
|
|
qeth_l3_add_multicast_ipv4(card);
|
|
|
|
qeth_l3_add_multicast_ipv6(card);
|
2016-06-16 22:18:58 +08:00
|
|
|
qeth_l3_delete_nonused_mc(card);
|
|
|
|
qeth_l3_add_all_new_mc(card);
|
|
|
|
|
|
|
|
spin_unlock_bh(&card->mclock);
|
|
|
|
|
2010-01-11 10:50:50 +08:00
|
|
|
if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
qeth_l3_handle_promisc_mode(card);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char *qeth_l3_arp_get_error_cause(int *rc)
|
|
|
|
{
|
|
|
|
switch (*rc) {
|
|
|
|
case QETH_IPA_ARP_RC_FAILED:
|
|
|
|
*rc = -EIO;
|
|
|
|
return "operation failed";
|
|
|
|
case QETH_IPA_ARP_RC_NOTSUPP:
|
|
|
|
*rc = -EOPNOTSUPP;
|
|
|
|
return "operation not supported";
|
|
|
|
case QETH_IPA_ARP_RC_OUT_OF_RANGE:
|
|
|
|
*rc = -EINVAL;
|
|
|
|
return "argument out of range";
|
|
|
|
case QETH_IPA_ARP_RC_Q_NOTSUPP:
|
|
|
|
*rc = -EOPNOTSUPP;
|
|
|
|
return "query operation not supported";
|
|
|
|
case QETH_IPA_ARP_RC_Q_NO_DATA:
|
|
|
|
*rc = -ENOENT;
|
|
|
|
return "no query data available";
|
|
|
|
default:
|
|
|
|
return "unknown error";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
|
|
|
|
{
|
|
|
|
int tmp;
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "arpstnoe");
|
2008-02-15 16:19:42 +08: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
|
|
|
|
*/
|
|
|
|
if (card->info.guestlan)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
|
2008-02-15 16:19:42 +08:00
|
|
|
IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
|
|
|
|
no_entries);
|
|
|
|
if (rc) {
|
|
|
|
tmp = rc;
|
2008-06-06 18:37:46 +08:00
|
|
|
QETH_DBF_MESSAGE(2, "Could not set number of ARP entries on "
|
|
|
|
"%s: %s (0x%x/%d)\n", QETH_CARD_IFNAME(card),
|
2008-02-15 16:19:42 +08:00
|
|
|
qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2010-12-08 10:57:58 +08: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 16:19:42 +08:00
|
|
|
{
|
2010-12-08 10:57:58 +08:00
|
|
|
__u32 rc;
|
|
|
|
__u8 is_hsi;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-12-08 10:57:58 +08: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 16:19:42 +08:00
|
|
|
}
|
2010-12-08 10:57:58 +08: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 16:19:42 +08: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;
|
|
|
|
int i;
|
2010-12-08 10:57:58 +08:00
|
|
|
int e;
|
|
|
|
int entrybytes_done;
|
|
|
|
int stripped_bytes;
|
|
|
|
__u8 do_strip_entries;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-12-08 10:57:58 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "arpquecb");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
qinfo = (struct qeth_arp_query_info *) reply->param;
|
|
|
|
cmd = (struct qeth_ipa_cmd *) data;
|
2010-12-08 10:57:58 +08:00
|
|
|
QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.prot_version);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (cmd->hdr.return_code) {
|
2010-12-08 10:57:58 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "arpcberr");
|
|
|
|
QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
|
2008-02-15 16:19:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (cmd->data.setassparms.hdr.return_code) {
|
|
|
|
cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
|
2010-12-08 10:57:58 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "setaperr");
|
|
|
|
QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
|
2008-02-15 16:19:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
qdata = &cmd->data.setassparms.data.query_arp;
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries);
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-12-08 10:57:58 +08: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) {
|
|
|
|
QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM);
|
2012-03-07 10:06:28 +08:00
|
|
|
cmd->hdr.return_code = IPA_RC_ENOMEM;
|
2010-12-08 10:57:58 +08:00
|
|
|
goto out_error;
|
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-12-08 10:57:58 +08: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 16:19:42 +08: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 10:57:58 +08:00
|
|
|
QETH_CARD_TEXT_(card, 4, "nove%i", qinfo->no_entries);
|
2008-02-15 16:19:42 +08: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 10:57:58 +08:00
|
|
|
QETH_CARD_TEXT_(card, 4, "rc%i", 0);
|
2008-02-15 16:19:42 +08:00
|
|
|
return 0;
|
|
|
|
out_error:
|
|
|
|
i = 0;
|
|
|
|
memcpy(qinfo->udata, &i, 4);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_send_ipa_arp_cmd(struct qeth_card *card,
|
|
|
|
struct qeth_cmd_buffer *iob, int len,
|
|
|
|
int (*reply_cb)(struct qeth_card *, struct qeth_reply *,
|
|
|
|
unsigned long),
|
|
|
|
void *reply_param)
|
|
|
|
{
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "sendarp");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
|
|
|
|
memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
|
|
|
|
&card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
|
|
|
|
return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob,
|
|
|
|
reply_cb, reply_param);
|
|
|
|
}
|
|
|
|
|
2010-12-08 10:57:58 +08: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 16:19:42 +08:00
|
|
|
{
|
|
|
|
struct qeth_cmd_buffer *iob;
|
2010-12-08 10:57:58 +08:00
|
|
|
struct qeth_ipa_cmd *cmd;
|
2008-02-15 16:19:42 +08:00
|
|
|
int tmp;
|
|
|
|
int rc;
|
|
|
|
|
2010-12-08 10:57:58 +08:00
|
|
|
QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot);
|
|
|
|
|
2015-12-11 19:27:54 +08:00
|
|
|
iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
|
|
|
|
IPA_CMD_ASS_ARP_QUERY_INFO,
|
|
|
|
sizeof(struct qeth_arp_query_data)
|
|
|
|
- sizeof(char),
|
|
|
|
prot);
|
2015-01-21 20:39:10 +08:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2010-12-08 10:57:58 +08:00
|
|
|
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
|
|
|
cmd->data.setassparms.data.query_arp.request_bits = 0x000F;
|
|
|
|
cmd->data.setassparms.data.query_arp.reply_bits = 0;
|
|
|
|
cmd->data.setassparms.data.query_arp.no_entries = 0;
|
|
|
|
rc = qeth_l3_send_ipa_arp_cmd(card, iob,
|
|
|
|
QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN,
|
|
|
|
qeth_l3_arp_query_cb, (void *)qinfo);
|
|
|
|
if (rc) {
|
|
|
|
tmp = rc;
|
|
|
|
QETH_DBF_MESSAGE(2,
|
|
|
|
"Error while querying ARP cache on %s: %s "
|
|
|
|
"(0x%x/%d)\n", QETH_CARD_IFNAME(card),
|
|
|
|
qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "arpquery");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/
|
|
|
|
IPA_ARP_PROCESSING)) {
|
2010-12-08 10:57:58 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "arpqnsup");
|
|
|
|
rc = -EOPNOTSUPP;
|
|
|
|
goto out;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
/* get size of userspace buffer and mask_bits -> 6 bytes */
|
2010-12-08 10:57:58 +08:00
|
|
|
if (copy_from_user(&qinfo, udata, 6)) {
|
|
|
|
rc = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL);
|
2010-12-08 10:57:58 +08:00
|
|
|
if (!qinfo.udata) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET;
|
2010-12-08 10:57:58 +08:00
|
|
|
rc = qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV4, &qinfo);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (rc) {
|
|
|
|
if (copy_to_user(udata, qinfo.udata, 4))
|
|
|
|
rc = -EFAULT;
|
2016-06-16 22:19:03 +08: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 16:19:42 +08:00
|
|
|
}
|
2016-06-16 22:19:03 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "qacts");
|
|
|
|
|
2010-12-08 10:57:58 +08:00
|
|
|
free_and_out:
|
2008-02-15 16:19:42 +08:00
|
|
|
kfree(qinfo.udata);
|
2010-12-08 10:57:58 +08:00
|
|
|
out:
|
2008-02-15 16:19:42 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_arp_add_entry(struct qeth_card *card,
|
|
|
|
struct qeth_arp_cache_entry *entry)
|
|
|
|
{
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
char buf[16];
|
|
|
|
int tmp;
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "arpadent");
|
2008-02-15 16:19:42 +08: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
|
|
|
|
*/
|
|
|
|
if (card->info.guestlan)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2015-12-11 19:27:54 +08:00
|
|
|
iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
|
2008-02-15 16:19:42 +08:00
|
|
|
IPA_CMD_ASS_ARP_ADD_ENTRY,
|
|
|
|
sizeof(struct qeth_arp_cache_entry),
|
|
|
|
QETH_PROT_IPV4);
|
2015-01-21 20:39:10 +08:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_setassparms(card, iob,
|
2008-02-15 16:19:42 +08:00
|
|
|
sizeof(struct qeth_arp_cache_entry),
|
|
|
|
(unsigned long) entry,
|
2016-06-16 22:18:59 +08:00
|
|
|
qeth_setassparms_cb, NULL);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (rc) {
|
|
|
|
tmp = rc;
|
|
|
|
qeth_l3_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
|
2008-06-06 18:37:46 +08:00
|
|
|
QETH_DBF_MESSAGE(2, "Could not add ARP entry for address %s "
|
|
|
|
"on %s: %s (0x%x/%d)\n", buf, QETH_CARD_IFNAME(card),
|
|
|
|
qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_arp_remove_entry(struct qeth_card *card,
|
|
|
|
struct qeth_arp_cache_entry *entry)
|
|
|
|
{
|
|
|
|
struct qeth_cmd_buffer *iob;
|
|
|
|
char buf[16] = {0, };
|
|
|
|
int tmp;
|
|
|
|
int rc;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "arprment");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* currently GuestLAN only supports the ARP assist function
|
|
|
|
* IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_REMOVE_ENTRY;
|
|
|
|
* thus we say EOPNOTSUPP for this ARP function
|
|
|
|
*/
|
|
|
|
if (card->info.guestlan)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
memcpy(buf, entry, 12);
|
2015-12-11 19:27:54 +08:00
|
|
|
iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
|
2008-02-15 16:19:42 +08:00
|
|
|
IPA_CMD_ASS_ARP_REMOVE_ENTRY,
|
|
|
|
12,
|
|
|
|
QETH_PROT_IPV4);
|
2015-01-21 20:39:10 +08:00
|
|
|
if (!iob)
|
|
|
|
return -ENOMEM;
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_setassparms(card, iob,
|
2008-02-15 16:19:42 +08:00
|
|
|
12, (unsigned long)buf,
|
2016-06-16 22:18:59 +08:00
|
|
|
qeth_setassparms_cb, NULL);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (rc) {
|
|
|
|
tmp = rc;
|
|
|
|
memset(buf, 0, 16);
|
|
|
|
qeth_l3_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
|
2008-06-06 18:37:46 +08:00
|
|
|
QETH_DBF_MESSAGE(2, "Could not delete ARP entry for address %s"
|
|
|
|
" on %s: %s (0x%x/%d)\n", buf, QETH_CARD_IFNAME(card),
|
|
|
|
qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_arp_flush_cache(struct qeth_card *card)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
int tmp;
|
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "arpflush");
|
2008-02-15 16:19:42 +08: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
|
|
|
|
*/
|
|
|
|
if (card->info.guestlan || (card->info.type == QETH_CARD_TYPE_IQD))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2015-09-18 22:06:51 +08:00
|
|
|
rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
|
2008-02-15 16:19:42 +08:00
|
|
|
IPA_CMD_ASS_ARP_FLUSH_CACHE, 0);
|
|
|
|
if (rc) {
|
|
|
|
tmp = rc;
|
2008-06-06 18:37:46 +08:00
|
|
|
QETH_DBF_MESSAGE(2, "Could not flush ARP cache on %s: %s "
|
|
|
|
"(0x%x/%d)\n", QETH_CARD_IFNAME(card),
|
2008-02-15 16:19:42 +08:00
|
|
|
qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
|
|
|
{
|
2008-07-26 17:24:10 +08:00
|
|
|
struct qeth_card *card = dev->ml_priv;
|
2008-02-15 16:19:42 +08:00
|
|
|
struct qeth_arp_cache_entry arp_entry;
|
|
|
|
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:
|
|
|
|
if (!capable(CAP_NET_ADMIN)) {
|
|
|
|
rc = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (copy_from_user(&arp_entry, rq->ifr_ifru.ifru_data,
|
|
|
|
sizeof(struct qeth_arp_cache_entry)))
|
|
|
|
rc = -EFAULT;
|
|
|
|
else
|
|
|
|
rc = qeth_l3_arp_add_entry(card, &arp_entry);
|
|
|
|
break;
|
|
|
|
case SIOC_QETH_ARP_REMOVE_ENTRY:
|
|
|
|
if (!capable(CAP_NET_ADMIN)) {
|
|
|
|
rc = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (copy_from_user(&arp_entry, rq->ifr_ifru.ifru_data,
|
|
|
|
sizeof(struct qeth_arp_cache_entry)))
|
|
|
|
rc = -EFAULT;
|
|
|
|
else
|
|
|
|
rc = qeth_l3_arp_remove_entry(card, &arp_entry);
|
|
|
|
break;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-08-15 23:02:46 +08:00
|
|
|
static int qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
|
2009-08-26 10:01:08 +08:00
|
|
|
{
|
|
|
|
int cast_type = RTN_UNSPEC;
|
2011-07-18 14:09:49 +08:00
|
|
|
struct neighbour *n = NULL;
|
|
|
|
struct dst_entry *dst;
|
|
|
|
|
2011-11-15 10:31:15 +08:00
|
|
|
rcu_read_lock();
|
2011-07-18 14:09:49 +08:00
|
|
|
dst = skb_dst(skb);
|
|
|
|
if (dst)
|
2012-07-03 13:02:33 +08:00
|
|
|
n = dst_neigh_lookup_skb(dst, skb);
|
2011-07-18 14:09:49 +08:00
|
|
|
if (n) {
|
|
|
|
cast_type = n->type;
|
2011-11-15 10:31:15 +08:00
|
|
|
rcu_read_unlock();
|
2012-07-03 13:02:33 +08:00
|
|
|
neigh_release(n);
|
2009-08-26 10:01:08 +08:00
|
|
|
if ((cast_type == RTN_BROADCAST) ||
|
|
|
|
(cast_type == RTN_MULTICAST) ||
|
|
|
|
(cast_type == RTN_ANYCAST))
|
|
|
|
return cast_type;
|
|
|
|
else
|
|
|
|
return RTN_UNSPEC;
|
|
|
|
}
|
2011-11-15 10:31:15 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2009-08-26 10:01:08 +08:00
|
|
|
/* try something else */
|
2017-04-07 15:15:36 +08:00
|
|
|
if (be16_to_cpu(skb->protocol) == ETH_P_IPV6)
|
2009-08-26 10:01:08 +08:00
|
|
|
return (skb_network_header(skb)[24] == 0xff) ?
|
|
|
|
RTN_MULTICAST : 0;
|
2017-04-07 15:15:36 +08:00
|
|
|
else if (be16_to_cpu(skb->protocol) == ETH_P_IP)
|
2009-08-26 10:01:08 +08:00
|
|
|
return ((skb_network_header(skb)[16] & 0xf0) == 0xe0) ?
|
|
|
|
RTN_MULTICAST : 0;
|
|
|
|
/* ... */
|
|
|
|
if (!memcmp(skb->data, skb->dev->broadcast, 6))
|
|
|
|
return RTN_BROADCAST;
|
|
|
|
else {
|
|
|
|
u16 hdr_mac;
|
|
|
|
|
|
|
|
hdr_mac = *((u16 *)skb->data);
|
|
|
|
/* tr multicast? */
|
|
|
|
switch (card->info.link_type) {
|
|
|
|
case QETH_LINK_TYPE_HSTR:
|
|
|
|
case QETH_LINK_TYPE_LANE_TR:
|
|
|
|
if ((hdr_mac == QETH_TR_MAC_NC) ||
|
|
|
|
(hdr_mac == QETH_TR_MAC_C))
|
|
|
|
return RTN_MULTICAST;
|
|
|
|
break;
|
|
|
|
/* eth or so multicast? */
|
|
|
|
default:
|
|
|
|
if ((hdr_mac == QETH_ETH_MAC_V4) ||
|
|
|
|
(hdr_mac == QETH_ETH_MAC_V6))
|
|
|
|
return RTN_MULTICAST;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cast_type;
|
|
|
|
}
|
|
|
|
|
2011-08-08 09:33:59 +08:00
|
|
|
static void qeth_l3_fill_af_iucv_hdr(struct qeth_card *card,
|
|
|
|
struct qeth_hdr *hdr, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
char daddr[16];
|
|
|
|
struct af_iucv_trans_hdr *iucv_hdr;
|
|
|
|
|
|
|
|
memset(hdr, 0, sizeof(struct qeth_hdr));
|
|
|
|
hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
|
|
|
|
hdr->hdr.l3.ext_flags = 0;
|
s390/qeth: no ETH header for outbound AF_IUCV
With AF_IUCV traffic, the skb passed to hard_start_xmit() has a 14 byte
slot at skb->data, intended for an ETH header. qeth_l3_fill_af_iucv_hdr()
fills this ETH header... and then immediately moves it to the
skb's headroom, where it disappears and is never seen again.
But it's still possible for us to return NETDEV_TX_BUSY after the skb has
been modified. Since we didn't get a private copy of the skb, the next
time the skb is delivered to hard_start_xmit() it no longer has the
expected layout (we moved the ETH header to the headroom, so skb->data
now starts at the IUCV_TRANS header). So when qeth_l3_fill_af_iucv_hdr()
does another round of rebuilding, the resulting qeth header ends up
all wrong. On transmission, the buffer is then rejected by
the HiperSockets device with SBALF15 = x'04'.
When this error is passed back to af_iucv as TX_NOTIFY_UNREACHABLE, it
tears down the offending socket.
As the ETH header for AF_IUCV serves no purpose, just align the code to
what we do for IP traffic on L3 HiperSockets: keep the ETH header at
skb->data, and pass down data_offset = ETH_HLEN to qeth_fill_buffer().
When mapping the payload into the SBAL elements, the ETH header is then
stripped off. This avoids the skb manipulations in
qeth_l3_fill_af_iucv_hdr(), and any buffer re-entering hard_start_xmit()
after NETDEV_TX_BUSY is now processed properly.
Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: Ursula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-03-23 21:55:09 +08:00
|
|
|
hdr->hdr.l3.length = skb->len - ETH_HLEN;
|
2011-08-08 09:33:59 +08:00
|
|
|
hdr->hdr.l3.flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST;
|
s390/qeth: no ETH header for outbound AF_IUCV
With AF_IUCV traffic, the skb passed to hard_start_xmit() has a 14 byte
slot at skb->data, intended for an ETH header. qeth_l3_fill_af_iucv_hdr()
fills this ETH header... and then immediately moves it to the
skb's headroom, where it disappears and is never seen again.
But it's still possible for us to return NETDEV_TX_BUSY after the skb has
been modified. Since we didn't get a private copy of the skb, the next
time the skb is delivered to hard_start_xmit() it no longer has the
expected layout (we moved the ETH header to the headroom, so skb->data
now starts at the IUCV_TRANS header). So when qeth_l3_fill_af_iucv_hdr()
does another round of rebuilding, the resulting qeth header ends up
all wrong. On transmission, the buffer is then rejected by
the HiperSockets device with SBALF15 = x'04'.
When this error is passed back to af_iucv as TX_NOTIFY_UNREACHABLE, it
tears down the offending socket.
As the ETH header for AF_IUCV serves no purpose, just align the code to
what we do for IP traffic on L3 HiperSockets: keep the ETH header at
skb->data, and pass down data_offset = ETH_HLEN to qeth_fill_buffer().
When mapping the payload into the SBAL elements, the ETH header is then
stripped off. This avoids the skb manipulations in
qeth_l3_fill_af_iucv_hdr(), and any buffer re-entering hard_start_xmit()
after NETDEV_TX_BUSY is now processed properly.
Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: Ursula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-03-23 21:55:09 +08:00
|
|
|
|
|
|
|
iucv_hdr = (struct af_iucv_trans_hdr *) (skb->data + ETH_HLEN);
|
2011-08-08 09:33:59 +08:00
|
|
|
memset(daddr, 0, sizeof(daddr));
|
|
|
|
daddr[0] = 0xfe;
|
|
|
|
daddr[1] = 0x80;
|
|
|
|
memcpy(&daddr[8], iucv_hdr->destUserID, 8);
|
|
|
|
memcpy(hdr->hdr.l3.dest_addr, daddr, 16);
|
|
|
|
}
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
|
|
|
|
struct sk_buff *skb, int ipv, int cast_type)
|
|
|
|
{
|
2011-07-18 14:09:49 +08:00
|
|
|
struct dst_entry *dst;
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
memset(hdr, 0, sizeof(struct qeth_hdr));
|
|
|
|
hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
|
|
|
|
hdr->hdr.l3.ext_flags = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* before we're going to overwrite this location with next hop ip.
|
|
|
|
* v6 uses passthrough, v4 sets the tag in the QDIO header.
|
|
|
|
*/
|
2015-01-14 00:13:44 +08:00
|
|
|
if (skb_vlan_tag_present(skb)) {
|
2008-04-24 16:15:22 +08:00
|
|
|
if ((ipv == 4) || (card->info.type == QETH_CARD_TYPE_IQD))
|
|
|
|
hdr->hdr.l3.ext_flags = QETH_HDR_EXT_VLAN_FRAME;
|
|
|
|
else
|
|
|
|
hdr->hdr.l3.ext_flags = QETH_HDR_EXT_INCLUDE_VLAN_TAG;
|
2015-01-14 00:13:44 +08:00
|
|
|
hdr->hdr.l3.vlan_id = skb_vlan_tag_get(skb);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
hdr->hdr.l3.length = skb->len - sizeof(struct qeth_hdr);
|
2011-11-15 10:31:15 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
2011-07-18 14:09:49 +08:00
|
|
|
dst = skb_dst(skb);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (ipv == 4) {
|
2012-02-01 18:49:17 +08:00
|
|
|
struct rtable *rt = (struct rtable *) dst;
|
|
|
|
__be32 *pkey = &ip_hdr(skb)->daddr;
|
|
|
|
|
2017-08-07 19:28:39 +08:00
|
|
|
if (rt && rt->rt_gateway)
|
2012-02-01 18:49:17 +08:00
|
|
|
pkey = &rt->rt_gateway;
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
/* IPv4 */
|
|
|
|
hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags4(cast_type);
|
|
|
|
memset(hdr->hdr.l3.dest_addr, 0, 12);
|
2012-02-01 18:49:17 +08:00
|
|
|
*((__be32 *) (&hdr->hdr.l3.dest_addr[12])) = *pkey;
|
2008-02-15 16:19:42 +08:00
|
|
|
} else if (ipv == 6) {
|
2012-02-01 18:49:17 +08:00
|
|
|
struct rt6_info *rt = (struct rt6_info *) dst;
|
|
|
|
struct in6_addr *pkey = &ipv6_hdr(skb)->daddr;
|
|
|
|
|
2017-08-07 19:28:39 +08:00
|
|
|
if (rt && !ipv6_addr_any(&rt->rt6i_gateway))
|
2012-02-01 18:49:17 +08:00
|
|
|
pkey = &rt->rt6i_gateway;
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
/* IPv6 */
|
|
|
|
hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags6(cast_type);
|
|
|
|
if (card->info.type == QETH_CARD_TYPE_IQD)
|
|
|
|
hdr->hdr.l3.flags &= ~QETH_HDR_PASSTHRU;
|
2012-02-01 18:49:17 +08:00
|
|
|
memcpy(hdr->hdr.l3.dest_addr, pkey, 16);
|
2008-02-15 16:19:42 +08:00
|
|
|
} else {
|
2012-05-11 03:50:52 +08:00
|
|
|
if (!memcmp(skb->data + sizeof(struct qeth_hdr),
|
2008-02-15 16:19:42 +08:00
|
|
|
skb->dev->broadcast, 6)) {
|
|
|
|
/* broadcast? */
|
|
|
|
hdr->hdr.l3.flags = QETH_CAST_BROADCAST |
|
|
|
|
QETH_HDR_PASSTHRU;
|
|
|
|
} else {
|
|
|
|
hdr->hdr.l3.flags = (cast_type == RTN_MULTICAST) ?
|
|
|
|
QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU :
|
|
|
|
QETH_CAST_UNICAST | QETH_HDR_PASSTHRU;
|
|
|
|
}
|
|
|
|
}
|
2011-11-15 10:31:15 +08:00
|
|
|
rcu_read_unlock();
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2017-08-15 23:02:46 +08:00
|
|
|
static void qeth_l3_hdr_csum(struct qeth_card *card, struct qeth_hdr *hdr,
|
|
|
|
struct sk_buff *skb)
|
2010-05-12 03:34:45 +08:00
|
|
|
{
|
|
|
|
struct iphdr *iph = ip_hdr(skb);
|
|
|
|
|
|
|
|
/* tcph->check contains already the pseudo hdr checksum
|
|
|
|
* so just set the header flags
|
|
|
|
*/
|
|
|
|
if (iph->protocol == IPPROTO_UDP)
|
|
|
|
hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_UDP;
|
2011-01-13 04:42:25 +08:00
|
|
|
hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ |
|
|
|
|
QETH_HDR_EXT_CSUM_HDR_REQ;
|
|
|
|
iph->check = 0;
|
2010-05-12 03:34:45 +08:00
|
|
|
if (card->options.performance_stats)
|
|
|
|
card->perf_stats.tx_csum++;
|
|
|
|
}
|
|
|
|
|
2009-03-25 04:57:16 +08:00
|
|
|
static void qeth_tso_fill_header(struct qeth_card *card,
|
|
|
|
struct qeth_hdr *qhdr, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct qeth_hdr_tso *hdr = (struct qeth_hdr_tso *)qhdr;
|
|
|
|
struct tcphdr *tcph = tcp_hdr(skb);
|
|
|
|
struct iphdr *iph = ip_hdr(skb);
|
|
|
|
struct ipv6hdr *ip6h = ipv6_hdr(skb);
|
|
|
|
|
|
|
|
/*fix header to TSO values ...*/
|
|
|
|
hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO;
|
2010-11-26 10:41:20 +08:00
|
|
|
hdr->hdr.hdr.l3.length = skb->len - sizeof(struct qeth_hdr_tso);
|
2009-03-25 04:57:16 +08:00
|
|
|
/*set values which are fix for the first approach ...*/
|
|
|
|
hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso);
|
|
|
|
hdr->ext.imb_hdr_no = 1;
|
|
|
|
hdr->ext.hdr_type = 1;
|
|
|
|
hdr->ext.hdr_version = 1;
|
|
|
|
hdr->ext.hdr_len = 28;
|
|
|
|
/*insert non-fix values */
|
|
|
|
hdr->ext.mss = skb_shinfo(skb)->gso_size;
|
2017-08-15 23:02:45 +08:00
|
|
|
hdr->ext.dg_hdr_len = (__u16)(ip_hdrlen(skb) + tcp_hdrlen(skb));
|
2009-03-25 04:57:16 +08:00
|
|
|
hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len -
|
|
|
|
sizeof(struct qeth_hdr_tso));
|
|
|
|
tcph->check = 0;
|
2017-04-07 15:15:36 +08:00
|
|
|
if (be16_to_cpu(skb->protocol) == ETH_P_IPV6) {
|
2009-03-25 04:57:16 +08:00
|
|
|
ip6h->payload_len = 0;
|
|
|
|
tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
|
|
|
|
0, IPPROTO_TCP, 0);
|
|
|
|
} else {
|
|
|
|
/*OSA want us to set these values ...*/
|
|
|
|
tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
|
|
|
|
0, IPPROTO_TCP, 0);
|
|
|
|
iph->tot_len = 0;
|
|
|
|
iph->check = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:52 +08:00
|
|
|
/**
|
2016-06-16 22:18:53 +08:00
|
|
|
* qeth_l3_get_elements_no_tso() - find number of SBALEs for skb data for tso
|
|
|
|
* @card: qeth card structure, to check max. elems.
|
|
|
|
* @skb: SKB address
|
|
|
|
* @extra_elems: extra elems needed, to check against max.
|
2016-06-16 22:18:52 +08:00
|
|
|
*
|
|
|
|
* Returns the number of pages, and thus QDIO buffer elements, needed to cover
|
|
|
|
* skb data, including linear part and fragments, but excluding TCP header.
|
|
|
|
* (Exclusion of TCP header distinguishes it from qeth_get_elements_no().)
|
|
|
|
* Checks if the result plus extra_elems fits under the limit for the card.
|
|
|
|
* Returns 0 if it does not.
|
|
|
|
* Note: extra_elems is not included in the returned result.
|
|
|
|
*/
|
2016-06-16 22:18:53 +08:00
|
|
|
static int qeth_l3_get_elements_no_tso(struct qeth_card *card,
|
2016-06-16 22:18:52 +08:00
|
|
|
struct sk_buff *skb, int extra_elems)
|
2009-11-12 08:11:44 +08:00
|
|
|
{
|
2016-06-16 22:18:52 +08:00
|
|
|
addr_t tcpdptr = (addr_t)tcp_hdr(skb) + tcp_hdrlen(skb);
|
|
|
|
int elements = qeth_get_elements_for_range(
|
|
|
|
tcpdptr,
|
|
|
|
(addr_t)skb->data + skb_headlen(skb)) +
|
2016-06-16 22:18:59 +08:00
|
|
|
qeth_get_elements_for_frags(skb);
|
2013-03-19 04:04:44 +08:00
|
|
|
|
2016-06-16 22:18:52 +08:00
|
|
|
if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
|
|
|
|
QETH_DBF_MESSAGE(2,
|
|
|
|
"Invalid size of TSO IP packet (Number=%d / Length=%d). Discarded.\n",
|
|
|
|
elements + extra_elems, skb->len);
|
|
|
|
return 0;
|
|
|
|
}
|
2009-11-12 08:11:44 +08:00
|
|
|
return elements;
|
|
|
|
}
|
|
|
|
|
2017-04-11 22:11:14 +08:00
|
|
|
static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
|
|
|
|
struct net_device *dev)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
int rc;
|
2017-04-07 15:15:36 +08:00
|
|
|
__be16 *tag;
|
2008-02-15 16:19:42 +08:00
|
|
|
struct qeth_hdr *hdr = NULL;
|
2016-06-16 22:18:52 +08:00
|
|
|
int hdr_elements = 0;
|
|
|
|
int elements;
|
2008-07-26 17:24:10 +08:00
|
|
|
struct qeth_card *card = dev->ml_priv;
|
2008-02-15 16:19:42 +08:00
|
|
|
struct sk_buff *new_skb = NULL;
|
|
|
|
int ipv = qeth_get_ip_version(skb);
|
2009-08-26 10:01:08 +08:00
|
|
|
int cast_type = qeth_l3_get_cast_type(card, skb);
|
2014-04-28 16:05:08 +08:00
|
|
|
struct qeth_qdio_out_q *queue =
|
|
|
|
card->qdio.out_qs[card->qdio.do_prio_queueing
|
|
|
|
|| (cast_type && card->info.is_multicast_different) ?
|
|
|
|
qeth_get_priority_queue(card, skb, ipv, cast_type) :
|
|
|
|
card->qdio.default_out_queue];
|
2008-02-15 16:19:42 +08:00
|
|
|
int tx_bytes = skb->len;
|
2017-08-18 16:19:06 +08:00
|
|
|
unsigned int hd_len = 0;
|
2016-06-16 22:18:53 +08:00
|
|
|
bool use_tso;
|
2008-08-01 22:39:13 +08:00
|
|
|
int data_offset = -1;
|
2017-08-15 23:02:39 +08:00
|
|
|
unsigned int nr_frags;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2011-08-08 09:33:59 +08:00
|
|
|
if (((card->info.type == QETH_CARD_TYPE_IQD) &&
|
|
|
|
(((card->options.cq != QETH_CQ_ENABLED) && !ipv) ||
|
|
|
|
((card->options.cq == QETH_CQ_ENABLED) &&
|
2017-04-07 15:15:36 +08:00
|
|
|
(be16_to_cpu(skb->protocol) != ETH_P_AF_IUCV)))) ||
|
2011-08-08 09:33:59 +08:00
|
|
|
card->options.sniffer)
|
2008-02-15 16:19:42 +08:00
|
|
|
goto tx_drop;
|
|
|
|
|
|
|
|
if ((card->state != CARD_STATE_UP) || !card->lan_online) {
|
|
|
|
card->stats.tx_carrier_errors++;
|
|
|
|
goto tx_drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((cast_type == RTN_BROADCAST) &&
|
|
|
|
(card->info.broadcast_capable == 0))
|
|
|
|
goto tx_drop;
|
|
|
|
|
|
|
|
if (card->options.performance_stats) {
|
|
|
|
card->perf_stats.outbound_cnt++;
|
|
|
|
card->perf_stats.outbound_start_time = qeth_get_micros();
|
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:53 +08:00
|
|
|
/* Ignore segment size from skb_is_gso(), 1 page is always used. */
|
|
|
|
use_tso = skb_is_gso(skb) &&
|
2017-08-15 23:02:45 +08:00
|
|
|
(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4);
|
2008-08-01 22:39:13 +08:00
|
|
|
|
2017-06-06 20:33:43 +08:00
|
|
|
if (card->info.type == QETH_CARD_TYPE_IQD) {
|
2008-08-01 22:39:13 +08:00
|
|
|
new_skb = skb;
|
s390/qeth: no ETH header for outbound AF_IUCV
With AF_IUCV traffic, the skb passed to hard_start_xmit() has a 14 byte
slot at skb->data, intended for an ETH header. qeth_l3_fill_af_iucv_hdr()
fills this ETH header... and then immediately moves it to the
skb's headroom, where it disappears and is never seen again.
But it's still possible for us to return NETDEV_TX_BUSY after the skb has
been modified. Since we didn't get a private copy of the skb, the next
time the skb is delivered to hard_start_xmit() it no longer has the
expected layout (we moved the ETH header to the headroom, so skb->data
now starts at the IUCV_TRANS header). So when qeth_l3_fill_af_iucv_hdr()
does another round of rebuilding, the resulting qeth header ends up
all wrong. On transmission, the buffer is then rejected by
the HiperSockets device with SBALF15 = x'04'.
When this error is passed back to af_iucv as TX_NOTIFY_UNREACHABLE, it
tears down the offending socket.
As the ETH header for AF_IUCV serves no purpose, just align the code to
what we do for IP traffic on L3 HiperSockets: keep the ETH header at
skb->data, and pass down data_offset = ETH_HLEN to qeth_fill_buffer().
When mapping the payload into the SBAL elements, the ETH header is then
stripped off. This avoids the skb manipulations in
qeth_l3_fill_af_iucv_hdr(), and any buffer re-entering hard_start_xmit()
after NETDEV_TX_BUSY is now processed properly.
Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: Ursula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-03-23 21:55:09 +08:00
|
|
|
data_offset = ETH_HLEN;
|
2017-08-18 16:19:08 +08:00
|
|
|
hd_len = sizeof(*hdr);
|
2008-08-01 22:39:13 +08:00
|
|
|
hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC);
|
|
|
|
if (!hdr)
|
|
|
|
goto tx_drop;
|
2016-06-16 22:18:52 +08:00
|
|
|
hdr_elements++;
|
2008-08-01 22:39:13 +08:00
|
|
|
} else {
|
|
|
|
/* create a clone with writeable headroom */
|
|
|
|
new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso)
|
|
|
|
+ VLAN_HLEN);
|
|
|
|
if (!new_skb)
|
|
|
|
goto tx_drop;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-03-09 04:36:54 +08:00
|
|
|
if (ipv == 4) {
|
2012-05-11 03:50:52 +08:00
|
|
|
skb_pull(new_skb, ETH_HLEN);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2015-01-14 00:13:44 +08:00
|
|
|
if (ipv != 4 && skb_vlan_tag_present(new_skb)) {
|
2008-02-15 16:19:42 +08:00
|
|
|
skb_push(new_skb, VLAN_HLEN);
|
|
|
|
skb_copy_to_linear_data(new_skb, new_skb->data + 4, 4);
|
|
|
|
skb_copy_to_linear_data_offset(new_skb, 4,
|
|
|
|
new_skb->data + 8, 4);
|
|
|
|
skb_copy_to_linear_data_offset(new_skb, 8,
|
|
|
|
new_skb->data + 12, 4);
|
2017-04-07 15:15:36 +08:00
|
|
|
tag = (__be16 *)(new_skb->data + 12);
|
|
|
|
*tag = cpu_to_be16(ETH_P_8021Q);
|
|
|
|
*(tag + 1) = cpu_to_be16(skb_vlan_tag_get(new_skb));
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
netif_stop_queue(dev);
|
|
|
|
|
|
|
|
/* fix hardware limitation: as long as we do not have sbal
|
2009-03-25 04:57:16 +08:00
|
|
|
* chaining we can not send long frag lists
|
2008-02-15 16:19:42 +08:00
|
|
|
*/
|
2016-06-16 22:18:53 +08:00
|
|
|
if ((card->info.type != QETH_CARD_TYPE_IQD) &&
|
|
|
|
((use_tso && !qeth_l3_get_elements_no_tso(card, new_skb, 1)) ||
|
2017-03-23 21:55:08 +08:00
|
|
|
(!use_tso && !qeth_get_elements_no(card, new_skb, 0, 0)))) {
|
2016-06-16 22:18:57 +08:00
|
|
|
int lin_rc = skb_linearize(new_skb);
|
|
|
|
|
|
|
|
if (card->options.performance_stats) {
|
|
|
|
if (lin_rc)
|
|
|
|
card->perf_stats.tx_linfail++;
|
|
|
|
else
|
|
|
|
card->perf_stats.tx_lin++;
|
|
|
|
}
|
|
|
|
if (lin_rc)
|
2016-06-16 22:18:53 +08:00
|
|
|
goto tx_drop;
|
2009-03-25 04:57:16 +08:00
|
|
|
}
|
2017-08-15 23:02:39 +08:00
|
|
|
nr_frags = skb_shinfo(new_skb)->nr_frags;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2016-06-16 22:18:53 +08:00
|
|
|
if (use_tso) {
|
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:23 +08:00
|
|
|
hdr = skb_push(new_skb, sizeof(struct qeth_hdr_tso));
|
2008-02-15 16:19:42 +08:00
|
|
|
memset(hdr, 0, sizeof(struct qeth_hdr_tso));
|
|
|
|
qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type);
|
|
|
|
qeth_tso_fill_header(card, hdr, new_skb);
|
2016-06-16 22:18:52 +08:00
|
|
|
hdr_elements++;
|
2008-02-15 16:19:42 +08:00
|
|
|
} else {
|
2008-08-01 22:39:13 +08:00
|
|
|
if (data_offset < 0) {
|
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:23 +08:00
|
|
|
hdr = skb_push(new_skb, sizeof(struct qeth_hdr));
|
2008-08-01 22:39:13 +08:00
|
|
|
qeth_l3_fill_header(card, hdr, new_skb, ipv,
|
|
|
|
cast_type);
|
|
|
|
} else {
|
2017-04-07 15:15:36 +08:00
|
|
|
if (be16_to_cpu(new_skb->protocol) == ETH_P_AF_IUCV)
|
2011-08-08 09:33:59 +08:00
|
|
|
qeth_l3_fill_af_iucv_hdr(card, hdr, new_skb);
|
|
|
|
else {
|
|
|
|
qeth_l3_fill_header(card, hdr, new_skb, ipv,
|
|
|
|
cast_type);
|
|
|
|
hdr->hdr.l3.length = new_skb->len - data_offset;
|
|
|
|
}
|
2008-08-01 22:39:13 +08:00
|
|
|
}
|
2010-05-12 03:34:45 +08:00
|
|
|
|
|
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL)
|
|
|
|
qeth_l3_hdr_csum(card, hdr, new_skb);
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:53 +08:00
|
|
|
elements = use_tso ?
|
|
|
|
qeth_l3_get_elements_no_tso(card, new_skb, hdr_elements) :
|
2017-03-23 21:55:08 +08:00
|
|
|
qeth_get_elements_no(card, new_skb, hdr_elements,
|
|
|
|
(data_offset > 0) ? data_offset : 0);
|
2016-06-16 22:18:52 +08:00
|
|
|
if (!elements) {
|
2009-03-25 04:57:16 +08:00
|
|
|
if (data_offset >= 0)
|
|
|
|
kmem_cache_free(qeth_core_header_cache, hdr);
|
|
|
|
goto tx_drop;
|
2009-03-25 04:57:15 +08:00
|
|
|
}
|
2016-06-16 22:18:52 +08:00
|
|
|
elements += hdr_elements;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:10 +08:00
|
|
|
if (card->info.type != QETH_CARD_TYPE_IQD) {
|
|
|
|
int len;
|
2017-08-18 16:19:06 +08:00
|
|
|
if (use_tso) {
|
|
|
|
hd_len = sizeof(struct qeth_hdr_tso) +
|
|
|
|
ip_hdrlen(new_skb) + tcp_hdrlen(new_skb);
|
|
|
|
len = hd_len;
|
|
|
|
} else {
|
2010-06-22 06:57:10 +08:00
|
|
|
len = sizeof(struct qeth_hdr_layer3);
|
2017-08-18 16:19:06 +08:00
|
|
|
}
|
2010-06-22 06:57:10 +08:00
|
|
|
|
2013-04-22 09:12:29 +08:00
|
|
|
if (qeth_hdr_chk_and_bounce(new_skb, &hdr, len))
|
2010-06-22 06:57:10 +08:00
|
|
|
goto tx_drop;
|
2017-08-18 16:19:06 +08:00
|
|
|
rc = qeth_do_send_packet(card, queue, new_skb, hdr, hd_len,
|
2017-08-18 16:19:07 +08:00
|
|
|
hd_len, elements);
|
2010-06-22 06:57:10 +08:00
|
|
|
} else
|
2017-09-19 03:18:18 +08:00
|
|
|
rc = qeth_do_send_packet_fast(queue, new_skb, hdr, data_offset,
|
|
|
|
hd_len);
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
if (!rc) {
|
|
|
|
card->stats.tx_packets++;
|
|
|
|
card->stats.tx_bytes += tx_bytes;
|
|
|
|
if (new_skb != skb)
|
|
|
|
dev_kfree_skb_any(skb);
|
|
|
|
if (card->options.performance_stats) {
|
2016-06-16 22:18:53 +08:00
|
|
|
if (use_tso) {
|
2008-02-15 16:19:42 +08:00
|
|
|
card->perf_stats.large_send_bytes += tx_bytes;
|
|
|
|
card->perf_stats.large_send_cnt++;
|
|
|
|
}
|
2009-03-25 04:57:16 +08:00
|
|
|
if (nr_frags) {
|
2008-02-15 16:19:42 +08:00
|
|
|
card->perf_stats.sg_skbs_sent++;
|
|
|
|
/* nr_frags + skb->data */
|
2009-03-25 04:57:16 +08:00
|
|
|
card->perf_stats.sg_frags_sent += nr_frags + 1;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
}
|
2009-07-06 10:23:38 +08:00
|
|
|
rc = NETDEV_TX_OK;
|
2008-02-15 16:19:42 +08:00
|
|
|
} else {
|
2008-08-01 22:39:13 +08:00
|
|
|
if (data_offset >= 0)
|
|
|
|
kmem_cache_free(qeth_core_header_cache, hdr);
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
if (rc == -EBUSY) {
|
|
|
|
if (new_skb != skb)
|
|
|
|
dev_kfree_skb_any(new_skb);
|
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
} else
|
|
|
|
goto tx_drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
netif_wake_queue(dev);
|
|
|
|
if (card->options.performance_stats)
|
|
|
|
card->perf_stats.outbound_time += qeth_get_micros() -
|
|
|
|
card->perf_stats.outbound_start_time;
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
tx_drop:
|
|
|
|
card->stats.tx_dropped++;
|
|
|
|
card->stats.tx_errors++;
|
|
|
|
if ((new_skb != skb) && new_skb)
|
|
|
|
dev_kfree_skb_any(new_skb);
|
|
|
|
dev_kfree_skb_any(skb);
|
2008-06-06 18:37:48 +08:00
|
|
|
netif_wake_queue(dev);
|
2008-02-15 16:19:42 +08:00
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
|
2011-01-13 04:42:24 +08:00
|
|
|
static int __qeth_l3_open(struct net_device *dev)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
2008-07-26 17:24:10 +08:00
|
|
|
struct qeth_card *card = dev->ml_priv;
|
2010-09-08 05:14:42 +08:00
|
|
|
int rc = 0;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "qethopen");
|
2011-01-13 04:42:24 +08:00
|
|
|
if (card->state == CARD_STATE_UP)
|
|
|
|
return rc;
|
2008-02-15 16:19:42 +08:00
|
|
|
if (card->state != CARD_STATE_SOFTSETUP)
|
|
|
|
return -ENODEV;
|
|
|
|
card->data.state = CH_STATE_UP;
|
|
|
|
card->state = CARD_STATE_UP;
|
|
|
|
netif_start_queue(dev);
|
|
|
|
|
2010-09-08 05:14:42 +08:00
|
|
|
if (qdio_stop_irq(card->data.ccwdev, 0) >= 0) {
|
|
|
|
napi_enable(&card->napi);
|
|
|
|
napi_schedule(&card->napi);
|
|
|
|
} else
|
|
|
|
rc = -EIO;
|
|
|
|
return rc;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
2011-01-13 04:42:24 +08:00
|
|
|
static int qeth_l3_open(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = dev->ml_priv;
|
|
|
|
|
|
|
|
QETH_CARD_TEXT(card, 5, "qethope_");
|
|
|
|
if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
|
|
|
|
QETH_CARD_TEXT(card, 3, "openREC");
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
}
|
|
|
|
return __qeth_l3_open(dev);
|
|
|
|
}
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
static int qeth_l3_stop(struct net_device *dev)
|
|
|
|
{
|
2008-07-26 17:24:10 +08:00
|
|
|
struct qeth_card *card = dev->ml_priv;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 4, "qethstop");
|
2008-02-15 16:19:42 +08:00
|
|
|
netif_tx_disable(dev);
|
2010-09-08 05:14:42 +08:00
|
|
|
if (card->state == CARD_STATE_UP) {
|
2008-02-15 16:19:42 +08:00
|
|
|
card->state = CARD_STATE_SOFTSETUP;
|
2010-09-08 05:14:42 +08:00
|
|
|
napi_disable(&card->napi);
|
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-09-02 16:03:33 +08:00
|
|
|
static const struct ethtool_ops qeth_l3_ethtool_ops = {
|
2008-02-15 16:19:42 +08:00
|
|
|
.get_link = ethtool_op_get_link,
|
|
|
|
.get_strings = qeth_core_get_strings,
|
|
|
|
.get_ethtool_stats = qeth_core_get_ethtool_stats,
|
2009-10-01 19:24:32 +08:00
|
|
|
.get_sset_count = qeth_core_get_sset_count,
|
2008-02-15 16:19:42 +08:00
|
|
|
.get_drvinfo = qeth_core_get_drvinfo,
|
2017-04-11 22:11:17 +08:00
|
|
|
.get_link_ksettings = qeth_core_ethtool_get_link_ksettings,
|
2008-02-15 16:19:42 +08: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;
|
|
|
|
}
|
|
|
|
|
2009-01-09 11:44:00 +08:00
|
|
|
static const struct net_device_ops qeth_l3_netdev_ops = {
|
2009-01-09 02:50:55 +08:00
|
|
|
.ndo_open = qeth_l3_open,
|
|
|
|
.ndo_stop = qeth_l3_stop,
|
|
|
|
.ndo_get_stats = qeth_get_stats,
|
|
|
|
.ndo_start_xmit = qeth_l3_hard_start_xmit,
|
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
2011-08-16 14:29:01 +08:00
|
|
|
.ndo_set_rx_mode = qeth_l3_set_multicast_list,
|
2017-04-11 22:11:10 +08:00
|
|
|
.ndo_do_ioctl = qeth_do_ioctl,
|
2011-07-20 12:54:41 +08:00
|
|
|
.ndo_change_mtu = qeth_change_mtu,
|
2016-06-16 22:18:59 +08:00
|
|
|
.ndo_fix_features = qeth_fix_features,
|
|
|
|
.ndo_set_features = qeth_set_features,
|
2009-01-09 02:50:55 +08: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 12:54:41 +08:00
|
|
|
.ndo_tx_timeout = qeth_tx_timeout,
|
2009-01-09 02:50:55 +08:00
|
|
|
};
|
|
|
|
|
2009-01-09 11:44:00 +08:00
|
|
|
static const struct net_device_ops qeth_l3_osa_netdev_ops = {
|
|
|
|
.ndo_open = qeth_l3_open,
|
|
|
|
.ndo_stop = qeth_l3_stop,
|
|
|
|
.ndo_get_stats = qeth_get_stats,
|
|
|
|
.ndo_start_xmit = qeth_l3_hard_start_xmit,
|
2017-12-01 17:14:50 +08:00
|
|
|
.ndo_features_check = qeth_features_check,
|
2009-01-09 11:44:00 +08:00
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
2011-08-16 14:29:01 +08:00
|
|
|
.ndo_set_rx_mode = qeth_l3_set_multicast_list,
|
2017-04-11 22:11:10 +08:00
|
|
|
.ndo_do_ioctl = qeth_do_ioctl,
|
2011-07-20 12:54:41 +08:00
|
|
|
.ndo_change_mtu = qeth_change_mtu,
|
2016-06-16 22:18:59 +08:00
|
|
|
.ndo_fix_features = qeth_fix_features,
|
|
|
|
.ndo_set_features = qeth_set_features,
|
2009-01-09 11:44:00 +08: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 12:54:41 +08:00
|
|
|
.ndo_tx_timeout = qeth_tx_timeout,
|
2009-01-09 11:44:00 +08:00
|
|
|
.ndo_neigh_setup = qeth_l3_neigh_setup,
|
|
|
|
};
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
static int qeth_l3_setup_netdev(struct qeth_card *card)
|
|
|
|
{
|
2015-01-21 20:39:10 +08:00
|
|
|
int rc;
|
|
|
|
|
2010-05-17 05:15:14 +08:00
|
|
|
if (card->info.type == QETH_CARD_TYPE_OSD ||
|
|
|
|
card->info.type == QETH_CARD_TYPE_OSX) {
|
2008-02-15 16:19:42 +08:00
|
|
|
if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) ||
|
|
|
|
(card->info.link_type == QETH_LINK_TYPE_HSTR)) {
|
2012-05-11 03:50:52 +08:00
|
|
|
pr_info("qeth_l3: ignoring TR device\n");
|
|
|
|
return -ENODEV;
|
2008-02-15 16:19:42 +08:00
|
|
|
} else {
|
|
|
|
card->dev = alloc_etherdev(0);
|
|
|
|
if (!card->dev)
|
|
|
|
return -ENODEV;
|
2009-01-09 11:44:00 +08:00
|
|
|
card->dev->netdev_ops = &qeth_l3_osa_netdev_ops;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
/*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;
|
2011-05-13 02:45:01 +08:00
|
|
|
if (!card->info.guestlan) {
|
|
|
|
card->dev->hw_features = NETIF_F_SG |
|
|
|
|
NETIF_F_RXCSUM | NETIF_F_IP_CSUM |
|
|
|
|
NETIF_F_TSO;
|
2016-06-16 22:19:00 +08:00
|
|
|
card->dev->vlan_features = NETIF_F_SG |
|
|
|
|
NETIF_F_RXCSUM | NETIF_F_IP_CSUM |
|
|
|
|
NETIF_F_TSO;
|
2017-12-01 17:14:50 +08:00
|
|
|
card->dev->features |= NETIF_F_SG;
|
2011-05-13 02:45:01 +08:00
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
} else if (card->info.type == QETH_CARD_TYPE_IQD) {
|
net: set name_assign_type in alloc_netdev()
Extend alloc_netdev{,_mq{,s}}() to take name_assign_type as argument, and convert
all users to pass NET_NAME_UNKNOWN.
Coccinelle patch:
@@
expression sizeof_priv, name, setup, txqs, rxqs, count;
@@
(
-alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
+alloc_netdev_mqs(sizeof_priv, name, NET_NAME_UNKNOWN, setup, txqs, rxqs)
|
-alloc_netdev_mq(sizeof_priv, name, setup, count)
+alloc_netdev_mq(sizeof_priv, name, NET_NAME_UNKNOWN, setup, count)
|
-alloc_netdev(sizeof_priv, name, setup)
+alloc_netdev(sizeof_priv, name, NET_NAME_UNKNOWN, setup)
)
v9: move comments here from the wrong commit
Signed-off-by: Tom Gundersen <teg@jklm.no>
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-07-14 22:37:24 +08:00
|
|
|
card->dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN,
|
|
|
|
ether_setup);
|
2008-02-15 16:19:42 +08:00
|
|
|
if (!card->dev)
|
|
|
|
return -ENODEV;
|
|
|
|
card->dev->flags |= IFF_NOARP;
|
2009-01-09 11:44:00 +08:00
|
|
|
card->dev->netdev_ops = &qeth_l3_netdev_ops;
|
2015-01-21 20:39:10 +08:00
|
|
|
rc = qeth_l3_iqd_read_initial_mac(card);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
2011-08-08 09:33:59 +08:00
|
|
|
if (card->options.hsuid[0])
|
|
|
|
memcpy(card->dev->perm_addr, card->options.hsuid, 9);
|
2008-02-15 16:19:42 +08:00
|
|
|
} else
|
|
|
|
return -ENODEV;
|
|
|
|
|
2008-07-26 17:24:10 +08:00
|
|
|
card->dev->ml_priv = card;
|
2008-02-15 16:19:42 +08:00
|
|
|
card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
|
|
|
|
card->dev->mtu = card->info.initial_mtu;
|
2016-10-21 01:55:23 +08:00
|
|
|
card->dev->min_mtu = 64;
|
|
|
|
card->dev->max_mtu = ETH_MAX_MTU;
|
2014-05-11 08:12:32 +08:00
|
|
|
card->dev->ethtool_ops = &qeth_l3_ethtool_ops;
|
2013-04-19 10:04:27 +08:00
|
|
|
card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX |
|
|
|
|
NETIF_F_HW_VLAN_CTAG_RX |
|
|
|
|
NETIF_F_HW_VLAN_CTAG_FILTER;
|
2014-10-06 09:38:35 +08:00
|
|
|
netif_keep_dst(card->dev);
|
2017-12-01 17:14:51 +08:00
|
|
|
netif_set_gso_max_size(card->dev, (QETH_MAX_BUFFER_ELEMENTS(card) - 1) *
|
|
|
|
PAGE_SIZE);
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
|
2017-04-11 22:11:11 +08:00
|
|
|
netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT);
|
2015-12-11 19:27:55 +08:00
|
|
|
netif_carrier_off(card->dev);
|
2008-02-15 16:19:42 +08:00
|
|
|
return register_netdev(card->dev);
|
|
|
|
}
|
|
|
|
|
2017-06-06 20:33:49 +08:00
|
|
|
static const struct device_type qeth_l3_devtype = {
|
|
|
|
.name = "qeth_layer3",
|
|
|
|
.groups = qeth_l3_attr_groups,
|
|
|
|
};
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
static int qeth_l3_probe_device(struct ccwgroup_device *gdev)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
|
2017-05-11 01:07:51 +08:00
|
|
|
int rc;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2017-06-06 20:33:49 +08:00
|
|
|
if (gdev->dev.type == &qeth_generic_devtype) {
|
|
|
|
rc = qeth_l3_create_device_attributes(&gdev->dev);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
}
|
2017-05-11 01:07:54 +08:00
|
|
|
hash_init(card->ip_htable);
|
|
|
|
hash_init(card->ip_mc_htable);
|
2008-02-15 16:19:42 +08:00
|
|
|
card->options.layer2 = 0;
|
2011-05-13 02:45:02 +08:00
|
|
|
card->info.hwtrap = 0;
|
2008-02-15 16:19:42 +08: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 20:33:49 +08:00
|
|
|
if (cgdev->dev.type == &qeth_generic_devtype)
|
|
|
|
qeth_l3_remove_device_attributes(&cgdev->dev);
|
2010-07-23 07:15:05 +08:00
|
|
|
|
2009-05-20 05:38:37 +08:00
|
|
|
qeth_set_allowed_threads(card, 0, 1);
|
2008-02-15 16:19:42 +08:00
|
|
|
wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
|
|
|
|
|
2011-02-27 14:41:36 +08:00
|
|
|
if (cgdev->state == CCWGROUP_ONLINE)
|
2008-02-15 16:19:42 +08:00
|
|
|
qeth_l3_set_offline(cgdev);
|
|
|
|
|
|
|
|
if (card->dev) {
|
2016-07-04 20:07:16 +08:00
|
|
|
netif_napi_del(&card->napi);
|
2008-02-15 16:19:42 +08:00
|
|
|
unregister_netdev(card->dev);
|
|
|
|
card->dev = NULL;
|
|
|
|
}
|
|
|
|
|
2016-06-16 22:18:58 +08:00
|
|
|
qeth_l3_clear_ip_htable(card, 0);
|
2008-02-15 16:19:42 +08:00
|
|
|
qeth_l3_clear_ipato_list(card);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
|
|
|
|
int rc = 0;
|
|
|
|
enum qeth_card_states recover_flag;
|
|
|
|
|
2010-07-23 07:15:05 +08:00
|
|
|
mutex_lock(&card->discipline_mutex);
|
2010-05-12 03:34:47 +08:00
|
|
|
mutex_lock(&card->conf_mutex);
|
2008-04-01 16:26:58 +08:00
|
|
|
QETH_DBF_TEXT(SETUP, 2, "setonlin");
|
|
|
|
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
recover_flag = card->state;
|
|
|
|
rc = qeth_core_hardsetup_card(card);
|
|
|
|
if (rc) {
|
2015-01-21 20:39:10 +08:00
|
|
|
QETH_DBF_TEXT_(SETUP, 2, "2err%04x", rc);
|
2009-11-12 08:11:43 +08:00
|
|
|
rc = -ENODEV;
|
2008-02-15 16:19:42 +08:00
|
|
|
goto out_remove;
|
|
|
|
}
|
|
|
|
|
2009-11-12 08:11:43 +08:00
|
|
|
if (!card->dev && qeth_l3_setup_netdev(card)) {
|
|
|
|
rc = -ENODEV;
|
2008-02-15 16:19:42 +08:00
|
|
|
goto out_remove;
|
2009-11-12 08:11:43 +08:00
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
|
2011-05-13 02:45:02 +08:00
|
|
|
if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) {
|
|
|
|
if (card->info.hwtrap &&
|
|
|
|
qeth_hw_trap(card, QETH_DIAGS_TRAP_ARM))
|
|
|
|
card->info.hwtrap = 0;
|
|
|
|
} else
|
|
|
|
card->info.hwtrap = 0;
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
card->state = CARD_STATE_HARDSETUP;
|
2010-09-08 05:14:42 +08:00
|
|
|
memset(&card->rx, 0, sizeof(struct qeth_rx));
|
2008-02-15 16:19:42 +08:00
|
|
|
qeth_print_status_message(card);
|
|
|
|
|
|
|
|
/* softsetup */
|
2008-04-01 16:26:58 +08:00
|
|
|
QETH_DBF_TEXT(SETUP, 2, "softsetp");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
rc = qeth_l3_setadapter_parms(card);
|
|
|
|
if (rc)
|
2015-01-21 20:39:10 +08:00
|
|
|
QETH_DBF_TEXT_(SETUP, 2, "2err%04x", rc);
|
2010-01-11 10:50:50 +08:00
|
|
|
if (!card->options.sniffer) {
|
|
|
|
rc = qeth_l3_start_ipassists(card);
|
2013-01-21 10:30:20 +08:00
|
|
|
if (rc) {
|
2010-01-11 10:50:50 +08:00
|
|
|
QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
|
2013-01-21 10:30:20 +08:00
|
|
|
goto out_remove;
|
|
|
|
}
|
2010-01-11 10:50:50 +08:00
|
|
|
rc = qeth_l3_setrouting_v4(card);
|
|
|
|
if (rc)
|
2015-01-21 20:39:10 +08:00
|
|
|
QETH_DBF_TEXT_(SETUP, 2, "4err%04x", rc);
|
2010-01-11 10:50:50 +08:00
|
|
|
rc = qeth_l3_setrouting_v6(card);
|
|
|
|
if (rc)
|
2015-01-21 20:39:10 +08:00
|
|
|
QETH_DBF_TEXT_(SETUP, 2, "5err%04x", rc);
|
2010-01-11 10:50:50 +08:00
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
netif_tx_disable(card->dev);
|
|
|
|
|
|
|
|
rc = qeth_init_qdio_queues(card);
|
|
|
|
if (rc) {
|
2008-04-01 16:26:58 +08:00
|
|
|
QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
|
2009-11-12 08:11:43 +08:00
|
|
|
rc = -ENODEV;
|
2008-02-15 16:19:42 +08:00
|
|
|
goto out_remove;
|
|
|
|
}
|
|
|
|
card->state = CARD_STATE_SOFTSETUP;
|
|
|
|
|
|
|
|
qeth_set_allowed_threads(card, 0xffffffff, 0);
|
2016-06-16 22:18:58 +08:00
|
|
|
qeth_l3_recover_ip(card);
|
2010-11-26 10:41:19 +08:00
|
|
|
if (card->lan_online)
|
|
|
|
netif_carrier_on(card->dev);
|
|
|
|
else
|
|
|
|
netif_carrier_off(card->dev);
|
2008-04-24 16:15:25 +08:00
|
|
|
if (recover_flag == CARD_STATE_RECOVER) {
|
2011-12-20 06:56:32 +08:00
|
|
|
rtnl_lock();
|
2008-04-24 16:15:25 +08:00
|
|
|
if (recovery_mode)
|
2011-01-13 04:42:24 +08:00
|
|
|
__qeth_l3_open(card->dev);
|
2011-12-20 06:56:32 +08:00
|
|
|
else
|
2008-04-24 16:15:25 +08:00
|
|
|
dev_open(card->dev);
|
|
|
|
qeth_l3_set_multicast_list(card->dev);
|
2016-09-15 20:39:21 +08:00
|
|
|
qeth_recover_features(card->dev);
|
2011-12-20 06:56:32 +08:00
|
|
|
rtnl_unlock();
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
2013-03-19 04:04:42 +08:00
|
|
|
qeth_trace_features(card);
|
2008-02-15 16:19:42 +08:00
|
|
|
/* let user_space know that device is online */
|
|
|
|
kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
|
2010-05-12 03:34:47 +08:00
|
|
|
mutex_unlock(&card->conf_mutex);
|
2010-07-23 07:15:05 +08:00
|
|
|
mutex_unlock(&card->discipline_mutex);
|
2008-02-15 16:19:42 +08:00
|
|
|
return 0;
|
|
|
|
out_remove:
|
|
|
|
qeth_l3_stop_card(card, 0);
|
|
|
|
ccw_device_set_offline(CARD_DDEV(card));
|
|
|
|
ccw_device_set_offline(CARD_WDEV(card));
|
|
|
|
ccw_device_set_offline(CARD_RDEV(card));
|
2014-02-24 20:12:06 +08:00
|
|
|
qdio_free(CARD_DDEV(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
if (recover_flag == CARD_STATE_RECOVER)
|
|
|
|
card->state = CARD_STATE_RECOVER;
|
|
|
|
else
|
|
|
|
card->state = CARD_STATE_DOWN;
|
2010-05-12 03:34:47 +08:00
|
|
|
mutex_unlock(&card->conf_mutex);
|
2010-07-23 07:15:05 +08:00
|
|
|
mutex_unlock(&card->discipline_mutex);
|
2009-11-12 08:11:43 +08:00
|
|
|
return rc;
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_set_online(struct ccwgroup_device *gdev)
|
|
|
|
{
|
|
|
|
return __qeth_l3_set_online(gdev, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev,
|
|
|
|
int recovery_mode)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
|
|
|
|
int rc = 0, rc2 = 0, rc3 = 0;
|
|
|
|
enum qeth_card_states recover_flag;
|
|
|
|
|
2010-07-23 07:15:05 +08:00
|
|
|
mutex_lock(&card->discipline_mutex);
|
2010-05-12 03:34:47 +08:00
|
|
|
mutex_lock(&card->conf_mutex);
|
2008-04-01 16:26:58 +08:00
|
|
|
QETH_DBF_TEXT(SETUP, 3, "setoffl");
|
|
|
|
QETH_DBF_HEX(SETUP, 3, &card, sizeof(void *));
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
if (card->dev && netif_carrier_ok(card->dev))
|
|
|
|
netif_carrier_off(card->dev);
|
|
|
|
recover_flag = card->state;
|
2011-05-13 02:45:02 +08:00
|
|
|
if ((!recovery_mode && card->info.hwtrap) || card->info.hwtrap == 2) {
|
|
|
|
qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
|
|
|
|
card->info.hwtrap = 1;
|
|
|
|
}
|
2008-10-24 17:16:52 +08:00
|
|
|
qeth_l3_stop_card(card, recovery_mode);
|
2011-12-20 06:56:36 +08:00
|
|
|
if ((card->options.cq == QETH_CQ_ENABLED) && card->dev) {
|
|
|
|
rtnl_lock();
|
|
|
|
call_netdevice_notifiers(NETDEV_REBOOT, card->dev);
|
|
|
|
rtnl_unlock();
|
|
|
|
}
|
2008-02-15 16:19:42 +08:00
|
|
|
rc = ccw_device_set_offline(CARD_DDEV(card));
|
|
|
|
rc2 = ccw_device_set_offline(CARD_WDEV(card));
|
|
|
|
rc3 = ccw_device_set_offline(CARD_RDEV(card));
|
|
|
|
if (!rc)
|
|
|
|
rc = (rc2) ? rc2 : rc3;
|
|
|
|
if (rc)
|
2008-04-01 16:26:58 +08:00
|
|
|
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
|
2014-02-24 20:12:06 +08:00
|
|
|
qdio_free(CARD_DDEV(card));
|
2008-02-15 16:19:42 +08:00
|
|
|
if (recover_flag == CARD_STATE_UP)
|
|
|
|
card->state = CARD_STATE_RECOVER;
|
|
|
|
/* let user_space know that device is offline */
|
|
|
|
kobject_uevent(&cgdev->dev.kobj, KOBJ_CHANGE);
|
2010-05-12 03:34:47 +08:00
|
|
|
mutex_unlock(&card->conf_mutex);
|
2010-07-23 07:15:05 +08:00
|
|
|
mutex_unlock(&card->discipline_mutex);
|
2008-02-15 16:19:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_set_offline(struct ccwgroup_device *cgdev)
|
|
|
|
{
|
|
|
|
return __qeth_l3_set_offline(cgdev, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_recover(void *ptr)
|
|
|
|
{
|
|
|
|
struct qeth_card *card;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
card = (struct qeth_card *) ptr;
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "recover1");
|
|
|
|
QETH_CARD_HEX(card, 2, &card, sizeof(void *));
|
2008-02-15 16:19:42 +08:00
|
|
|
if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
|
|
|
|
return 0;
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 2, "recover2");
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_warn(&card->gdev->dev,
|
|
|
|
"A recovery process has been started for the device\n");
|
2013-04-08 06:19:27 +08:00
|
|
|
qeth_set_recovery_task(card);
|
2008-02-15 16:19:42 +08:00
|
|
|
__qeth_l3_set_offline(card->gdev, 1);
|
|
|
|
rc = __qeth_l3_set_online(card->gdev, 1);
|
|
|
|
if (!rc)
|
2008-12-25 20:39:49 +08:00
|
|
|
dev_info(&card->gdev->dev,
|
|
|
|
"Device successfully recovered!\n");
|
2008-09-19 18:56:03 +08:00
|
|
|
else {
|
2013-01-21 10:30:20 +08:00
|
|
|
qeth_close_dev(card);
|
|
|
|
dev_warn(&card->gdev->dev, "The qeth device driver "
|
qeth: fix deadlock between recovery and bonding driver
The recovery thread, when failing, tears down the respective interface. To do
so, it needs to obtain the rtnl lock first, as the interface configuration is
changed.
If another process tries to modify an interface setting at the same time, that
process can obtain the rtnl lock first, but the respective callback in the qeth
driver will block until recovery has completed - which cannot happen since the
calling process already obtained it.
In one particular case, the bonding driver acquired the rtnl lock to modify the
card's MAC address, while the recovery failed at the same time due to the card
being removed. Hence qeth_l2_set_mac_address (implicitly holding the rtnl lock)
was waiting on qeth_l2_recover, which deadlocked when waiting on the rtnl lock.
This patch uses rtnl_trylock instead of rtnl_lock in the recovery thread. If the
lock cannot be obtained, the interface will be left up, but the card state
remains in CARD_STATE_RECOVER, which will prevent any further activities on the
card.
Signed-off-by: Stefan Raspl <raspl@linux.vnet.ibm.com>
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Reviewed-by: Ursula Braun <ursula.braun@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-10-16 03:21:18 +08:00
|
|
|
"failed to recover an error on the device\n");
|
2008-09-19 18:56:03 +08:00
|
|
|
}
|
2013-04-08 06:19:27 +08:00
|
|
|
qeth_clear_recovery_task(card);
|
2010-09-08 05:14:42 +08:00
|
|
|
qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
|
|
|
|
qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
|
2008-02-15 16:19:42 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-16 16:30:31 +08:00
|
|
|
static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
|
|
|
|
|
|
|
|
if (card->dev)
|
|
|
|
netif_device_detach(card->dev);
|
|
|
|
qeth_set_allowed_threads(card, 0, 1);
|
|
|
|
wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
|
|
|
|
if (gdev->state == CCWGROUP_OFFLINE)
|
|
|
|
return 0;
|
|
|
|
if (card->state == CARD_STATE_UP) {
|
2011-05-13 02:45:02 +08:00
|
|
|
if (card->info.hwtrap)
|
|
|
|
qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM);
|
2009-06-16 16:30:31 +08:00
|
|
|
__qeth_l3_set_offline(card->gdev, 1);
|
|
|
|
} else
|
|
|
|
__qeth_l3_set_offline(card->gdev, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qeth_l3_pm_resume(struct ccwgroup_device *gdev)
|
|
|
|
{
|
|
|
|
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (gdev->state == CCWGROUP_OFFLINE)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (card->state == CARD_STATE_RECOVER) {
|
|
|
|
rc = __qeth_l3_set_online(card->gdev, 1);
|
|
|
|
if (rc) {
|
2010-03-09 04:36:56 +08:00
|
|
|
rtnl_lock();
|
|
|
|
dev_close(card->dev);
|
|
|
|
rtnl_unlock();
|
2009-06-16 16:30:31 +08:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
rc = __qeth_l3_set_online(card->gdev, 0);
|
|
|
|
out:
|
|
|
|
qeth_set_allowed_threads(card, 0xffffffff, 0);
|
|
|
|
if (card->dev)
|
|
|
|
netif_device_attach(card->dev);
|
|
|
|
if (rc)
|
|
|
|
dev_warn(&card->gdev->dev, "The qeth device driver "
|
|
|
|
"failed to recover an error on the device\n");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2014-01-29 16:23:48 +08: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-16 00:02:21 +08:00
|
|
|
struct qeth_discipline qeth_l3_discipline = {
|
2017-06-06 20:33:49 +08:00
|
|
|
.devtype = &qeth_l3_devtype,
|
2012-05-16 00:02:21 +08:00
|
|
|
.start_poll = qeth_qdio_start_poll,
|
|
|
|
.input_handler = (qdio_handler_t *) qeth_qdio_input_handler,
|
|
|
|
.output_handler = (qdio_handler_t *) qeth_qdio_output_handler,
|
2017-04-11 22:11:11 +08:00
|
|
|
.process_rx_buffer = qeth_l3_process_inbound_buffer,
|
2012-05-16 00:02:21 +08:00
|
|
|
.recover = qeth_l3_recover,
|
|
|
|
.setup = qeth_l3_probe_device,
|
2008-02-15 16:19:42 +08:00
|
|
|
.remove = qeth_l3_remove_device,
|
|
|
|
.set_online = qeth_l3_set_online,
|
|
|
|
.set_offline = qeth_l3_set_offline,
|
2009-06-16 16:30:31 +08:00
|
|
|
.freeze = qeth_l3_pm_suspend,
|
|
|
|
.thaw = qeth_l3_pm_resume,
|
|
|
|
.restore = qeth_l3_pm_resume,
|
2017-04-11 22:11:10 +08:00
|
|
|
.do_ioctl = qeth_l3_do_ioctl,
|
2014-01-29 16:23:48 +08:00
|
|
|
.control_event_handler = qeth_l3_control_event,
|
2008-02-15 16:19:42 +08:00
|
|
|
};
|
2012-05-16 00:02:21 +08:00
|
|
|
EXPORT_SYMBOL_GPL(qeth_l3_discipline);
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
static int qeth_l3_ip_event(struct notifier_block *this,
|
2008-03-23 09:22:42 +08:00
|
|
|
unsigned long event, void *ptr)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
2016-06-16 22:18:58 +08:00
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
|
|
|
|
struct net_device *dev = (struct net_device *)ifa->ifa_dev->dev;
|
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
struct qeth_card *card;
|
|
|
|
|
2008-03-25 20:47:49 +08:00
|
|
|
if (dev_net(dev) != &init_net)
|
2008-03-23 09:22:42 +08:00
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
2008-02-15 16:19:42 +08:00
|
|
|
card = qeth_l3_get_card_from_dev(dev);
|
|
|
|
if (!card)
|
|
|
|
return NOTIFY_DONE;
|
2012-05-16 09:28:26 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "ipevent");
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
|
2016-06-16 22:18:58 +08:00
|
|
|
if (addr) {
|
2017-04-07 15:15:36 +08:00
|
|
|
addr->u.a4.addr = be32_to_cpu(ifa->ifa_address);
|
|
|
|
addr->u.a4.mask = be32_to_cpu(ifa->ifa_mask);
|
2008-02-15 16:19:42 +08:00
|
|
|
addr->type = QETH_IP_TYPE_NORMAL;
|
|
|
|
} else
|
2016-06-16 22:18:58 +08:00
|
|
|
return NOTIFY_DONE;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case NETDEV_UP:
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
qeth_l3_add_ip(card, addr);
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
2008-02-15 16:19:42 +08:00
|
|
|
break;
|
|
|
|
case NETDEV_DOWN:
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
qeth_l3_delete_ip(card, addr);
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
2008-02-15 16:19:42 +08:00
|
|
|
break;
|
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
kfree(addr);
|
2008-02-15 16:19:42 +08:00
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 09:22:42 +08:00
|
|
|
unsigned long event, void *ptr)
|
2008-02-15 16:19:42 +08:00
|
|
|
{
|
|
|
|
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
|
|
|
|
struct net_device *dev = (struct net_device *)ifa->idev->dev;
|
|
|
|
struct qeth_ipaddr *addr;
|
|
|
|
struct qeth_card *card;
|
|
|
|
|
|
|
|
card = qeth_l3_get_card_from_dev(dev);
|
|
|
|
if (!card)
|
|
|
|
return NOTIFY_DONE;
|
2010-06-22 06:57:05 +08:00
|
|
|
QETH_CARD_TEXT(card, 3, "ip6event");
|
2008-02-15 16:19:42 +08:00
|
|
|
if (!qeth_is_supported(card, IPA_IPV6))
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
|
|
|
addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6);
|
2016-06-16 22:18:58 +08:00
|
|
|
if (addr) {
|
2008-02-15 16:19:42 +08:00
|
|
|
memcpy(&addr->u.a6.addr, &ifa->addr, sizeof(struct in6_addr));
|
|
|
|
addr->u.a6.pfxlen = ifa->prefix_len;
|
|
|
|
addr->type = QETH_IP_TYPE_NORMAL;
|
|
|
|
} else
|
2016-06-16 22:18:58 +08:00
|
|
|
return NOTIFY_DONE;
|
2008-02-15 16:19:42 +08:00
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case NETDEV_UP:
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
qeth_l3_add_ip(card, addr);
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
2008-02-15 16:19:42 +08:00
|
|
|
break;
|
|
|
|
case NETDEV_DOWN:
|
2016-06-16 22:18:58 +08:00
|
|
|
spin_lock_bh(&card->ip_lock);
|
|
|
|
qeth_l3_delete_ip(card, addr);
|
|
|
|
spin_unlock_bh(&card->ip_lock);
|
2008-02-15 16:19:42 +08:00
|
|
|
break;
|
|
|
|
}
|
2016-06-16 22:18:58 +08:00
|
|
|
|
|
|
|
kfree(addr);
|
2008-02-15 16:19:42 +08:00
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 06:57:05 +08:00
|
|
|
QETH_DBF_TEXT(SETUP, 5, "regnotif");
|
2008-02-15 16:19:42 +08: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 06:57:05 +08:00
|
|
|
QETH_DBF_TEXT(SETUP, 5, "unregnot");
|
2012-11-19 10:46:50 +08:00
|
|
|
WARN_ON(unregister_inetaddr_notifier(&qeth_l3_ip_notifier));
|
|
|
|
WARN_ON(unregister_inet6addr_notifier(&qeth_l3_ip6_notifier));
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int __init qeth_l3_init(void)
|
|
|
|
{
|
2008-12-25 20:39:49 +08:00
|
|
|
pr_info("register layer 3 discipline\n");
|
2017-12-21 03:10:58 +08:00
|
|
|
return qeth_l3_register_notifiers();
|
2008-02-15 16:19:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit qeth_l3_exit(void)
|
|
|
|
{
|
|
|
|
qeth_l3_unregister_notifiers();
|
2008-12-25 20:39:49 +08:00
|
|
|
pr_info("unregister layer 3 discipline\n");
|
2008-02-15 16:19:42 +08: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");
|