* Re: [stable-2.6.32 PATCH] ixgbe: backport bug fix for tx panic
From: Jeff Kirsher @ 2010-05-25 7:15 UTC (permalink / raw)
To: stable, greg; +Cc: netdev, linux-kernel, davem, Brandon, Jesse Brandeburg
In-Reply-To: <20100511004655.30590.74584.stgit@localhost.localdomain>
On Mon, May 10, 2010 at 17:46, Jeff Kirsher <jeffrey.t.kirsher@intel.com> wrote:
> From: Jesse Brandeburg <jesse.brandeburg@intel.com>
>
> backporting this commit:
>
> commit fdd3d631cddad20ad9d3e1eb7dbf26825a8a121f
> Author: Krishna Kumar <krkumar2@in.ibm.com>
> Date: Wed Feb 3 13:13:10 2010 +0000
>
> ixgbe: Fix return of invalid txq
>
> a developer had complained of getting lots of warnings:
>
> "eth16 selects TX queue 98, but real number of TX queues is 64"
>
> http://www.mail-archive.com/e1000-devel@lists.sourceforge.net/msg02200.html
>
> As there was no follow up on that bug, I am submitting this
> patch assuming that the other return points will not return
> invalid txq's, and also that this fixes the bug (not tested).
>
> Signed-off-by: Krishna Kumar <krkumar2@in.ibm.com>
> Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
> Acked-by: Peter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
> Signed-off-by: David S. Miller <davem@davemloft.net>
>
> CC: Brandon <brandon@ifup.org>
> Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
> ---
>
> drivers/net/ixgbe/ixgbe_main.c | 8 ++++++--
> 1 files changed, 6 insertions(+), 2 deletions(-)
>
Greg - status? Did you queue this patch for the stable release and I missed it?
--
Cheers,
Jeff
^ permalink raw reply
* [PATCH RFC] netfilter: iptables target SYNPROXY
From: Changli Gao @ 2010-05-25 7:06 UTC (permalink / raw)
To: Patrick McHardy
Cc: David S. Miller, Alexey Kuznetsov, James Morris, netfilter-devel,
netdev, Changli Gao
iptables target SYNPROXY.
This patch implements an iptables target SYNPROXY, which works in the raw table
of the PREROUTING chain, before conntracking system. Syncookies is used, so no
new state is introduced into the conntracking system. In fact, until the first
connection is established, conntracking system doesn't see any packets. So when
there is a SYN-flood attack, conntracking system won't be busy on finding and
deleting the un-assured ct.
As the SYN-packet of the second connection request is sent locally, the DNAT
rules which are in the PREROUTING chain should be moved to the OUTPUT chain.
Signed-off-by: Changli Gao <xiaosuo@gmail.com>
----
include/net/netfilter/nf_conntrack.h | 8
include/net/netfilter/nf_conntrack_core.h | 15
include/net/netfilter/nf_conntrack_extend.h | 2
include/net/tcp.h | 14
net/ipv4/netfilter/Kconfig | 12
net/ipv4/netfilter/Makefile | 1
net/ipv4/netfilter/ipt_SYNPROXY.c | 658 ++++++++++++++++++++++++++++
net/ipv4/syncookies.c | 21
net/ipv4/tcp_ipv4.c | 5
net/netfilter/nf_conntrack_core.c | 44 +
10 files changed, 764 insertions(+), 16 deletions(-)
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index bde095f..001e6ee 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -310,5 +310,13 @@ do { \
#define MODULE_ALIAS_NFCT_HELPER(helper) \
MODULE_ALIAS("nfct-helper-" helper)
+#if defined(CONFIG_IP_NF_TARGET_SYNPROXY) || \
+ defined(CONFIG_IP_NF_TARGET_SYNPROXY_MODULE)
+extern int (*syn_proxy_pre_hook)(struct sk_buff *skb, struct nf_conn *ct,
+ enum ip_conntrack_info ctinfo);
+
+extern int (*syn_proxy_post_hook)(struct sk_buff *skb, struct nf_conn *ct,
+ enum ip_conntrack_info ctinfo);
+#endif
#endif /* __KERNEL__ */
#endif /* _NF_CONNTRACK_H */
diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h
index dffde8e..ae7d4be 100644
--- a/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -63,8 +63,21 @@ static inline int nf_conntrack_confirm(struct sk_buff *skb)
if (ct && ct != &nf_conntrack_untracked) {
if (!nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct))
ret = __nf_conntrack_confirm(skb);
- if (likely(ret == NF_ACCEPT))
+ if (likely(ret == NF_ACCEPT)) {
+#if defined(CONFIG_IP_NF_TARGET_SYNPROXY) || \
+ defined(CONFIG_IP_NF_TARGET_SYNPROXY_MODULE)
+ int (*syn_proxy)(struct sk_buff *, struct nf_conn *,
+ enum ip_conntrack_info);
+#endif
+
nf_ct_deliver_cached_events(ct);
+#if defined(CONFIG_IP_NF_TARGET_SYNPROXY) || \
+ defined(CONFIG_IP_NF_TARGET_SYNPROXY_MODULE)
+ syn_proxy = rcu_dereference(syn_proxy_post_hook);
+ if (syn_proxy)
+ ret = syn_proxy(skb, ct, skb->nfctinfo);
+#endif
+ }
}
return ret;
}
diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h
index 32d15bd..b2ae7e9 100644
--- a/include/net/netfilter/nf_conntrack_extend.h
+++ b/include/net/netfilter/nf_conntrack_extend.h
@@ -11,6 +11,7 @@ enum nf_ct_ext_id {
NF_CT_EXT_ACCT,
NF_CT_EXT_ECACHE,
NF_CT_EXT_ZONE,
+ NF_CT_EXT_SYNPROXY,
NF_CT_EXT_NUM,
};
@@ -19,6 +20,7 @@ enum nf_ct_ext_id {
#define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter
#define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache
#define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone
+#define NF_CT_EXT_SYNPROXY_TYPE struct syn_proxy_state
/* Extensions: optional stuff which isn't permanently in struct. */
struct nf_ct_ext {
diff --git a/include/net/tcp.h b/include/net/tcp.h
index a144914..b1d59c2 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -460,8 +460,18 @@ extern int tcp_disconnect(struct sock *sk, int flags);
extern __u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS];
extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
struct ip_options *opt);
-extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb,
- __u16 *mss);
+extern __u32 __cookie_v4_init_sequence(__be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport, __u32 seq,
+ __u16 *mssp);
+static inline __u32 cookie_v4_init_sequence(const struct iphdr *iph,
+ const struct tcphdr *th,
+ __u16 *mssp)
+{
+ return __cookie_v4_init_sequence(iph->saddr, iph->daddr, th->source,
+ th->dest, ntohl(th->seq), mssp);
+}
+extern int cookie_v4_check_sequence(const struct iphdr *iph,
+ const struct tcphdr *th, __u32 cookie);
extern __u32 cookie_init_timestamp(struct request_sock *req);
extern void cookie_check_timestamp(struct tcp_options_received *tcp_opt);
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index 1833bdb..4c0b5df 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -343,6 +343,18 @@ config IP_NF_RAW
If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>. If unsure, say `N'.
+config IP_NF_TARGET_SYNPROXY
+ tristate "SYNPROXY target support (EXPERIMENTAL)"
+ depends on IP_NF_RAW && EXPERIMENTAL
+ depends on NF_CONNTRACK_IPV4
+ depends on SYN_COOKIES
+ help
+ The SYNPROXY target allows a raw rule to specify that some TCP
+ connections are relayed to protect the TCP servers from the SYN-flood
+ DoS attacks.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
# security table for MAC policy
config IP_NF_SECURITY
tristate "Security table"
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index 4811159..ae6a688 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
+obj-$(CONFIG_IP_NF_TARGET_SYNPROXY) += ipt_SYNPROXY.o
# generic ARP tables
obj-$(CONFIG_IP_NF_ARPTABLES) += arp_tables.o
diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c
new file mode 100644
index 0000000..2794aac
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_SYNPROXY.c
@@ -0,0 +1,658 @@
+/* (C) 2010- Changli Gao <xiaosuo@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * It bases on ipt_REJECT.c
+ */
+#define pr_fmt(fmt) "SYNPROXY: " fmt
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <linux/unaligned/access_ok.h>
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <net/route.h>
+#include <net/dst.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_extend.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Changli Gao <xiaosuo@gmail.com>");
+MODULE_DESCRIPTION("Xtables: \"SYNPROXY\" target for IPv4");
+
+enum {
+ TCP_SEND_FLAG_NOTRACE = 0x1,
+ TCP_SEND_FLAG_SYNCOOKIE = 0x2,
+ TCP_SEND_FLAG_ACK2SYN = 0x4,
+};
+
+struct syn_proxy_state {
+ u16 seq_inited;
+ __be16 window;
+ u32 seq_diff;
+};
+
+static int get_mtu(const struct dst_entry *dst)
+{
+ int mtu;
+
+ mtu = dst_mtu(dst);
+ if (mtu)
+ return mtu;
+
+ return dst->dev ? dst->dev->mtu : 0;
+}
+
+static int get_advmss(const struct dst_entry *dst)
+{
+ int advmss;
+
+ advmss = dst_metric(dst, RTAX_ADVMSS);
+ if (advmss)
+ return advmss;
+ advmss = get_mtu(dst);
+ if (advmss)
+ return advmss - (sizeof(struct iphdr) + sizeof(struct tcphdr));
+
+ return TCP_MSS_DEFAULT;
+}
+
+static int syn_proxy_route(struct sk_buff *skb, struct net *net, u16 *pmss)
+{
+ const struct iphdr *iph = ip_hdr(skb);
+ struct rtable *rt;
+ struct flowi fl = {};
+ unsigned int type;
+ int flags = 0;
+ int err;
+ u16 mss;
+
+ type = inet_addr_type(net, iph->saddr);
+ if (type != RTN_LOCAL) {
+ type = inet_addr_type(net, iph->daddr);
+ if (type == RTN_LOCAL)
+ flags |= FLOWI_FLAG_ANYSRC;
+ }
+
+ if (type == RTN_LOCAL) {
+ fl.nl_u.ip4_u.daddr = iph->daddr;
+ fl.nl_u.ip4_u.saddr = iph->saddr;
+ fl.nl_u.ip4_u.tos = RT_TOS(iph->tos);
+ fl.flags = flags;
+ err = ip_route_output_key(net, &rt, &fl);
+ if (err)
+ goto out;
+
+ skb_dst_set(skb, &rt->u.dst);
+ } else {
+ /* non-local src, find valid iif to satisfy
+ * rp-filter when calling ip_route_input. */
+ fl.nl_u.ip4_u.daddr = iph->saddr;
+ err = ip_route_output_key(net, &rt, &fl);
+ if (err)
+ goto out;
+
+ err = ip_route_input(skb, iph->daddr, iph->saddr,
+ RT_TOS(iph->tos), rt->u.dst.dev);
+ if (err) {
+ dst_release(&rt->u.dst);
+ goto out;
+ }
+ if (pmss) {
+ mss = get_advmss(&rt->u.dst);
+ if (*pmss > mss)
+ *pmss = mss;
+ }
+ dst_release(&rt->u.dst);
+ }
+
+ err = skb_dst(skb)->error;
+ if (!err && pmss) {
+ mss = get_advmss(skb_dst(skb));
+ if (*pmss > mss)
+ *pmss = mss;
+ }
+
+out:
+ return err;
+}
+
+static int tcp_send(__be32 src, __be32 dst, __be16 sport, __be16 dport,
+ u32 seq, u32 ack_seq, __be16 window, u16 mss,
+ __be32 tcp_flags, u8 tos, struct net_device *dev, int flags,
+ struct sk_buff *oskb)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct tcphdr *th;
+ int err, len;
+
+ len = sizeof(*th);
+ if (mss)
+ len += TCPOLEN_MSS;
+
+ skb = NULL;
+ /* caller must give me a large enough oskb */
+ if (oskb) {
+ unsigned char *odata = oskb->data;
+
+ if (skb_recycle_check(oskb, 0)) {
+ oskb->data = odata;
+ skb_reset_tail_pointer(oskb);
+ skb = oskb;
+ pr_debug("recycle skb\n");
+ }
+ }
+ if (!skb) {
+ skb = alloc_skb(LL_MAX_HEADER + sizeof(*iph) + len, GFP_ATOMIC);
+ if (!skb) {
+ err = -ENOMEM;
+ goto out;
+ }
+ skb_reserve(skb, LL_MAX_HEADER);
+ }
+
+ skb_reset_network_header(skb);
+ if (!(flags & TCP_SEND_FLAG_ACK2SYN) || skb != oskb) {
+ iph = (struct iphdr *)skb_put(skb, sizeof(*iph));
+ iph->version = 4;
+ iph->ihl = sizeof(*iph) / 4;
+ iph->tos = tos;
+ /* tot_len is set in ip_local_out() */
+ iph->id = 0;
+ iph->frag_off = htons(IP_DF);
+ iph->protocol = IPPROTO_TCP;
+ iph->saddr = src;
+ iph->daddr = dst;
+ th = (struct tcphdr *)skb_put(skb, len);
+ th->source = sport;
+ th->dest = dport;
+ } else {
+ iph = (struct iphdr *)skb->data;
+ iph->id = 0;
+ iph->frag_off = htons(IP_DF);
+ skb_put(skb, iph->ihl * 4 + len);
+ th = (struct tcphdr *)(skb->data + iph->ihl * 4);
+ }
+
+ th->seq = htonl(seq);
+ th->ack_seq = htonl(ack_seq);
+ tcp_flag_word(th) = tcp_flags;
+ th->doff = len / 4;
+ th->window = window;
+ th->urg_ptr = 0;
+
+ if ((flags & TCP_SEND_FLAG_SYNCOOKIE) && mss)
+ err = syn_proxy_route(skb, dev_net(dev), &mss);
+ else
+ err = syn_proxy_route(skb, dev_net(dev), NULL);
+ if (err)
+ goto err_out;
+
+ if ((flags & TCP_SEND_FLAG_SYNCOOKIE)) {
+ if (mss) {
+ th->seq = htonl(__cookie_v4_init_sequence(dst, src,
+ dport, sport,
+ ack_seq - 1,
+ &mss));
+ } else {
+ mss = TCP_MSS_DEFAULT;
+ th->seq = htonl(__cookie_v4_init_sequence(dst, src,
+ dport, sport,
+ ack_seq - 1,
+ &mss));
+ mss = 0;
+ }
+ }
+
+ if (mss)
+ * (__force __be32 *)(th + 1) = htonl((TCPOPT_MSS << 24) |
+ (TCPOLEN_MSS << 16) |
+ mss);
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ th->check = ~tcp_v4_check(len, src, dst, 0);
+ skb->csum_start = (unsigned char *)th - skb->head;
+ skb->csum_offset = offsetof(struct tcphdr, check);
+
+ if (!(flags & TCP_SEND_FLAG_ACK2SYN) || skb != oskb)
+ iph->ttl = dst_metric(skb_dst(skb), RTAX_HOPLIMIT);
+
+ if (skb->len > get_mtu(skb_dst(skb))) {
+ if (printk_ratelimit())
+ pr_warning("%s has smaller mtu: %d\n",
+ skb_dst(skb)->dev->name,
+ get_mtu(skb_dst(skb)));
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ if ((flags & TCP_SEND_FLAG_NOTRACE)) {
+ skb->nfct = &nf_conntrack_untracked.ct_general;
+ skb->nfctinfo = IP_CT_NEW;
+ nf_conntrack_get(skb->nfct);
+ }
+
+ pr_debug("ip_local_out: %pI4n:%hu -> %pI4n:%hu (seq=%u, "
+ "ack_seq=%u mss=%hu flags=%x)\n", &src, ntohs(th->source),
+ &dst, ntohs(th->dest), ntohl(th->seq), ack_seq, mss,
+ ntohl(tcp_flags));
+
+ err = ip_local_out(skb);
+ if (err > 0)
+ err = net_xmit_errno(err);
+
+ pr_debug("ip_local_out: return with %d\n", err);
+out:
+ if (oskb && oskb != skb)
+ kfree_skb(oskb);
+
+ return err;
+
+err_out:
+ kfree_skb(skb);
+ goto out;
+}
+
+static int get_mss(u8 *data, int len)
+{
+ u8 olen;
+
+ while (len >= TCPOLEN_MSS) {
+ switch (data[0]) {
+ case TCPOPT_EOL:
+ return 0;
+ case TCPOPT_NOP:
+ data++;
+ len--;
+ break;
+ case TCPOPT_MSS:
+ if (data[1] != TCPOLEN_MSS)
+ return -EINVAL;
+ return get_unaligned_be16(data + 2);
+ default:
+ olen = data[1];
+ if (olen < 2 || olen > len)
+ return -EINVAL;
+ data += olen;
+ len -= olen;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static DEFINE_PER_CPU(struct syn_proxy_state, syn_proxy_state);
+
+/* syn_proxy_pre isn't under the protection of nf_conntrack_proto_tcp.c */
+static int syn_proxy_pre(struct sk_buff *skb, struct nf_conn *ct,
+ enum ip_conntrack_info ctinfo)
+{
+ struct syn_proxy_state *state;
+ struct iphdr *iph;
+ struct tcphdr *th, _th;
+
+ /* only support IPv4 now */
+ iph = ip_hdr(skb);
+ if (iph->version != 4)
+ return NF_ACCEPT;
+
+ th = skb_header_pointer(skb, iph->ihl * 4, sizeof(_th), &_th);
+ BUG_ON(th == NULL);
+
+ if (!ct || !nf_ct_is_confirmed(ct)) {
+ int ret;
+
+ if (!th->syn && th->ack) {
+ u16 mss;
+ struct sk_buff *rec_skb;
+
+ mss = cookie_v4_check_sequence(iph, th,
+ ntohl(th->ack_seq) - 1);
+ if (!mss)
+ return NF_ACCEPT;
+
+ pr_debug("%pI4n:%hu -> %pI4n:%hu(mss=%hu)\n",
+ &iph->saddr, ntohs(th->source),
+ &iph->daddr, ntohs(th->dest), mss);
+
+ if (skb_tailroom(skb) < TCPOLEN_MSS &&
+ skb->len < iph->ihl * 4 + sizeof(*th) + TCPOLEN_MSS)
+ rec_skb = NULL;
+ else
+ rec_skb = skb;
+
+ local_bh_disable();
+ state = &__get_cpu_var(syn_proxy_state);
+ state->seq_inited = 1;
+ state->window = th->window;
+ state->seq_diff = ntohl(th->ack_seq) - 1;
+ if (rec_skb)
+ tcp_send(iph->saddr, iph->daddr, 0, 0,
+ ntohl(th->seq) - 1, 0, th->window,
+ mss, TCP_FLAG_SYN, 0, skb->dev,
+ TCP_SEND_FLAG_ACK2SYN, rec_skb);
+ else
+ tcp_send(iph->saddr, iph->daddr, th->source,
+ th->dest, ntohl(th->seq) - 1, 0,
+ th->window, mss, TCP_FLAG_SYN,
+ iph->tos, skb->dev, 0, NULL);
+ state->seq_inited = 0;
+ local_bh_enable();
+
+ if (!rec_skb)
+ kfree_skb(skb);
+
+ return NF_STOLEN;
+ }
+
+ if (!ct || !th->syn || th->ack)
+ return NF_ACCEPT;
+
+ ret = NF_ACCEPT;
+ local_bh_disable();
+ state = &__get_cpu_var(syn_proxy_state);
+ if (state->seq_inited) {
+ struct syn_proxy_state *nstate;
+
+ nstate = nf_ct_ext_add(ct, NF_CT_EXT_SYNPROXY,
+ GFP_ATOMIC);
+ if (nstate != NULL) {
+ nstate->seq_inited = 0;
+ nstate->window = state->window;
+ nstate->seq_diff = state->seq_diff;
+ pr_debug("seq_diff: %u\n", nstate->seq_diff);
+ } else {
+ ret = NF_DROP;
+ }
+ }
+ local_bh_enable();
+
+ return ret;
+ }
+
+ state = nf_ct_ext_find(ct, NF_CT_EXT_SYNPROXY);
+ if (!state)
+ return NF_ACCEPT;
+
+ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
+ __be32 newack;
+
+ /* don't need to mangle duplicate SYN packets */
+ if (th->syn && !th->ack)
+ return NF_ACCEPT;
+ if (!skb_make_writable(skb, ip_hdrlen(skb) + sizeof(*th)))
+ return NF_DROP;
+ th = (struct tcphdr *)(skb->data + ip_hdrlen(skb));
+ newack = htonl(ntohl(th->ack_seq) - state->seq_diff);
+ inet_proto_csum_replace4(&th->check, skb, th->ack_seq, newack,
+ 0);
+ pr_debug("alter ack seq: %u -> %u\n",
+ ntohl(th->ack_seq), ntohl(newack));
+ th->ack_seq = newack;
+ } else {
+ /* Simultaneous open ? Oh, no. The connection between
+ * client and us is established. */
+ if (th->syn && !th->ack)
+ return NF_DROP;
+ }
+
+ return NF_ACCEPT;
+}
+
+static int syn_proxy_mangle_pkt(struct sk_buff *skb, struct iphdr *iph,
+ struct tcphdr *th, u32 seq_diff)
+{
+ __be32 new;
+ int olen;
+
+ if (skb->len < (iph->ihl + th->doff) * 4)
+ return NF_DROP;
+ if (!skb_make_writable(skb, (iph->ihl + th->doff) * 4))
+ return NF_DROP;
+ iph = (struct iphdr *)(skb->data);
+ th = (struct tcphdr *)(skb->data + iph->ihl * 4);
+
+ new = tcp_flag_word(th) & (~TCP_FLAG_SYN);
+ inet_proto_csum_replace4(&th->check, skb, tcp_flag_word(th), new, 0);
+ tcp_flag_word(th) = new;
+
+ new = htonl(ntohl(th->seq) + seq_diff);
+ inet_proto_csum_replace4(&th->check, skb, th->seq, new, 0);
+ pr_debug("alter seq: %u -> %u\n", ntohl(th->seq), ntohl(new));
+ th->seq = new;
+
+ olen = th->doff - sizeof(*th) / 4;
+ if (olen) {
+ __be32 *opt;
+
+ opt = (__force __be32 *)(th + 1);
+#define TCPOPT_EOL_WORD ((TCPOPT_EOL << 24) + (TCPOPT_EOL << 16) + \
+ (TCPOPT_EOL << 8) + TCPOPT_EOL)
+ inet_proto_csum_replace4(&th->check, skb, *opt, TCPOPT_EOL_WORD,
+ 0);
+ *opt = TCPOPT_EOL_WORD;
+ }
+
+ return NF_ACCEPT;
+}
+
+static int syn_proxy_post(struct sk_buff *skb, struct nf_conn *ct,
+ enum ip_conntrack_info ctinfo)
+{
+ struct syn_proxy_state *state;
+ struct iphdr *iph;
+ struct tcphdr *th;
+
+ /* untraced packets don't have NF_CT_EXT_SYNPROXY ext, as they don't
+ * enter syn_proxy_pre() */
+ state = nf_ct_ext_find(ct, NF_CT_EXT_SYNPROXY);
+ if (state == NULL)
+ return NF_ACCEPT;
+
+ iph = ip_hdr(skb);
+ if (!skb_make_writable(skb, iph->ihl * 4 + sizeof(*th)))
+ return NF_DROP;
+ th = (struct tcphdr *)(skb->data + iph->ihl * 4);
+ if (!state->seq_inited) {
+ if (th->syn) {
+ /* It must be from original direction, as the ones
+ * from the other side are dropped in function
+ * syn_proxy_pre() */
+ if (!th->ack)
+ return NF_ACCEPT;
+
+ pr_debug("SYN-ACK %pI4n:%hu -> %pI4n:%hu "
+ "(seq=%u ack_seq=%u)\n",
+ &iph->saddr, ntohs(th->source), &iph->daddr,
+ ntohs(th->dest), ntohl(th->seq),
+ ntohl(th->ack_seq));
+
+ /* SYN-ACK from reply direction with the protection
+ * of conntrack */
+ spin_lock_bh(&ct->lock);
+ if (!state->seq_inited) {
+ state->seq_inited = 1;
+ pr_debug("update seq_diff %u -> %u\n",
+ state->seq_diff,
+ state->seq_diff - ntohl(th->seq));
+ state->seq_diff -= ntohl(th->seq);
+ }
+ spin_unlock_bh(&ct->lock);
+ tcp_send(iph->daddr, iph->saddr, th->dest, th->source,
+ ntohl(th->ack_seq),
+ ntohl(th->seq) + 1 + state->seq_diff,
+ state->window, 0, TCP_FLAG_ACK, iph->tos,
+ skb->dev, 0, NULL);
+
+ return syn_proxy_mangle_pkt(skb, iph, th,
+ state->seq_diff + 1);
+ } else {
+ __be32 newseq;
+
+ if (!th->rst)
+ return NF_ACCEPT;
+ newseq = htonl(state->seq_diff + 1);
+ inet_proto_csum_replace4(&th->check, skb, th->seq,
+ newseq, 0);
+ pr_debug("alter RST seq: %u -> %u\n",
+ ntohl(th->seq), ntohl(newseq));
+ th->seq = newseq;
+
+ return NF_ACCEPT;
+ }
+ }
+
+ /* ct should be in ESTABLISHED state, but if the ack packets from
+ * us are lost. */
+ if (th->syn) {
+ if (!th->ack)
+ return NF_ACCEPT;
+
+ tcp_send(iph->daddr, iph->saddr, th->dest, th->source,
+ ntohl(th->ack_seq),
+ ntohl(th->seq) + 1 + state->seq_diff,
+ state->window, 0, TCP_FLAG_ACK, iph->tos,
+ skb->dev, 0, NULL);
+
+ return syn_proxy_mangle_pkt(skb, iph, th, state->seq_diff + 1);
+ }
+
+ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
+ __be32 newseq;
+
+ newseq = htonl(ntohl(th->seq) + state->seq_diff);
+ inet_proto_csum_replace4(&th->check, skb, th->seq, newseq, 0);
+ pr_debug("alter seq: %u -> %u\n", ntohl(th->seq),
+ ntohl(newseq));
+ th->seq = newseq;
+ }
+
+ return NF_ACCEPT;
+}
+
+static int tcp_process(struct sk_buff *skb)
+{
+ const struct iphdr *iph;
+ const struct tcphdr *th;
+ int err;
+ u16 mss;
+
+ iph = ip_hdr(skb);
+ if (iph->frag_off & htons(IP_OFFSET))
+ goto out;
+ if (!pskb_may_pull(skb, iph->ihl * 4 + sizeof(*th)))
+ goto out;
+ th = (const struct tcphdr *)(skb->data + iph->ihl * 4);
+ if (th->fin || th->rst || th->ack || !th->syn)
+ goto out;
+
+ if (nf_ip_checksum(skb, NF_INET_PRE_ROUTING, iph->ihl * 4, IPPROTO_TCP))
+ goto out;
+ mss = 0;
+ if (th->doff > sizeof(*th) / 4) {
+ if (!pskb_may_pull(skb, (iph->ihl + th->doff) * 4))
+ goto out;
+ err = get_mss((u8 *)(th + 1), th->doff * 4 - sizeof(*th));
+ if (err < 0)
+ goto out;
+ if (err != 0)
+ mss = err;
+ } else if (th->doff != sizeof(*th) / 4)
+ goto out;
+
+ tcp_send(iph->daddr, iph->saddr, th->dest, th->source, 0,
+ ntohl(th->seq) + 1, 0, mss, TCP_FLAG_SYN | TCP_FLAG_ACK,
+ iph->tos, skb->dev,
+ TCP_SEND_FLAG_NOTRACE | TCP_SEND_FLAG_SYNCOOKIE, skb);
+
+ return NF_STOLEN;
+
+out:
+ return NF_DROP;
+}
+
+static unsigned int synproxy_tg(struct sk_buff *skb,
+ const struct xt_action_param *par)
+{
+ struct nf_conn *ct;
+ enum ip_conntrack_info ctinfo;
+ int ret;
+
+ /* received from lo */
+ ct = nf_ct_get(skb, &ctinfo);
+ if (ct)
+ return IPT_CONTINUE;
+
+ local_bh_disable();
+ if (!__get_cpu_var(syn_proxy_state).seq_inited)
+ ret = tcp_process(skb);
+ else
+ ret = IPT_CONTINUE;
+ local_bh_enable();
+
+ return ret;
+}
+
+static struct xt_target synproxy_tg_reg __read_mostly = {
+ .name = "SYNPROXY",
+ .family = NFPROTO_IPV4,
+ .target = synproxy_tg,
+ .table = "raw",
+ .hooks = (1 << NF_INET_PRE_ROUTING),
+ .proto = IPPROTO_TCP,
+ .me = THIS_MODULE,
+};
+
+static struct nf_ct_ext_type syn_proxy_state_ext __read_mostly = {
+ .len = sizeof(struct syn_proxy_state),
+ .align = __alignof__(struct syn_proxy_state),
+ .id = NF_CT_EXT_SYNPROXY,
+};
+
+static int __init synproxy_tg_init(void)
+{
+ int err, cpu;
+
+ for_each_possible_cpu(cpu)
+ per_cpu(syn_proxy_state, cpu).seq_inited = 0;
+ rcu_assign_pointer(syn_proxy_pre_hook, syn_proxy_pre);
+ rcu_assign_pointer(syn_proxy_post_hook, syn_proxy_post);
+ err = nf_ct_extend_register(&syn_proxy_state_ext);
+ if (err)
+ goto err_out;
+ err = xt_register_target(&synproxy_tg_reg);
+ if (err)
+ goto err_out2;
+
+ return err;
+
+err_out2:
+ nf_ct_extend_unregister(&syn_proxy_state_ext);
+err_out:
+ rcu_assign_pointer(syn_proxy_post_hook, NULL);
+ rcu_assign_pointer(syn_proxy_pre_hook, NULL);
+ rcu_barrier();
+
+ return err;
+}
+
+static void __exit synproxy_tg_exit(void)
+{
+ xt_unregister_target(&synproxy_tg_reg);
+ nf_ct_extend_unregister(&syn_proxy_state_ext);
+ rcu_assign_pointer(syn_proxy_post_hook, NULL);
+ rcu_assign_pointer(syn_proxy_pre_hook, NULL);
+ rcu_barrier();
+}
+
+module_init(synproxy_tg_init);
+module_exit(synproxy_tg_exit);
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 5c24db4..d61d374 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -160,26 +160,22 @@ static __u16 const msstab[] = {
* Generate a syncookie. mssp points to the mss, which is returned
* rounded down to the value encoded in the cookie.
*/
-__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)
+__u32 __cookie_v4_init_sequence(__be32 saddr, __be32 daddr, __be16 sport,
+ __be16 dport, __u32 seq, __u16 *mssp)
{
- const struct iphdr *iph = ip_hdr(skb);
- const struct tcphdr *th = tcp_hdr(skb);
int mssind;
const __u16 mss = *mssp;
- tcp_synq_overflow(sk);
-
/* XXX sort msstab[] by probability? Binary search? */
for (mssind = 0; mss > msstab[mssind + 1]; mssind++)
;
*mssp = msstab[mssind] + 1;
- NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT);
- return secure_tcp_syn_cookie(iph->saddr, iph->daddr,
- th->source, th->dest, ntohl(th->seq),
+ return secure_tcp_syn_cookie(saddr, daddr, sport, dport, seq,
jiffies / (HZ * 60), mssind);
}
+EXPORT_SYMBOL(__cookie_v4_init_sequence);
/*
* This (misnamed) value is the age of syncookie which is permitted.
@@ -192,10 +188,9 @@ __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)
* Check if a ack sequence number is a valid syncookie.
* Return the decoded mss if it is, or 0 if not.
*/
-static inline int cookie_check(struct sk_buff *skb, __u32 cookie)
+int cookie_v4_check_sequence(const struct iphdr *iph, const struct tcphdr *th,
+ __u32 cookie)
{
- const struct iphdr *iph = ip_hdr(skb);
- const struct tcphdr *th = tcp_hdr(skb);
__u32 seq = ntohl(th->seq) - 1;
__u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,
th->source, th->dest, seq,
@@ -204,6 +199,7 @@ static inline int cookie_check(struct sk_buff *skb, __u32 cookie)
return mssind < NUM_MSS ? msstab[mssind] + 1 : 0;
}
+EXPORT_SYMBOL(cookie_v4_check_sequence);
static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
@@ -270,7 +266,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
goto out;
if (tcp_synq_no_recent_overflow(sk) ||
- (mss = cookie_check(skb, cookie)) == 0) {
+ (mss = cookie_v4_check_sequence(ip_hdr(skb), tcp_hdr(skb),
+ cookie)) == 0) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED);
goto out;
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 202cf09..9879c3b 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1330,8 +1330,11 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
#ifdef CONFIG_SYN_COOKIES
syn_flood_warning(skb);
req->cookie_ts = tmp_opt.tstamp_ok;
+ tcp_synq_overflow(sk);
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT);
+ isn = cookie_v4_init_sequence(ip_hdr(skb), tcp_hdr(skb),
+ &req->mss);
#endif
- isn = cookie_v4_init_sequence(sk, skb, &req->mss);
} else if (!isn) {
struct inet_peer *peer = NULL;
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index b83c530..2a2ef7b 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -847,8 +847,24 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
l3proto, l4proto, &set_reply, &ctinfo);
if (!ct) {
/* Not valid part of a connection */
+#if defined(CONFIG_IP_NF_TARGET_SYNPROXY) || \
+ defined(CONFIG_IP_NF_TARGET_SYNPROXY_MODULE)
+ int (*syn_proxy)(struct sk_buff *, struct nf_conn *,
+ enum ip_conntrack_info);
+
+ syn_proxy = rcu_dereference(syn_proxy_pre_hook);
+ if (protonum == IPPROTO_TCP && syn_proxy) {
+ ret = syn_proxy(skb, NULL, ctinfo);
+ if (ret == NF_ACCEPT)
+ NF_CT_STAT_INC_ATOMIC(net, invalid);
+ } else {
+#endif
NF_CT_STAT_INC_ATOMIC(net, invalid);
ret = NF_ACCEPT;
+#if defined(CONFIG_IP_NF_TARGET_SYNPROXY) || \
+ defined(CONFIG_IP_NF_TARGET_SYNPROXY_MODULE)
+ }
+#endif
goto out;
}
@@ -861,6 +877,20 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
NF_CT_ASSERT(skb->nfct);
+#if defined(CONFIG_IP_NF_TARGET_SYNPROXY) || \
+ defined(CONFIG_IP_NF_TARGET_SYNPROXY_MODULE)
+ {
+ int (*syn_proxy)(struct sk_buff *, struct nf_conn *,
+ enum ip_conntrack_info);
+
+ syn_proxy = rcu_dereference(syn_proxy_pre_hook);
+ if (protonum == IPPROTO_TCP && syn_proxy) {
+ ret = syn_proxy(skb, ct, ctinfo);
+ if (ret != NF_ACCEPT)
+ goto out;
+ }
+ }
+#endif
ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum);
if (ret <= 0) {
/* Invalid: inverse of the return code tells
@@ -1448,6 +1478,17 @@ s16 (*nf_ct_nat_offset)(const struct nf_conn *ct,
u32 seq);
EXPORT_SYMBOL_GPL(nf_ct_nat_offset);
+#if defined(CONFIG_IP_NF_TARGET_SYNPROXY) || \
+ defined(CONFIG_IP_NF_TARGET_SYNPROXY_MODULE)
+int (*syn_proxy_pre_hook)(struct sk_buff *skb, struct nf_conn *ct,
+ enum ip_conntrack_info ctinfo);
+EXPORT_SYMBOL(syn_proxy_pre_hook);
+
+int (*syn_proxy_post_hook)(struct sk_buff *skb, struct nf_conn *ct,
+ enum ip_conntrack_info ctinfo);
+EXPORT_SYMBOL(syn_proxy_post_hook);
+#endif
+
int nf_conntrack_init(struct net *net)
{
int ret;
@@ -1468,6 +1509,9 @@ int nf_conntrack_init(struct net *net)
/* Howto get NAT offsets */
rcu_assign_pointer(nf_ct_nat_offset, NULL);
+
+ rcu_assign_pointer(syn_proxy_pre_hook, NULL);
+ rcu_assign_pointer(syn_proxy_post_hook, NULL);
}
return 0;
^ permalink raw reply related
* Re: [PATCH] sock.h: fix kernel-doc warning
From: David Miller @ 2010-05-25 6:56 UTC (permalink / raw)
To: randy.dunlap; +Cc: netdev, akpm
In-Reply-To: <20100524222225.0fc298eb.randy.dunlap@oracle.com>
From: Randy Dunlap <randy.dunlap@oracle.com>
Date: Mon, 24 May 2010 22:22:25 -0700
> From: Randy Dunlap <randy.dunlap@oracle.com>
>
> Fix sock.h kernel-doc warning:
> Warning(include/net/sock.h:1438): No description found for parameter 'wq'
>
> Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Applied, thanks Randy.
^ permalink raw reply
* Re: linux-next: build failure after merge of the final tree
From: David Miller @ 2010-05-25 6:56 UTC (permalink / raw)
To: herbert; +Cc: sfr, netdev, linux-next, linux-kernel
In-Reply-To: <20100525051442.GA8480@gondor.apana.org.au>
From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Tue, 25 May 2010 15:14:42 +1000
> On Mon, May 24, 2010 at 09:58:51PM -0700, David Miller wrote:
>>
>> Herbert, can you take a look?
>
> Sorry, I hadn't tested the built-in case.
>
> cls_cgroup: Fix build error when built-in
>
> There is a typo in cgroup_cls_state when cls_cgroup is built-in.
>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Applied, thanks Herbert.
^ permalink raw reply
* Re: [Nios2-dev] [PATCH] ethoc: fix null dereference in ethoc_probe
From: Thomas Chou @ 2010-05-25 6:51 UTC (permalink / raw)
To: nios2-dev; +Cc: netdev, thierry.reding, linux-kernel, Dan Carpenter
In-Reply-To: <4BFB6B4B.3020107@zhaw.ch>
Tobias Klauser wrote:
>> @@ -944,6 +946,7 @@ static int ethoc_probe(struct platform_device *pdev)
>> priv = netdev_priv(netdev);
>> priv->netdev = netdev;
>> priv->dma_alloc = 0;
>> + priv->io_region_size = mmio->end - mmio->start + 1;
>
> Better use the resource_size inline here.
Hi Tobias,
I just see your patch of resource_size from the git log. Sorry, I missed
this one. I will send update on this. Thank you.
Cheers,
Thomas
^ permalink raw reply
* Re: [Nios2-dev] [PATCH] ethoc: fix null dereference in ethoc_probe
From: Tobias Klauser @ 2010-05-25 6:16 UTC (permalink / raw)
To: nios2-dev; +Cc: netdev, thierry.reding, linux-kernel, Dan Carpenter
In-Reply-To: <1274669042-1901-1-git-send-email-thomas@wytron.com.tw>
On 05/24/2010 04:44 AM, Thomas Chou wrote:
> Dan reported the patch 0baa080c75c: "ethoc: use system memory
> as buffer" introduced a potential null dereference.
>
> 1060 free:
> 1061 if (priv->dma_alloc)
> ^^^^^^^^^^^^^^^
> priv can be null here.
>
> He also suggested that the error handling is not complete.
>
> This patch fixes the null priv issue and improves resources
> releasing in ethoc_probe() and ethoc_remove().
>
> Reported-by: Dan Carpenter <error27@gmail.com>
> Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
> ---
> drivers/net/ethoc.c | 34 ++++++++++++++++++++++++++++++----
> 1 files changed, 30 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c
> index a8d9250..fddd5f5 100644
> --- a/drivers/net/ethoc.c
> +++ b/drivers/net/ethoc.c
> @@ -174,6 +174,7 @@ MODULE_PARM_DESC(buffer_size, "DMA buffer allocation size");
> * @iobase: pointer to I/O memory region
> * @membase: pointer to buffer memory region
> * @dma_alloc: dma allocated buffer size
> + * @io_region_size: I/O memory region size
> * @num_tx: number of send buffers
> * @cur_tx: last send buffer written
> * @dty_tx: last buffer actually sent
> @@ -193,6 +194,7 @@ struct ethoc {
> void __iomem *iobase;
> void __iomem *membase;
> int dma_alloc;
> + resource_size_t io_region_size;
>
> unsigned int num_tx;
> unsigned int cur_tx;
> @@ -944,6 +946,7 @@ static int ethoc_probe(struct platform_device *pdev)
> priv = netdev_priv(netdev);
> priv->netdev = netdev;
> priv->dma_alloc = 0;
> + priv->io_region_size = mmio->end - mmio->start + 1;
Better use the resource_size inline here.
> priv->iobase = devm_ioremap_nocache(&pdev->dev, netdev->base_addr,
> resource_size(mmio));
^^^
Then you could use the io_region_size here to avoid calculating the size
again.
> @@ -1049,20 +1052,34 @@ static int ethoc_probe(struct platform_device *pdev)
> ret = register_netdev(netdev);
> if (ret < 0) {
> dev_err(&netdev->dev, "failed to register interface\n");
> - goto error;
> + goto error2;
> }
>
> goto out;
>
> +error2:
> + netif_napi_del(&priv->napi);
> error:
> mdiobus_unregister(priv->mdio);
> free_mdio:
> kfree(priv->mdio->irq);
> mdiobus_free(priv->mdio);
> free:
> - if (priv->dma_alloc)
> - dma_free_coherent(NULL, priv->dma_alloc, priv->membase,
> - netdev->mem_start);
> + if (priv) {
> + if (priv->dma_alloc)
> + dma_free_coherent(NULL, priv->dma_alloc, priv->membase,
> + netdev->mem_start);
> + else if (priv->membase)
> + devm_iounmap(&pdev->dev, priv->membase);
> + if (priv->iobase)
> + devm_iounmap(&pdev->dev, priv->iobase);
> + }
> + if (mem)
> + devm_release_mem_region(&pdev->dev, mem->start,
> + mem->end - mem->start + 1);
resource_size again
> + if (mmio)
> + devm_release_mem_region(&pdev->dev, mmio->start,
> + mmio->end - mmio->start + 1);
ditto
> free_netdev(netdev);
> out:
> return ret;
> @@ -1080,6 +1097,7 @@ static int ethoc_remove(struct platform_device *pdev)
> platform_set_drvdata(pdev, NULL);
>
> if (netdev) {
> + netif_napi_del(&priv->napi);
> phy_disconnect(priv->phy);
> priv->phy = NULL;
>
> @@ -1091,6 +1109,14 @@ static int ethoc_remove(struct platform_device *pdev)
> if (priv->dma_alloc)
> dma_free_coherent(NULL, priv->dma_alloc, priv->membase,
> netdev->mem_start);
> + else {
> + devm_iounmap(&pdev->dev, priv->membase);
> + devm_release_mem_region(&pdev->dev, netdev->mem_start,
> + netdev->mem_end - netdev->mem_start + 1);
> + }
> + devm_iounmap(&pdev->dev, priv->iobase);
> + devm_release_mem_region(&pdev->dev, netdev->base_addr,
> + priv->io_region_size);
> unregister_netdev(netdev);
> free_netdev(netdev);
> }
^ permalink raw reply
* [PATCH] vhost: Fix host panic if ioctl called with wrong index
From: Krishna Kumar @ 2010-05-25 5:40 UTC (permalink / raw)
To: mst; +Cc: netdev, kvm, Krishna Kumar
From: Krishna Kumar <krkumar2@in.ibm.com>
Missed a boundary value check in vhost_set_vring. The host panics if
idx == nvqs is used in ioctl commands in vhost_virtqueue_init.
Signed-off-by: Krishna Kumar <krkumar2@in.ibm.com>
---
drivers/vhost/vhost.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff -ruNp org/drivers/vhost/vhost.c new/drivers/vhost/vhost.c
--- org/drivers/vhost/vhost.c 2010-05-24 09:25:57.000000000 +0530
+++ new/drivers/vhost/vhost.c 2010-05-24 09:26:53.000000000 +0530
@@ -374,7 +374,7 @@ static long vhost_set_vring(struct vhost
r = get_user(idx, idxp);
if (r < 0)
return r;
- if (idx > d->nvqs)
+ if (idx >= d->nvqs)
return -ENOBUFS;
vq = d->vqs + idx;
^ permalink raw reply
* [PATCH] sock.h: fix kernel-doc warning
From: Randy Dunlap @ 2010-05-25 5:22 UTC (permalink / raw)
To: netdev, akpm; +Cc: davem
From: Randy Dunlap <randy.dunlap@oracle.com>
Fix sock.h kernel-doc warning:
Warning(include/net/sock.h:1438): No description found for parameter 'wq'
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
---
include/net/sock.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- lnx-2634-g8-kerndoc.orig/include/net/sock.h
+++ lnx-2634-g8-kerndoc/include/net/sock.h
@@ -1404,7 +1404,7 @@ static inline int sk_has_allocations(con
/**
* wq_has_sleeper - check if there are any waiting processes
- * @sk: struct socket_wq
+ * @wq: struct socket_wq
*
* Returns true if socket_wq has waiting processes
*
^ permalink raw reply
* Re: linux-next: build failure after merge of the final tree
From: Herbert Xu @ 2010-05-25 5:14 UTC (permalink / raw)
To: David Miller; +Cc: sfr, netdev, linux-next, linux-kernel
In-Reply-To: <20100524.215851.207470612.davem@davemloft.net>
On Mon, May 24, 2010 at 09:58:51PM -0700, David Miller wrote:
>
> Herbert, can you take a look?
Sorry, I hadn't tested the built-in case.
cls_cgroup: Fix build error when built-in
There is a typo in cgroup_cls_state when cls_cgroup is built-in.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
diff --git a/include/net/cls_cgroup.h b/include/net/cls_cgroup.h
index f9a0b01..4e75a1f 100644
--- a/include/net/cls_cgroup.h
+++ b/include/net/cls_cgroup.h
@@ -31,7 +31,7 @@ static inline u32 task_cls_classid(struct task_struct *p)
return 0;
return container_of(task_subsys_state(p, net_cls_subsys_id),
- struct cgroup_cls_state, css).classid;
+ struct cgroup_cls_state, css)->classid;
}
#else
extern int net_cls_subsys_id;
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply related
* Re: linux-next: build failure after merge of the final tree
From: David Miller @ 2010-05-25 4:58 UTC (permalink / raw)
To: sfr; +Cc: netdev, linux-next, linux-kernel, herbert
In-Reply-To: <20100525141046.975afe1f.sfr@canb.auug.org.au>
From: Stephen Rothwell <sfr@canb.auug.org.au>
Date: Tue, 25 May 2010 14:10:46 +1000
> After merging the final tree, today's linux-next build (powerpc
> allyesconfig) failed like this:
>
> In file included from net/socket.c:97:
> include/net/cls_cgroup.h: In function 'task_cls_classid':
> include/net/cls_cgroup.h:33: error: request for member 'classid' in something not a structure or union
> In file included from net/core/sock.c:126:
> include/net/cls_cgroup.h: In function 'task_cls_classid':
> include/net/cls_cgroup.h:33: error: request for member 'classid' in something not a structure or union
> In file included from net/sched/cls_cgroup.c:23:
> include/net/cls_cgroup.h: In function 'task_cls_classid':
> include/net/cls_cgroup.h:33: error: request for member 'classid' in something not a structure or union
>
> CONFIG_CGROUPS=y, CONFIG_NET_CLS_CGROUP=y
>
> Caused by commit f845172531fb7410c7fb7780b1a6e51ee6df7d52 ("cls_cgroup:
> Store classid in struct sock") from the net-current tree.
>
> I have reverted that commit for today (and commit
> 8286274284e15b11b0f531b6ceeef21fbe00a8dd "tun: Update classid on packet
> injection" that depends on it).
Yuck, and we already went through two iterations of this patch fixing exactly
these kinds of build problems :-/
Herbert, can you take a look?
Thanks!
^ permalink raw reply
* Re: linux-next: build warning in Linus' tree
From: David Miller @ 2010-05-25 4:58 UTC (permalink / raw)
To: sfr; +Cc: netdev, linux-next, linux-kernel, NeilJay
In-Reply-To: <20100525114614.fd61d822.sfr@canb.auug.org.au>
From: Stephen Rothwell <sfr@canb.auug.org.au>
Date: Tue, 25 May 2010 11:46:14 +1000
> Hi Dave,
>
> Today's linux-next build (x86_64 allmodconfig) produced this warning:
>
> drivers/net/usb/asix.c: In function 'asix_rx_fixup':
> drivers/net/usb/asix.c:325: warning: cast from pointer to integer of different size
> drivers/net/usb/asix.c:354: warning: cast from pointer to integer of different size
>
> Introduced by commit 3f78d1f210ff89af77f042ab7f4a8fee39feb1c9
> ("drivers/net/usb/asix.c: Fix unaligned accesses"). This commit casts
> skb->data to u32.
Thanks I'll look into this.
^ permalink raw reply
* linux-next: build failure after merge of the final tree
From: Stephen Rothwell @ 2010-05-25 4:10 UTC (permalink / raw)
To: David Miller, netdev; +Cc: linux-next, linux-kernel, Herbert Xu
[-- Attachment #1: Type: text/plain, Size: 1180 bytes --]
Hi Dave,
After merging the final tree, today's linux-next build (powerpc
allyesconfig) failed like this:
In file included from net/socket.c:97:
include/net/cls_cgroup.h: In function 'task_cls_classid':
include/net/cls_cgroup.h:33: error: request for member 'classid' in something not a structure or union
In file included from net/core/sock.c:126:
include/net/cls_cgroup.h: In function 'task_cls_classid':
include/net/cls_cgroup.h:33: error: request for member 'classid' in something not a structure or union
In file included from net/sched/cls_cgroup.c:23:
include/net/cls_cgroup.h: In function 'task_cls_classid':
include/net/cls_cgroup.h:33: error: request for member 'classid' in something not a structure or union
CONFIG_CGROUPS=y, CONFIG_NET_CLS_CGROUP=y
Caused by commit f845172531fb7410c7fb7780b1a6e51ee6df7d52 ("cls_cgroup:
Store classid in struct sock") from the net-current tree.
I have reverted that commit for today (and commit
8286274284e15b11b0f531b6ceeef21fbe00a8dd "tun: Update classid on packet
injection" that depends on it).
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH] ipv4: Allow configuring subnets as local addresses
From: Stephen Hemminger @ 2010-05-25 3:29 UTC (permalink / raw)
To: Tom Herbert; +Cc: davem, netdev
In-Reply-To: <alpine.DEB.1.00.1005232249380.18495@pokey.mtv.corp.google.com>
On Sun, 23 May 2010 22:54:12 -0700 (PDT)
Tom Herbert <therbert@google.com> wrote:
> This patch allows a host to be configured to respond to any address in
> a specified range as if it were local, without actually needing to
> configure the address on an interface. This is done through routing
> table configuration. For instance, to configure a host to respond
> to any address in 10.1/16 received on eth0 as a local address we can do:
>
> ip rule add from all iif eth0 lookup 200
> ip route add local 10.1/16 dev lo proto kernel scope host src 127.0.0.1 table 200
>
> This host is now reachable by any 10.1/16 address (route lookup on
> input for packets received on eth0 can find the route). On output, the
> rule will not be matched so that this host can still send packets to
> 10.1/16 (not sent on loopback). Presumably, external routing can be
> configured to make sense out of this.
>
> To make this work, we needed to modify the logic in finding the
> interface which is assigned a given source address for output
> (dev_ip_find). We perform a normal fib_lookup instead of just a
> lookup on the local table, and in the lookup we ignore the input
> interface for matching.
>
> This patch is useful to implement IP-anycast for subnets of virtual
> addresses.
>
> Signed-off-by: Tom Herbert <therbert@google.com>
It makes sense, no sure what else will break because of this.
This won't work so well with routing daemons like Quagga(BGP, Zebra)
etc because they believe loopback is special but they don't
handle multiple routing tables well anyway.
^ permalink raw reply
* [PATCH 2/2] rtl8192su: Fix procfs code for interfaces not named wlan0
From: Ben Hutchings @ 2010-05-25 3:25 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: devel, netdev
In-Reply-To: <1274757630.7908.12.camel@localhost>
The current code creates directories in procfs named after interfaces,
but doesn't handle renaming. This can result in name collisions and
consequent WARNINGs. It also means that the interface name cannot
reliably be used to remove the directory - in fact the current code
doesn't even try, and always uses "wlan0"!
Since the name of a proc_dir_entry is embedded in it, use that when
removing it.
Add a netdev notifier to catch interface renaming, and remove and
re-add the directory at this point.
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
---
This is compile-tested only.
All this procfs crap should really be replaced - stats should be exposed
through the proper wireless stats interface and registers through
debugfs or not at all. But for now let's patch it up to avoid the
WARNING.
Ben.
drivers/staging/rtl8192su/r8192U_core.c | 35 ++++++++++++++++++++++++++++--
1 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/rtl8192su/r8192U_core.c b/drivers/staging/rtl8192su/r8192U_core.c
index 90f2df4..4bd3710 100644
--- a/drivers/staging/rtl8192su/r8192U_core.c
+++ b/drivers/staging/rtl8192su/r8192U_core.c
@@ -27,6 +27,7 @@
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/eeprom_93cx6.h>
+#include <linux/notifier.h>
#undef LOOP_TEST
#undef DUMP_RX
@@ -161,6 +162,8 @@ MODULE_PARM_DESC(channels," Channel bitmask for specific locales. NYI");
static int __devinit rtl8192_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id);
static void __devexit rtl8192_usb_disconnect(struct usb_interface *intf);
+static const struct net_device_ops rtl8192_netdev_ops;
+static struct notifier_block proc_netdev_notifier;
static struct usb_driver rtl8192_usb_driver = {
.name = RTL819xU_MODULE_NAME, /* Driver name */
@@ -992,14 +995,22 @@ static int proc_get_stats_rx(char *page, char **start,
int rtl8192_proc_module_init(void)
{
+ int ret;
+
RT_TRACE(COMP_INIT, "Initializing proc filesystem");
rtl8192_proc=create_proc_entry(RTL819xU_MODULE_NAME, S_IFDIR, init_net.proc_net);
- return rtl8192_proc ? 0 : -ENOMEM;
+ if (!rtl8192_proc)
+ return -ENOMEM;
+ ret = register_netdevice_notifier(&proc_netdev_notifier);
+ if (ret)
+ remove_proc_entry(RTL819xU_MODULE_NAME, init_net.proc_net);
+ return ret;
}
void rtl8192_proc_module_remove(void)
{
+ unregister_netdevice_notifier(&proc_netdev_notifier);
remove_proc_entry(RTL819xU_MODULE_NAME, init_net.proc_net);
}
@@ -1027,8 +1038,7 @@ void rtl8192_proc_remove_one(struct net_device *dev)
remove_proc_entry("registers-e", priv->dir_dev);
// remove_proc_entry("cck-registers",priv->dir_dev);
// remove_proc_entry("ofdm-registers",priv->dir_dev);
- //remove_proc_entry(dev->name, rtl8192_proc);
- remove_proc_entry("wlan0", rtl8192_proc);
+ remove_proc_entry(priv->dir_dev->name, rtl8192_proc);
priv->dir_dev = NULL;
}
}
@@ -1145,6 +1155,25 @@ void rtl8192_proc_init_one(struct net_device *dev)
dev->name);
}
}
+
+static int proc_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *net_dev = ptr;
+
+ if (net_dev->netdev_ops == &rtl8192_netdev_ops &&
+ event == NETDEV_CHANGENAME) {
+ rtl8192_proc_remove_one(net_dev);
+ rtl8192_proc_init_one(net_dev);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block proc_netdev_notifier = {
+ .notifier_call = proc_netdev_event,
+};
+
/****************************************************************************
-----------------------------MISC STUFF-------------------------
*****************************************************************************/
--
1.7.1
^ permalink raw reply related
* Re: [PATCH] ipv4: Allow configuring subnets as local addresses
From: Mark Smith @ 2010-05-24 21:43 UTC (permalink / raw)
To: Tom Herbert; +Cc: davem, netdev
In-Reply-To: <alpine.DEB.1.00.1005232249380.18495@pokey.mtv.corp.google.com>
Hi Tom,
On Sun, 23 May 2010 22:54:12 -0700 (PDT)
Tom Herbert <therbert@google.com> wrote:
> This patch allows a host to be configured to respond to any address in
> a specified range as if it were local, without actually needing to
> configure the address on an interface. This is done through routing
> table configuration. For instance, to configure a host to respond
> to any address in 10.1/16 received on eth0 as a local address we can do:
>
> ip rule add from all iif eth0 lookup 200
> ip route add local 10.1/16 dev lo proto kernel scope host src 127.0.0.1 table 200
>
> This host is now reachable by any 10.1/16 address (route lookup on
> input for packets received on eth0 can find the route). On output, the
> rule will not be matched so that this host can still send packets to
> 10.1/16 (not sent on loopback). Presumably, external routing can be
> configured to make sense out of this.
>
I'd be careful about making that assumption. IIRC, a very popular
router vendor's IP implementation treats 'connected' routes fairly
specially, and won't let you create a static route that covers a subset
of connected addresses with a next hop. IOW, connected routes,
regardless of their prefix length, win over the longest match rule, and
can't have their preference lowered. I think I got around it by
assigning a /32 to the interface, and having a static route for the
rest of the local address space pointing out the interface. It was
mostly a bit of an experiment, and probably quite unobvious to most
people if they saw it in production. I probably wouldn't want to do it
that way for that reason.
The more traditional way to have a host support multiple addresses is
to have a static route towards it for address space that is different
to what is assigned to the link between the host and the router. e.g.
[router].1-- 172.16.0/24--.2[host][10.1/16], possibly with 10.1/16
addresses assigned to (the|a) loopback or dummy interface. Would your
code work in this scenario?
Regards,
Mark.
> To make this work, we needed to modify the logic in finding the
> interface which is assigned a given source address for output
> (dev_ip_find). We perform a normal fib_lookup instead of just a
> lookup on the local table, and in the lookup we ignore the input
> interface for matching.
>
> This patch is useful to implement IP-anycast for subnets of virtual
> addresses.
>
> Signed-off-by: Tom Herbert <therbert@google.com>
> ---
> diff --git a/include/net/flow.h b/include/net/flow.h
> index bb08692..0ac3fb5 100644
> --- a/include/net/flow.h
> +++ b/include/net/flow.h
> @@ -49,6 +49,7 @@ struct flowi {
> __u8 proto;
> __u8 flags;
> #define FLOWI_FLAG_ANYSRC 0x01
> +#define FLOWI_FLAG_MATCH_ANY_IIF 0x02
> union {
> struct {
> __be16 sport;
> diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
> index 42e84e0..f6e18b2 100644
> --- a/net/core/fib_rules.c
> +++ b/net/core/fib_rules.c
> @@ -182,7 +182,8 @@ static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
> {
> int ret = 0;
>
> - if (rule->iifindex && (rule->iifindex != fl->iif))
> + if (rule->iifindex && (rule->iifindex != fl->iif) &&
> + !(fl->flags & FLOWI_FLAG_MATCH_ANY_IIF))
> goto out;
>
> if (rule->oifindex && (rule->oifindex != fl->oif))
> diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
> index 4f0ed45..64f953e 100644
> --- a/net/ipv4/fib_frontend.c
> +++ b/net/ipv4/fib_frontend.c
> @@ -153,17 +153,16 @@ static void fib_flush(struct net *net)
>
> struct net_device * ip_dev_find(struct net *net, __be32 addr)
> {
> - struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
> + struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } },
> + .flags = FLOWI_FLAG_MATCH_ANY_IIF };
> struct fib_result res;
> struct net_device *dev = NULL;
> - struct fib_table *local_table;
>
> #ifdef CONFIG_IP_MULTIPLE_TABLES
> res.r = NULL;
> #endif
>
> - local_table = fib_get_table(net, RT_TABLE_LOCAL);
> - if (!local_table || fib_table_lookup(local_table, &fl, &res))
> + if (fib_lookup(net, &fl, &res))
> return NULL;
> if (res.type != RTN_LOCAL)
> goto out;
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* NULL Pointer Deference: NFS & Telnet
From: Arce, Abraham @ 2010-05-25 2:19 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-nfs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Hi,
I have 2 scenarios in which I am getting a NULL pointer dereference:
1) root filesystem over nfs
2) telnet connection
The issue appeared on this commit
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=f8965467f366fd18f01feafb5db10512d7b4422c
The driver I am working with is drivers/net/ks8851.c
Any help will be highly appreciated...
---
Scenario 1 | root filesystem over nfs
Looking up port of RPC 100005/1 on 10.87.231.229
VFS: Mounted root (nfs filesystem) on device 0:10.
Freeing init memory: 128K
Unable to handle kernel NULL pointer dereference at virtual address 00000000
[..]
PC is at put_page+0xc/0x120
LR is at skb_release_data+0x74/0xb8
[..]
Backtrace:
[<c0086dd0>] (put_page+0x0/0x120)
[<c01d0a48>] (skb_release_data+0x0/0xb8)
[<c01d1044>] (skb_release_all+0x0/0x20)
[<c01d075c>] (__kfree_skb+0x0/0xbc)
[<c01d0818>] (consume_skb+0x0/0x58)
[<c01d39cc>] (skb_free_datagram+0x0/0x40)
[<c023ff74>] (xs_udp_data_ready+0x0/0x1e8)
[<c01ce034>] (sock_queue_rcv_skb+0x0/0x1c0)
[<c01fbba8>] (ip_queue_rcv_skb+0x0/0x58)
[<c02176c0>] (__udp_queue_rcv_skb+0x0/0x18c)
[<c0218e28>] (udp_queue_rcv_skb+0x0/0x348)
[<c02195a4>] (__udp4_lib_rcv+0x0/0x564)
[<c0219b08>] (udp_rcv+0x0/0x20)
[<c01f5f34>] (ip_local_deliver+0x0/0x264)
[<c01f586c>] (ip_rcv+0x0/0x6c8)
[<c01d7ec0>] (__netif_receive_skb+0x0/0x2d0)
[<c01d8190>] (process_backlog+0x0/0x16c)
[<c01d8e14>] (net_rx_action+0x0/0x18c)
[<c00521a0>] (__do_softirq+0x0/0x12c)
[<c00522cc>] (irq_exit+0x0/0x70)
[<c0028000>] (asm_do_IRQ+0x0/0xc8)
Complete log at http://pastebin.mozilla.org/728027
---
Scenario 2
1. Root filesystem booted in ram
2. eth0 brought up
3. telnetd daemon started
4. tried to connect through telnet
# Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = d98e8000
[..]
PC is at put_page+0xc/0x120
LR is at skb_release_data+0x74/0xb8
[..]
Backtrace:
[<c0086dd0>] (put_page+0x0/0x120)
[<c01d0a48>] (skb_release_data+0x0/0xb8)
[<c01d1044>] (skb_release_all+0x0/0x20)
[<c01d075c>] (__kfree_skb+0x0/0xbc)
[<c0202444>] (tcp_recvmsg+0x0/0x93c)
[<c02201e8>] (inet_recvmsg+0x0/0xec)
[<c01c7fd0>] (sock_aio_read+0x0/0xf8)
[<c00ab3ac>] (do_sync_read+0x0/0xec)
[<c00abfbc>] (vfs_read+0x0/0x164)
[<c00ac1a0>] (sys_read+0x0/0x70)
[<c0029100>] (ret_fast_syscall+0x0/0x30)
Complete log at http://pastebin.mozilla.org/728028
Best Regards
Abraham Arce
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* linux-next: build warning in Linus' tree
From: Stephen Rothwell @ 2010-05-25 1:46 UTC (permalink / raw)
To: David Miller, netdev; +Cc: linux-next, linux-kernel, Neil Jones
[-- Attachment #1: Type: text/plain, Size: 574 bytes --]
Hi Dave,
Today's linux-next build (x86_64 allmodconfig) produced this warning:
drivers/net/usb/asix.c: In function 'asix_rx_fixup':
drivers/net/usb/asix.c:325: warning: cast from pointer to integer of different size
drivers/net/usb/asix.c:354: warning: cast from pointer to integer of different size
Introduced by commit 3f78d1f210ff89af77f042ab7f4a8fee39feb1c9
("drivers/net/usb/asix.c: Fix unaligned accesses"). This commit casts
skb->data to u32.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH net-2.6] macvlan: do a proper cleanup in macvlan_common_newlink()
From: David Miller @ 2010-05-25 1:41 UTC (permalink / raw)
To: kaber; +Cc: jpirko, netdev
In-Reply-To: <4BFACBE0.4020109@trash.net>
From: Patrick McHardy <kaber@trash.net>
Date: Mon, 24 May 2010 20:56:32 +0200
> Jiri Pirko wrote:
>> Mon, May 24, 2010 at 05:58:58PM CEST, jpirko@
>> Subject: [PATCH net-2.6] macvlan: do proper cleanup in macvlan_common_newlink() V2
>>
>> Fixes possible memory leak.
>
> Looks good, thanks.
>
>> Signed-off-by: Jiri Pirko <jpirko@redhat.com>
>
> Acked-by: Patrick McHardy <kaber@trash.net>
Applied, thanks.
^ permalink raw reply
* Re: [PATCH net-2.6] be2net: Bug fix in init code in probe
From: David Miller @ 2010-05-25 1:39 UTC (permalink / raw)
To: sarveshwarb; +Cc: netdev
In-Reply-To: <20100524112033.GA3666@serverengines.com>
From: Sarveshwar Bandi <sarveshwarb@serverengines.com>
Date: Mon, 24 May 2010 16:50:47 +0530
> PCI function reset needs to invoked after fw init ioctl is issued.
>
> Signed-off-by: Sarveshwar Bandi <sarveshwarb@serverengines.com>
Applied, thanks.
^ permalink raw reply
* Re: net/dccp: expansion of error code size
From: David Miller @ 2010-05-25 1:37 UTC (permalink / raw)
To: yuasa; +Cc: linux-mips, netdev
In-Reply-To: <20100524154508.10a40589.yuasa@linux-mips.org>
From: Yoichi Yuasa <yuasa@linux-mips.org>
Date: Mon, 24 May 2010 15:45:08 +0900
> Because MIPS's EDQUOT value is 1133(0x46d).
> It's larger than u8.
>
> Signed-off-by: Yoichi Yuasa <yuasa@linux-mips.org>
Applied, thank you.
^ permalink raw reply
* Re: [PATCH] ipvs: Add missing locking during connection table hashing and unhashing
From: Simon Horman @ 2010-05-24 23:56 UTC (permalink / raw)
To: Sven Wegener; +Cc: Julian Anastasov, Wensong Zhang, netdev, lvs-devel
In-Reply-To: <alpine.LNX.2.00.1005202243480.22717@titan.stealer.net>
On Thu, May 20, 2010 at 10:55:32PM +0200, Sven Wegener wrote:
> The code that hashes and unhashes connections from the connection table
> is missing locking of the connection being modified, which opens up a
> race condition and results in memory corruption when this race condition
> is hit.
>
> Here is what happens in pretty verbose form:
>
> CPU 0 CPU 1
> ------------ ------------
> An active connection is terminated and
> we schedule ip_vs_conn_expire() on this
> CPU to expire this connection.
>
> IRQ assignment is changed to this CPU,
> but the expire timer stays scheduled on
> the other CPU.
>
> New connection from same ip:port comes
> in right before the timer expires, we
> find the inactive connection in our
> connection table and get a reference to
> it. We proper lock the connection in
> tcp_state_transition() and read the
> connection flags in set_tcp_state().
>
> ip_vs_conn_expire() gets called, we
> unhash the connection from our
> connection table and remove the hashed
> flag in ip_vs_conn_unhash(), without
> proper locking!
>
> While still holding proper locks we
> write the connection flags in
> set_tcp_state() and this sets the hashed
> flag again.
>
> ip_vs_conn_expire() fails to expire the
> connection, because the other CPU has
> incremented the reference count. We try
> to re-insert the connection into our
> connection table, but this fails in
> ip_vs_conn_hash(), because the hashed
> flag has been set by the other CPU. We
> re-schedule execution of
> ip_vs_conn_expire(). Now this connection
> has the hashed flag set, but isn't
> actually hashed in our connection table
> and has a dangling list_head.
>
> We drop the reference we held on the
> connection and schedule the expire timer
> for timeouting the connection on this
> CPU. Further packets won't be able to
> find this connection in our connection
> table.
>
> ip_vs_conn_expire() gets called again,
> we think it's already hashed, but the
> list_head is dangling and while removing
> the connection from our connection table
> we write to the memory location where
> this list_head points to.
>
> The result will probably be a kernel oops at some other point in time.
Nice analysis.
> Signed-off-by: Sven Wegener <sven.wegener@stealer.net>
> Cc: stable@kernel.org
Acked-by: Simon Horman <horms@verge.net.au>
> ---
> net/netfilter/ipvs/ip_vs_conn.c | 4 ++++
> 1 files changed, 4 insertions(+), 0 deletions(-)
>
> This race condition is pretty subtle, but it can be triggered remotely.
> It needs the IRQ assignment change or another circumstance where packets
> coming from the same ip:port for the same service are being processed on
> different CPUs. And it involves hitting the exact time at which
> ip_vs_conn_expire() gets called. It can be avoided by making sure that
> all packets from one connection are always processed on the same CPU and
> can be made harder to exploit by changing the connection timeouts to
> some custom values.
>
> diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
> index d8f7e8e..ff04e9e 100644
> --- a/net/netfilter/ipvs/ip_vs_conn.c
> +++ b/net/netfilter/ipvs/ip_vs_conn.c
> @@ -162,6 +162,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
> hash = ip_vs_conn_hashkey(cp->af, cp->protocol, &cp->caddr, cp->cport);
>
> ct_write_lock(hash);
> + spin_lock(&cp->lock);
>
> if (!(cp->flags & IP_VS_CONN_F_HASHED)) {
> list_add(&cp->c_list, &ip_vs_conn_tab[hash]);
> @@ -174,6 +175,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
> ret = 0;
> }
>
> + spin_unlock(&cp->lock);
> ct_write_unlock(hash);
>
> return ret;
> @@ -193,6 +195,7 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
> hash = ip_vs_conn_hashkey(cp->af, cp->protocol, &cp->caddr, cp->cport);
>
> ct_write_lock(hash);
> + spin_lock(&cp->lock);
>
> if (cp->flags & IP_VS_CONN_F_HASHED) {
> list_del(&cp->c_list);
> @@ -202,6 +205,7 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
> } else
> ret = 0;
>
> + spin_unlock(&cp->lock);
> ct_write_unlock(hash);
>
> return ret;
^ permalink raw reply
* [PATCH 2/2] qlcnic: NIC Partitioning - non privileged mode
From: Anirban Chakraborty @ 2010-05-24 23:48 UTC (permalink / raw)
To: David Miller, netdev@vger.kernel.org; +Cc: Ameen Rahman, Amit Salecha
Added support for non privileged mode of npar functions.
Bumped up version number to 5.0.3.
Please apply.
thanks,
Anirban
Signed-off-by: Anirban Chakraborty <anirban.chakraborty@qlogic.com>
---
drivers/net/qlcnic/qlcnic.h | 13 +---
drivers/net/qlcnic/qlcnic_hdr.h | 14 +++--
drivers/net/qlcnic/qlcnic_main.c | 124 +++++++++++++++++++++++++++++++++++---
3 files changed, 128 insertions(+), 23 deletions(-)
diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/qlcnic/qlcnic.h
index 31a0b43..02db363 100644
--- a/drivers/net/qlcnic/qlcnic.h
+++ b/drivers/net/qlcnic/qlcnic.h
@@ -51,8 +51,8 @@
#define _QLCNIC_LINUX_MAJOR 5
#define _QLCNIC_LINUX_MINOR 0
-#define _QLCNIC_LINUX_SUBVERSION 2
-#define QLCNIC_LINUX_VERSIONID "5.0.2"
+#define _QLCNIC_LINUX_SUBVERSION 3
+#define QLCNIC_LINUX_VERSIONID "5.0.3"
#define QLCNIC_DRV_IDC_VER 0x01
#define QLCNIC_VERSION_CODE(a, b, c) (((a) << 24) + ((b) << 16) + (c))
@@ -891,6 +891,7 @@ struct qlcnic_mac_req {
#define QLCNIC_LRO_ENABLED 0x08
#define QLCNIC_BRIDGE_ENABLED 0X10
#define QLCNIC_DIAG_ENABLED 0x20
+#define QLCNIC_NPAR_ENABLED 0x40
#define QLCNIC_IS_MSI_FAMILY(adapter) \
((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED))
@@ -1159,13 +1160,6 @@ int qlcnic_check_loopback_buff(unsigned char *data);
netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
void qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring);
-/* Functions from qlcnic_vf.c */
-int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *, u32);
-int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32);
-int qlcnicvf_set_ilb_mode(struct qlcnic_adapter *adapter);
-void qlcnicvf_clear_ilb_mode(struct qlcnic_adapter *adapter);
-void qlcnicvf_set_port_mode(struct qlcnic_adapter *adapter);
-
/* Management functions */
int qlcnic_set_mac_address(struct qlcnic_adapter *, u8*);
int qlcnic_get_mac_address(struct qlcnic_adapter *, u8*);
@@ -1234,6 +1228,7 @@ struct qlcnic_nic_template {
int (*config_led) (struct qlcnic_adapter *, u32, u32);
int (*set_ilb_mode) (struct qlcnic_adapter *);
void (*clear_ilb_mode) (struct qlcnic_adapter *);
+ int (*start_firmware) (struct qlcnic_adapter *);
};
#define QLCDB(adapter, lvl, _fmt, _args...) do { \
diff --git a/drivers/net/qlcnic/qlcnic_hdr.h b/drivers/net/qlcnic/qlcnic_hdr.h
index 1bcfb12..7b81cab 100644
--- a/drivers/net/qlcnic/qlcnic_hdr.h
+++ b/drivers/net/qlcnic/qlcnic_hdr.h
@@ -701,10 +701,11 @@ enum {
#define QLCNIC_CRB_DEV_REF_COUNT (QLCNIC_CAM_RAM(0x138))
#define QLCNIC_CRB_DEV_STATE (QLCNIC_CAM_RAM(0x140))
-#define QLCNIC_CRB_DRV_STATE (QLCNIC_CAM_RAM(0x144))
-#define QLCNIC_CRB_DRV_SCRATCH (QLCNIC_CAM_RAM(0x148))
-#define QLCNIC_CRB_DEV_PARTITION_INFO (QLCNIC_CAM_RAM(0x14c))
+#define QLCNIC_CRB_DRV_STATE (QLCNIC_CAM_RAM(0x144))
+#define QLCNIC_CRB_DRV_SCRATCH (QLCNIC_CAM_RAM(0x148))
+#define QLCNIC_CRB_DEV_PARTITION_INFO (QLCNIC_CAM_RAM(0x14c))
#define QLCNIC_CRB_DRV_IDC_VER (QLCNIC_CAM_RAM(0x174))
+#define QLCNIC_CRB_DEV_NPAR_STATE (QLCNIC_CAM_RAM(0x19c))
#define QLCNIC_ROM_DEV_INIT_TIMEOUT (0x3e885c)
#define QLCNIC_ROM_DRV_RESET_TIMEOUT (0x3e8860)
@@ -717,6 +718,9 @@ enum {
#define QLCNIC_DEV_FAILED 0x6
#define QLCNIC_DEV_QUISCENT 0x7
+#define QLCNIC_DEV_NPAR_NOT_RDY 0
+#define QLCNIC_DEV_NPAR_RDY 1
+
#define QLC_DEV_CHECK_ACTIVE(VAL, FN) ((VAL) &= (1 << (FN * 4)))
#define QLC_DEV_SET_REF_CNT(VAL, FN) ((VAL) |= (1 << (FN * 4)))
#define QLC_DEV_CLR_REF_CNT(VAL, FN) ((VAL) &= ~(1 << (FN * 4)))
@@ -732,8 +736,8 @@ enum {
#define QLCNIC_TYPE_ISCSI 3
#define QLCNIC_RCODE_DRIVER_INFO 0x20000000
-#define QLCNIC_RCODE_DRIVER_CAN_RELOAD 0x40000000
-#define QLCNIC_RCODE_FATAL_ERROR 0x80000000
+#define QLCNIC_RCODE_DRIVER_CAN_RELOAD BIT_30
+#define QLCNIC_RCODE_FATAL_ERROR BIT_31
#define QLCNIC_FWERROR_PEGNUM(code) ((code) & 0xff)
#define QLCNIC_FWERROR_CODE(code) ((code >> 8) & 0xfffff)
diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c
index 3b4fdb3..9a1b60f 100644
--- a/drivers/net/qlcnic/qlcnic_main.c
+++ b/drivers/net/qlcnic/qlcnic_main.c
@@ -103,7 +103,14 @@ static irqreturn_t qlcnic_msix_intr(int irq, void *data);
static struct net_device_stats *qlcnic_get_stats(struct net_device *netdev);
static void qlcnic_config_indev_addr(struct net_device *dev, unsigned long);
-
+static int qlcnic_start_firmware(struct qlcnic_adapter *);
+
+static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *);
+static void qlcnicvf_clear_ilb_mode(struct qlcnic_adapter *);
+static int qlcnicvf_set_ilb_mode(struct qlcnic_adapter *);
+static int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32);
+static int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *, u32);
+static int qlcnicvf_start_firmware(struct qlcnic_adapter *);
/* PCI Device ID Table */
#define ENTRY(device) \
{PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, (device)), \
@@ -375,7 +382,8 @@ static struct qlcnic_nic_template qlcnic_ops = {
.config_bridged_mode = qlcnic_config_bridged_mode,
.config_led = qlcnic_config_led,
.set_ilb_mode = qlcnic_set_ilb_mode,
- .clear_ilb_mode = qlcnic_clear_ilb_mode
+ .clear_ilb_mode = qlcnic_clear_ilb_mode,
+ .start_firmware = qlcnic_start_firmware
};
static struct qlcnic_nic_template qlcnic_pf_ops = {
@@ -383,7 +391,17 @@ static struct qlcnic_nic_template qlcnic_pf_ops = {
.config_bridged_mode = qlcnic_config_bridged_mode,
.config_led = qlcnic_config_led,
.set_ilb_mode = qlcnic_set_ilb_mode,
- .clear_ilb_mode = qlcnic_clear_ilb_mode
+ .clear_ilb_mode = qlcnic_clear_ilb_mode,
+ .start_firmware = qlcnic_start_firmware
+};
+
+static struct qlcnic_nic_template qlcnic_vf_ops = {
+ .get_mac_addr = qlcnic_get_mac_address,
+ .config_bridged_mode = qlcnicvf_config_bridged_mode,
+ .config_led = qlcnicvf_config_led,
+ .set_ilb_mode = qlcnicvf_set_ilb_mode,
+ .clear_ilb_mode = qlcnicvf_clear_ilb_mode,
+ .start_firmware = qlcnicvf_start_firmware
};
static void
@@ -467,7 +485,6 @@ qlcnic_cleanup_pci_map(struct qlcnic_adapter *adapter)
iounmap(adapter->ahw.pci_base0);
}
-/* Use api lock to access this function */
static int
qlcnic_set_function_modes(struct qlcnic_adapter *adapter)
{
@@ -567,6 +584,7 @@ qlcnic_get_driver_mode(struct qlcnic_adapter *adapter)
/* Set privilege level for other functions */
if (qlcnic_config_npars)
qlcnic_set_function_modes(adapter);
+ qlcnic_dev_set_npar_ready(adapter);
dev_info(&adapter->pdev->dev,
"HAL Version: %d, Management function\n",
adapter->fw_hal_version);
@@ -578,6 +596,13 @@ qlcnic_get_driver_mode(struct qlcnic_adapter *adapter)
adapter->fw_hal_version);
adapter->nic_ops = &qlcnic_pf_ops;
break;
+ case QLCNIC_NON_PRIV_FUNC:
+ adapter->op_mode = QLCNIC_NON_PRIV_FUNC;
+ dev_info(&adapter->pdev->dev,
+ "HAL Version: %d Non Privileged function\n",
+ adapter->fw_hal_version);
+ adapter->nic_ops = &qlcnic_vf_ops;
+ break;
default:
dev_info(&adapter->pdev->dev, "Unknown function mode: %d\n",
priv_level);
@@ -772,6 +797,8 @@ wait_init:
QLCWR32(adapter, QLCNIC_CRB_DEV_STATE, QLCNIC_DEV_READY);
qlcnic_idc_debug_info(adapter, 1);
+ qlcnic_dev_set_npar_ready(adapter);
+
qlcnic_check_options(adapter);
if (adapter->fw_hal_version != QLCNIC_FW_BASE &&
@@ -1244,7 +1271,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (qlcnic_setup_idc_param(adapter))
goto err_out_iounmap;
- err = qlcnic_start_firmware(adapter);
+ err = adapter->nic_ops->start_firmware(adapter);
if (err) {
dev_err(&pdev->dev, "Loading fw failed.Please Reboot\n");
goto err_out_decr_ref;
@@ -1410,7 +1437,7 @@ qlcnic_resume(struct pci_dev *pdev)
pci_set_master(pdev);
pci_restore_state(pdev);
- err = qlcnic_start_firmware(adapter);
+ err = adapter->nic_ops->start_firmware(adapter);
if (err) {
dev_err(&pdev->dev, "failed to start firmware\n");
return err;
@@ -2260,7 +2287,7 @@ qlcnic_fwinit_work(struct work_struct *work)
{
struct qlcnic_adapter *adapter = container_of(work,
struct qlcnic_adapter, fw_work.work);
- u32 dev_state = 0xf;
+ u32 dev_state = 0xf, npar_state;
if (qlcnic_api_lock(adapter))
goto err_ret;
@@ -2273,6 +2300,19 @@ qlcnic_fwinit_work(struct work_struct *work)
return;
}
+ if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) {
+ npar_state = QLCRD32(adapter, QLCNIC_CRB_DEV_NPAR_STATE);
+ if (npar_state == QLCNIC_DEV_NPAR_RDY) {
+ qlcnic_api_unlock(adapter);
+ goto wait_npar;
+ } else {
+ qlcnic_schedule_work(adapter, qlcnic_fwinit_work,
+ FW_POLL_DELAY);
+ qlcnic_api_unlock(adapter);
+ return;
+ }
+ }
+
if (adapter->fw_wait_cnt++ > adapter->reset_ack_timeo) {
dev_err(&adapter->pdev->dev, "Reset:Failed to get ack %d sec\n",
adapter->reset_ack_timeo);
@@ -2305,7 +2345,7 @@ skip_ack_check:
qlcnic_api_unlock(adapter);
- if (!qlcnic_start_firmware(adapter)) {
+ if (!adapter->nic_ops->start_firmware(adapter)) {
qlcnic_schedule_work(adapter, qlcnic_attach_work, 0);
return;
}
@@ -2314,6 +2354,7 @@ skip_ack_check:
qlcnic_api_unlock(adapter);
+wait_npar:
dev_state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE);
QLCDB(adapter, HW, "Func waiting: Device state=%u\n", dev_state);
@@ -2328,7 +2369,7 @@ skip_ack_check:
break;
default:
- if (!qlcnic_start_firmware(adapter)) {
+ if (!adapter->nic_ops->start_firmware(adapter)) {
qlcnic_schedule_work(adapter, qlcnic_attach_work, 0);
return;
}
@@ -2402,6 +2443,30 @@ qlcnic_dev_request_reset(struct qlcnic_adapter *adapter)
qlcnic_api_unlock(adapter);
}
+/* Transit to NPAR READY state from NPAR NOT READY state */
+static void
+qlcnic_dev_set_npar_ready(struct qlcnic_adapter *adapter)
+{
+ u32 state;
+
+ if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC ||
+ adapter->fw_hal_version == QLCNIC_FW_BASE)
+ return;
+
+ if (qlcnic_api_lock(adapter))
+ return;
+
+ state = QLCRD32(adapter, QLCNIC_CRB_DEV_NPAR_STATE);
+
+ if (state != QLCNIC_DEV_NPAR_RDY) {
+ QLCWR32(adapter, QLCNIC_CRB_DEV_NPAR_STATE,
+ QLCNIC_DEV_NPAR_RDY);
+ QLCDB(adapter, DRV, "NPAR READY state set\n");
+ }
+
+ qlcnic_api_unlock(adapter);
+}
+
static void
qlcnic_schedule_work(struct qlcnic_adapter *adapter,
work_func_t func, int delay)
@@ -2885,6 +2950,47 @@ done:
return NOTIFY_DONE;
}
+static int
+qlcnicvf_start_firmware(struct qlcnic_adapter *adapter)
+{
+ int err;
+
+ err = qlcnic_can_start_firmware(adapter);
+ if (err)
+ return err;
+
+ qlcnic_check_options(adapter);
+
+ adapter->need_fw_reset = 0;
+
+ return err;
+}
+
+static int
+qlcnicvf_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable)
+{
+ return -EOPNOTSUPP;
+}
+
+static int
+qlcnicvf_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate)
+{
+ return -EOPNOTSUPP;
+}
+
+static int
+qlcnicvf_set_ilb_mode(struct qlcnic_adapter *adapter)
+{
+ return -EOPNOTSUPP;
+}
+
+static void
+qlcnicvf_clear_ilb_mode(struct qlcnic_adapter *adapter)
+{
+ return;
+}
+
+
static struct notifier_block qlcnic_netdev_cb = {
.notifier_call = qlcnic_netdev_event,
};
--
1.6.0.2
^ permalink raw reply related
* [PATCH 1/2] qlcnic: NIC Partitioning
From: Anirban Chakraborty @ 2010-05-24 23:48 UTC (permalink / raw)
To: David Miller, netdev@vger.kernel.org; +Cc: Amit Salecha, Ameen Rahman
Following changes have been added to enable the adapter to work in
NIC partitioning mode where multiple PCI functions of an adapter port can
be configured to work as NIC functions. The first function that is enumerated on
the PCI bus assumes the role of management function which, besides being able
to do all the NIC functionality, can configure other NIC partitions. Other NIC
functions can be configured as privileged or non privileged functions.
Privileged function can not configure other NIC functions but can do all the
NIC functionality including any firmware initialization, chip reset etc. Non
privileged functions can do only basic IO. For chip reset etc, it depends on the
privilege or management function.
1. Added code to determine PCI function number independent of kernel API.
2. Added Driver - FW version 2.0 support.
3. Changed producer and consumer register offset calculation.
4. Added management and privileged operation modes for npar functions. A module
parameter has been added to control it.
6. Added support for configuring the eswitch in the adapter.
Please apply.
thanks,
Anirban
Signed-off-by: Anirban Chakraborty <anirban.chakraborty@qlogic.com>
---
drivers/net/qlcnic/qlcnic.h | 121 ++++++++-
drivers/net/qlcnic/qlcnic_ctx.c | 513 +++++++++++++++++++++++++++++++++--
drivers/net/qlcnic/qlcnic_ethtool.c | 11 +-
drivers/net/qlcnic/qlcnic_hdr.h | 70 +++++
drivers/net/qlcnic/qlcnic_hw.c | 14 +-
drivers/net/qlcnic/qlcnic_init.c | 40 ++-
drivers/net/qlcnic/qlcnic_main.c | 195 ++++++++++++--
7 files changed, 890 insertions(+), 74 deletions(-)
diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/qlcnic/qlcnic.h
index 896d40d..31a0b43 100644
--- a/drivers/net/qlcnic/qlcnic.h
+++ b/drivers/net/qlcnic/qlcnic.h
@@ -197,8 +197,7 @@ struct cmd_desc_type0 {
__le64 addr_buffer4;
- __le32 reserved2;
- __le16 reserved;
+ u8 eth_addr[ETH_ALEN];
__le16 vlan_TCI;
} __attribute__ ((aligned(64)));
@@ -315,6 +314,8 @@ struct uni_data_desc{
#define QLCNIC_BRDTYPE_P3_10G_XFP 0x0032
#define QLCNIC_BRDTYPE_P3_10G_TP 0x0080
+#define QLCNIC_MSIX_TABLE_OFFSET 0x44
+
/* Flash memory map */
#define QLCNIC_BRDCFG_START 0x4000 /* board config */
#define QLCNIC_BOOTLD_START 0x10000 /* bootld */
@@ -542,7 +543,17 @@ struct qlcnic_recv_context {
#define QLCNIC_CDRP_CMD_READ_PEXQ_PARAMETERS 0x0000001c
#define QLCNIC_CDRP_CMD_GET_LIC_CAPABILITIES 0x0000001d
#define QLCNIC_CDRP_CMD_READ_MAX_LRO_PER_BOARD 0x0000001e
-#define QLCNIC_CDRP_CMD_MAX 0x0000001f
+#define QLCNIC_CDRP_CMD_MAC_ADDRESS 0x0000001f
+
+#define QLCNIC_CDRP_CMD_GET_PCI_INFO 0x00000020
+#define QLCNIC_CDRP_CMD_GET_NIC_INFO 0x00000021
+#define QLCNIC_CDRP_CMD_SET_NIC_INFO 0x00000022
+#define QLCNIC_CDRP_CMD_RESET_NPAR 0x00000023
+#define QLCNIC_CDRP_CMD_GET_ESWITCH_CAPABILITY 0x00000024
+#define QLCNIC_CDRP_CMD_TOGGLE_ESWITCH 0x00000025
+#define QLCNIC_CDRP_CMD_GET_ESWITCH_STATUS 0x00000026
+#define QLCNIC_CDRP_CMD_SET_PORTMIRRORING 0x00000027
+#define QLCNIC_CDRP_CMD_CONFIGURE_ESWITCH 0x00000028
#define QLCNIC_RCODE_SUCCESS 0
#define QLCNIC_RCODE_TIMEOUT 17
@@ -560,7 +571,6 @@ struct qlcnic_recv_context {
/*
* Context state
*/
-#define QLCHAL_VERSION 1
#define QLCNIC_HOST_CTX_STATE_ACTIVE 2
@@ -887,6 +897,7 @@ struct qlcnic_mac_req {
#define MSIX_ENTRIES_PER_ADAPTER NUM_STS_DESC_RINGS
#define QLCNIC_MSIX_TBL_SPACE 8192
#define QLCNIC_PCI_REG_MSIX_TBL 0x44
+#define QLCNIC_MSIX_TBL_PGSIZE 4096
#define QLCNIC_NETDEV_WEIGHT 128
#define QLCNIC_ADAPTER_UP_MAGIC 777
@@ -923,7 +934,6 @@ struct qlcnic_adapter {
u8 mc_enabled;
u8 max_mc_count;
u8 rss_supported;
- u8 rsrvd1;
u8 fw_wait_cnt;
u8 fw_fail_cnt;
u8 tx_timeo_cnt;
@@ -940,6 +950,15 @@ struct qlcnic_adapter {
u16 link_autoneg;
u16 module_type;
+ u16 op_mode;
+ u16 switch_mode;
+ u16 max_tx_ques;
+ u16 max_rx_ques;
+ u16 min_tx_bw;
+ u16 max_tx_bw;
+ u16 max_mtu;
+
+ u32 fw_hal_version;
u32 capabilities;
u32 flags;
u32 irq;
@@ -948,18 +967,22 @@ struct qlcnic_adapter {
u32 int_vec_bit;
u32 heartbit;
+ u8 max_mac_filters;
u8 dev_state;
u8 diag_test;
u8 diag_cnt;
u8 reset_ack_timeo;
u8 dev_init_timeo;
- u8 rsrd1;
u16 msg_enable;
u8 mac_addr[ETH_ALEN];
u64 dev_rst_time;
+ struct qlcnic_pci_info *npars;
+ struct qlcnic_eswitch *eswitch;
+ struct qlcnic_nic_template *nic_ops;
+
struct qlcnic_adapter_stats stats;
struct qlcnic_recv_context recv_ctx;
@@ -984,6 +1007,53 @@ struct qlcnic_adapter {
const struct firmware *fw;
};
+struct qlcnic_info {
+ __le16 pci_func;
+ __le16 op_mode; /* 1 = Priv, 2 = NP, 3 = NP passthru */
+ __le16 phys_port;
+ __le16 switch_mode; /* 0 = disabled, 1 = int, 2 = ext */
+
+ __le32 capabilities;
+ u8 max_mac_filters;
+ u8 reserved1;
+ __le16 max_mtu;
+
+ __le16 max_tx_ques;
+ __le16 max_rx_ques;
+ __le16 min_tx_bw;
+ __le16 max_tx_bw;
+ u8 reserved2[104];
+};
+
+struct qlcnic_pci_info {
+ __le16 id; /* pci function id */
+ __le16 active; /* 1 = Enabled */
+ __le16 type; /* 1 = NIC, 2 = FCoE, 3 = iSCSI */
+ __le16 default_port; /* default port number */
+
+ __le16 tx_min_bw; /* Multiple of 100mbpc */
+ __le16 tx_max_bw;
+ __le16 reserved1[2];
+
+ u8 mac[ETH_ALEN];
+ u8 reserved2[106];
+};
+
+struct qlcnic_eswitch {
+ u8 port;
+ u8 active_vports;
+ u8 active_vlans;
+ u8 active_ucast_filters;
+ u8 max_ucast_filters;
+ u8 max_active_vlans;
+
+ u32 flags;
+#define QLCNIC_SWITCH_ENABLE BIT_1
+#define QLCNIC_SWITCH_VLAN_FILTERING BIT_2
+#define QLCNIC_SWITCH_PROMISC_MODE BIT_3
+#define QLCNIC_SWITCH_PORT_MIRRORING BIT_4
+};
+
int qlcnic_fw_cmd_query_phy(struct qlcnic_adapter *adapter, u32 reg, u32 *val);
int qlcnic_fw_cmd_set_phy(struct qlcnic_adapter *adapter, u32 reg, u32 val);
@@ -1070,13 +1140,14 @@ void qlcnic_advert_link_change(struct qlcnic_adapter *adapter, int linkup);
int qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu);
int qlcnic_change_mtu(struct net_device *netdev, int new_mtu);
int qlcnic_config_hw_lro(struct qlcnic_adapter *adapter, int enable);
-int qlcnic_config_bridged_mode(struct qlcnic_adapter *adapter, int enable);
+int qlcnic_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable);
int qlcnic_send_lro_cleanup(struct qlcnic_adapter *adapter);
void qlcnic_update_cmd_producer(struct qlcnic_adapter *adapter,
struct qlcnic_host_tx_ring *tx_ring);
-int qlcnic_get_mac_addr(struct qlcnic_adapter *adapter, u64 *mac);
+int qlcnic_get_mac_addr(struct qlcnic_adapter *adapter, u8 *mac);
void qlcnic_clear_ilb_mode(struct qlcnic_adapter *adapter);
int qlcnic_set_ilb_mode(struct qlcnic_adapter *adapter);
+void qlcnic_fetch_mac(struct qlcnic_adapter *, u32, u32, u8, u8 *);
/* Functions from qlcnic_main.c */
int qlcnic_reset_context(struct qlcnic_adapter *);
@@ -1088,6 +1159,32 @@ int qlcnic_check_loopback_buff(unsigned char *data);
netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
void qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring);
+/* Functions from qlcnic_vf.c */
+int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *, u32);
+int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32);
+int qlcnicvf_set_ilb_mode(struct qlcnic_adapter *adapter);
+void qlcnicvf_clear_ilb_mode(struct qlcnic_adapter *adapter);
+void qlcnicvf_set_port_mode(struct qlcnic_adapter *adapter);
+
+/* Management functions */
+int qlcnic_set_mac_address(struct qlcnic_adapter *, u8*);
+int qlcnic_get_mac_address(struct qlcnic_adapter *, u8*);
+int qlcnic_get_nic_info(struct qlcnic_adapter *, u8);
+int qlcnic_set_nic_info(struct qlcnic_adapter *, struct qlcnic_info *);
+int qlcnic_get_pci_info(struct qlcnic_adapter *);
+int qlcnic_reset_partition(struct qlcnic_adapter *, u8);
+
+/* eSwitch management functions */
+int qlcnic_get_eswitch_capabilities(struct qlcnic_adapter *, u8,
+ struct qlcnic_eswitch *);
+int qlcnic_get_eswitch_status(struct qlcnic_adapter *, u8,
+ struct qlcnic_eswitch *);
+int qlcnic_toggle_eswitch(struct qlcnic_adapter *, u8, u8);
+int qlcnic_config_switch_port(struct qlcnic_adapter *, u8, int, u8, u8,
+ u8, u8, u16);
+int qlcnic_config_port_mirroring(struct qlcnic_adapter *, u8, u8, u8);
+extern int qlcnic_config_tso;
+
/*
* QLOGIC Board information
*/
@@ -1131,6 +1228,14 @@ static inline u32 qlcnic_tx_avail(struct qlcnic_host_tx_ring *tx_ring)
extern const struct ethtool_ops qlcnic_ethtool_ops;
+struct qlcnic_nic_template {
+ int (*get_mac_addr) (struct qlcnic_adapter *, u8*);
+ int (*config_bridged_mode) (struct qlcnic_adapter *, u32);
+ int (*config_led) (struct qlcnic_adapter *, u32, u32);
+ int (*set_ilb_mode) (struct qlcnic_adapter *);
+ void (*clear_ilb_mode) (struct qlcnic_adapter *);
+};
+
#define QLCDB(adapter, lvl, _fmt, _args...) do { \
if (NETIF_MSG_##lvl & adapter->msg_enable) \
printk(KERN_INFO "%s: %s: " _fmt, \
diff --git a/drivers/net/qlcnic/qlcnic_ctx.c b/drivers/net/qlcnic/qlcnic_ctx.c
index c2c1f5c..1e1dc58 100644
--- a/drivers/net/qlcnic/qlcnic_ctx.c
+++ b/drivers/net/qlcnic/qlcnic_ctx.c
@@ -88,12 +88,12 @@ qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu)
if (recv_ctx->state == QLCNIC_HOST_CTX_STATE_ACTIVE) {
if (qlcnic_issue_cmd(adapter,
- adapter->ahw.pci_func,
- QLCHAL_VERSION,
- recv_ctx->context_id,
- mtu,
- 0,
- QLCNIC_CDRP_CMD_SET_MTU)) {
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ recv_ctx->context_id,
+ mtu,
+ 0,
+ QLCNIC_CDRP_CMD_SET_MTU)) {
dev_err(&adapter->pdev->dev, "Failed to set mtu\n");
return -EIO;
@@ -121,7 +121,7 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
int i, nrds_rings, nsds_rings;
size_t rq_size, rsp_size;
- u32 cap, reg, val;
+ u32 cap, reg, val, reg2;
int err;
struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx;
@@ -197,7 +197,7 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
phys_addr = hostrq_phys_addr;
err = qlcnic_issue_cmd(adapter,
adapter->ahw.pci_func,
- QLCHAL_VERSION,
+ adapter->fw_hal_version,
(u32)(phys_addr >> 32),
(u32)(phys_addr & 0xffffffff),
rq_size,
@@ -216,8 +216,12 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
rds_ring = &recv_ctx->rds_rings[i];
reg = le32_to_cpu(prsp_rds[i].host_producer_crb);
- rds_ring->crb_rcv_producer = qlcnic_get_ioaddr(adapter,
+ if (adapter->fw_hal_version == QLCNIC_FW_BASE)
+ rds_ring->crb_rcv_producer = qlcnic_get_ioaddr(adapter,
QLCNIC_REG(reg - 0x200));
+ else
+ rds_ring->crb_rcv_producer = adapter->ahw.pci_base0 +
+ reg;
}
prsp_sds = ((struct qlcnic_cardrsp_sds_ring *)
@@ -227,12 +231,18 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
sds_ring = &recv_ctx->sds_rings[i];
reg = le32_to_cpu(prsp_sds[i].host_consumer_crb);
- sds_ring->crb_sts_consumer = qlcnic_get_ioaddr(adapter,
- QLCNIC_REG(reg - 0x200));
+ reg2 = le32_to_cpu(prsp_sds[i].interrupt_crb);
- reg = le32_to_cpu(prsp_sds[i].interrupt_crb);
- sds_ring->crb_intr_mask = qlcnic_get_ioaddr(adapter,
+ if (adapter->fw_hal_version == QLCNIC_FW_BASE) {
+ sds_ring->crb_sts_consumer = qlcnic_get_ioaddr(adapter,
QLCNIC_REG(reg - 0x200));
+ sds_ring->crb_intr_mask = qlcnic_get_ioaddr(adapter,
+ QLCNIC_REG(reg2 - 0x200));
+ } else {
+ sds_ring->crb_sts_consumer = adapter->ahw.pci_base0 +
+ reg;
+ sds_ring->crb_intr_mask = adapter->ahw.pci_base0 + reg2;
+ }
}
recv_ctx->state = le32_to_cpu(prsp->host_ctx_state);
@@ -253,7 +263,7 @@ qlcnic_fw_cmd_destroy_rx_ctx(struct qlcnic_adapter *adapter)
if (qlcnic_issue_cmd(adapter,
adapter->ahw.pci_func,
- QLCHAL_VERSION,
+ adapter->fw_hal_version,
recv_ctx->context_id,
QLCNIC_DESTROY_CTX_RESET,
0,
@@ -319,7 +329,7 @@ qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter)
phys_addr = rq_phys_addr;
err = qlcnic_issue_cmd(adapter,
adapter->ahw.pci_func,
- QLCHAL_VERSION,
+ adapter->fw_hal_version,
(u32)(phys_addr >> 32),
((u32)phys_addr & 0xffffffff),
rq_size,
@@ -327,8 +337,12 @@ qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter)
if (err == QLCNIC_RCODE_SUCCESS) {
temp = le32_to_cpu(prsp->cds_ring.host_producer_crb);
- tx_ring->crb_cmd_producer = qlcnic_get_ioaddr(adapter,
+ if (adapter->fw_hal_version == QLCNIC_FW_BASE)
+ tx_ring->crb_cmd_producer = qlcnic_get_ioaddr(adapter,
QLCNIC_REG(temp - 0x200));
+ else
+ tx_ring->crb_cmd_producer = adapter->ahw.pci_base0 +
+ temp;
adapter->tx_context_id =
le16_to_cpu(prsp->context_id);
@@ -351,7 +365,7 @@ qlcnic_fw_cmd_destroy_tx_ctx(struct qlcnic_adapter *adapter)
{
if (qlcnic_issue_cmd(adapter,
adapter->ahw.pci_func,
- QLCHAL_VERSION,
+ adapter->fw_hal_version,
adapter->tx_context_id,
QLCNIC_DESTROY_CTX_RESET,
0,
@@ -368,7 +382,7 @@ qlcnic_fw_cmd_query_phy(struct qlcnic_adapter *adapter, u32 reg, u32 *val)
if (qlcnic_issue_cmd(adapter,
adapter->ahw.pci_func,
- QLCHAL_VERSION,
+ adapter->fw_hal_version,
reg,
0,
0,
@@ -385,7 +399,7 @@ qlcnic_fw_cmd_set_phy(struct qlcnic_adapter *adapter, u32 reg, u32 val)
{
return qlcnic_issue_cmd(adapter,
adapter->ahw.pci_func,
- QLCHAL_VERSION,
+ adapter->fw_hal_version,
reg,
val,
0,
@@ -533,3 +547,464 @@ void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter)
}
}
+/* Set MAC address of a NIC partition */
+int qlcnic_set_mac_address(struct qlcnic_adapter *adapter, u8* mac)
+{
+ int err = 0;
+ u32 arg1, arg2, arg3;
+
+ arg1 = adapter->ahw.pci_func | BIT_9;
+ arg2 = mac[0] | (mac[1] << 8) | (mac[2] << 16) | (mac[3] << 24);
+ arg3 = mac[4] | (mac[5] << 16);
+
+ err = qlcnic_issue_cmd(adapter,
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ arg1,
+ arg2,
+ arg3,
+ QLCNIC_CDRP_CMD_MAC_ADDRESS);
+
+ if (err != QLCNIC_RCODE_SUCCESS) {
+ dev_err(&adapter->pdev->dev,
+ "Failed to set mac address%d\n", err);
+ err = -EIO;
+ }
+
+ return err;
+}
+
+/* Get MAC address of a NIC partition */
+int qlcnic_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac)
+{
+ int err;
+ u32 arg1;
+
+ arg1 = adapter->ahw.pci_func | BIT_8;
+ err = qlcnic_issue_cmd(adapter,
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ arg1,
+ 0,
+ 0,
+ QLCNIC_CDRP_CMD_MAC_ADDRESS);
+
+ if (err == QLCNIC_RCODE_SUCCESS) {
+ qlcnic_fetch_mac(adapter, QLCNIC_ARG1_CRB_OFFSET,
+ QLCNIC_ARG2_CRB_OFFSET, 0, mac);
+ dev_info(&adapter->pdev->dev, "MAC address: %pM\n", mac);
+ } else {
+ dev_err(&adapter->pdev->dev,
+ "Failed to get mac address%d\n", err);
+ err = -EIO;
+ }
+
+ return err;
+}
+
+/* Get info of a NIC partition */
+int qlcnic_get_nic_info(struct qlcnic_adapter *adapter, u8 func_id)
+{
+ int err;
+ dma_addr_t nic_dma_t;
+ struct qlcnic_info *nic_info;
+ void *nic_info_addr;
+ size_t nic_size = sizeof(struct qlcnic_info);
+
+ nic_info_addr = pci_alloc_consistent(adapter->pdev,
+ nic_size, &nic_dma_t);
+ if (!nic_info_addr)
+ return -ENOMEM;
+ memset(nic_info_addr, 0, nic_size);
+
+ nic_info = (struct qlcnic_info *) nic_info_addr;
+ err = qlcnic_issue_cmd(adapter,
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ MSD(nic_dma_t),
+ LSD(nic_dma_t),
+ (func_id << 16 | nic_size),
+ QLCNIC_CDRP_CMD_GET_NIC_INFO);
+
+ if (err == QLCNIC_RCODE_SUCCESS) {
+ adapter->physical_port = le16_to_cpu(nic_info->phys_port);
+ adapter->switch_mode = le16_to_cpu(nic_info->switch_mode);
+ adapter->max_tx_ques = le16_to_cpu(nic_info->max_tx_ques);
+ adapter->max_rx_ques = le16_to_cpu(nic_info->max_rx_ques);
+ adapter->min_tx_bw = le16_to_cpu(nic_info->min_tx_bw);
+ adapter->max_tx_bw = le16_to_cpu(nic_info->max_tx_bw);
+ adapter->max_mtu = le16_to_cpu(nic_info->max_mtu);
+ adapter->capabilities = le32_to_cpu(nic_info->capabilities);
+ adapter->max_mac_filters = nic_info->max_mac_filters;
+
+ dev_info(&adapter->pdev->dev,
+ "phy port: %d switch_mode: %d,\n"
+ "\tmax_tx_q: %d max_rx_q: %d min_tx_bw: 0x%x,\n"
+ "\tmax_tx_bw: 0x%x max_mtu:0x%x, capabilities: 0x%x\n",
+ adapter->physical_port, adapter->switch_mode,
+ adapter->max_tx_ques, adapter->max_rx_ques,
+ adapter->min_tx_bw, adapter->max_tx_bw,
+ adapter->max_mtu, adapter->capabilities);
+ } else {
+ dev_err(&adapter->pdev->dev,
+ "Failed to get nic info%d\n", err);
+ err = -EIO;
+ }
+
+ pci_free_consistent(adapter->pdev, nic_size, nic_info_addr, nic_dma_t);
+ return err;
+}
+
+/* Configure a NIC partition */
+int qlcnic_set_nic_info(struct qlcnic_adapter *adapter, struct qlcnic_info *nic)
+{
+ int err = -EIO;
+ u32 func_state;
+ dma_addr_t nic_dma_t;
+ void *nic_info_addr;
+ struct qlcnic_info *nic_info;
+ size_t nic_size = sizeof(struct qlcnic_info);
+
+ if (adapter->op_mode != QLCNIC_MGMT_FUNC)
+ return err;
+
+ if (qlcnic_api_lock(adapter))
+ return err;
+
+ func_state = QLCRD32(adapter, QLCNIC_CRB_DEV_REF_COUNT);
+ if (QLC_DEV_CHECK_ACTIVE(func_state, nic->pci_func)) {
+ qlcnic_api_unlock(adapter);
+ return err;
+ }
+
+ qlcnic_api_unlock(adapter);
+
+ nic_info_addr = pci_alloc_consistent(adapter->pdev, nic_size,
+ &nic_dma_t);
+ if (!nic_info_addr)
+ return -ENOMEM;
+
+ memset(nic_info_addr, 0, nic_size);
+ nic_info = (struct qlcnic_info *)nic_info_addr;
+
+ nic_info->pci_func = cpu_to_le16(nic->pci_func);
+ nic_info->op_mode = cpu_to_le16(nic->op_mode);
+ nic_info->phys_port = cpu_to_le16(nic->phys_port);
+ nic_info->switch_mode = cpu_to_le16(nic->switch_mode);
+ nic_info->capabilities = cpu_to_le32(nic->capabilities);
+ nic_info->max_mac_filters = nic->max_mac_filters;
+ nic_info->max_tx_ques = cpu_to_le16(nic->max_tx_ques);
+ nic_info->max_rx_ques = cpu_to_le16(nic->max_rx_ques);
+ nic_info->min_tx_bw = cpu_to_le16(nic->min_tx_bw);
+ nic_info->max_tx_bw = cpu_to_le16(nic->max_tx_bw);
+
+ err = qlcnic_issue_cmd(adapter,
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ MSD(nic_dma_t),
+ LSD(nic_dma_t),
+ nic_size,
+ QLCNIC_CDRP_CMD_SET_NIC_INFO);
+
+ if (err != QLCNIC_RCODE_SUCCESS) {
+ dev_err(&adapter->pdev->dev,
+ "Failed to set nic info%d\n", err);
+ err = -EIO;
+ }
+
+ pci_free_consistent(adapter->pdev, nic_size, nic_info_addr, nic_dma_t);
+ return err;
+}
+
+/* Get PCI Info of a partition */
+int qlcnic_get_pci_info(struct qlcnic_adapter *adapter)
+{
+ int err = 0, i;
+ dma_addr_t pci_info_dma_t;
+ struct qlcnic_pci_info *npar;
+ void *pci_info_addr;
+ size_t npar_size = sizeof(struct qlcnic_pci_info);
+ size_t pci_size = npar_size * QLCNIC_MAX_PCI_FUNC;
+
+ pci_info_addr = pci_alloc_consistent(adapter->pdev, pci_size,
+ &pci_info_dma_t);
+ if (!pci_info_addr)
+ return -ENOMEM;
+ memset(pci_info_addr, 0, pci_size);
+
+ if (!adapter->npars)
+ adapter->npars = kzalloc(pci_size, GFP_KERNEL);
+ if (!adapter->npars) {
+ err = -ENOMEM;
+ goto err_npar;
+ }
+
+ if (!adapter->eswitch)
+ adapter->eswitch = kzalloc(sizeof(struct qlcnic_eswitch) *
+ QLCNIC_NIU_MAX_XG_PORTS, GFP_KERNEL);
+ if (!adapter->eswitch) {
+ err = -ENOMEM;
+ goto err_eswitch;
+ }
+
+ npar = (struct qlcnic_pci_info *) pci_info_addr;
+ err = qlcnic_issue_cmd(adapter,
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ MSD(pci_info_dma_t),
+ LSD(pci_info_dma_t),
+ pci_size,
+ QLCNIC_CDRP_CMD_GET_PCI_INFO);
+
+ if (err == QLCNIC_RCODE_SUCCESS) {
+ for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++, npar++) {
+ adapter->npars[i].id = le32_to_cpu(npar->id);
+ adapter->npars[i].active = le32_to_cpu(npar->active);
+ adapter->npars[i].type = le32_to_cpu(npar->type);
+ adapter->npars[i].default_port =
+ le32_to_cpu(npar->default_port);
+ adapter->npars[i].tx_min_bw =
+ le32_to_cpu(npar->tx_min_bw);
+ adapter->npars[i].tx_max_bw =
+ le32_to_cpu(npar->tx_max_bw);
+ memcpy(adapter->npars[i].mac, npar->mac, ETH_ALEN);
+ }
+ } else {
+ dev_err(&adapter->pdev->dev,
+ "Failed to get PCI Info%d\n", err);
+ kfree(adapter->npars);
+ err = -EIO;
+ }
+ goto err_npar;
+
+err_eswitch:
+ kfree(adapter->npars);
+ adapter->npars = NULL;
+
+err_npar:
+ pci_free_consistent(adapter->pdev, pci_size, pci_info_addr,
+ pci_info_dma_t);
+ return err;
+}
+
+/* Reset a NIC partition */
+
+int qlcnic_reset_partition(struct qlcnic_adapter *adapter, u8 func_no)
+{
+ int err = -EIO;
+
+ if (adapter->op_mode != QLCNIC_MGMT_FUNC)
+ return err;
+
+ err = qlcnic_issue_cmd(adapter,
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ func_no,
+ 0,
+ 0,
+ QLCNIC_CDRP_CMD_RESET_NPAR);
+
+ if (err != QLCNIC_RCODE_SUCCESS) {
+ dev_err(&adapter->pdev->dev,
+ "Failed to issue reset partition%d\n", err);
+ err = -EIO;
+ }
+
+ return err;
+}
+
+/* Get eSwitch Capabilities */
+int qlcnic_get_eswitch_capabilities(struct qlcnic_adapter *adapter, u8 port,
+ struct qlcnic_eswitch *eswitch)
+{
+ int err = -EIO;
+ u32 arg1, arg2;
+
+ if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC)
+ return err;
+
+ err = qlcnic_issue_cmd(adapter,
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ port,
+ 0,
+ 0,
+ QLCNIC_CDRP_CMD_GET_ESWITCH_CAPABILITY);
+
+ if (err == QLCNIC_RCODE_SUCCESS) {
+ arg1 = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET);
+ arg2 = QLCRD32(adapter, QLCNIC_ARG2_CRB_OFFSET);
+
+ eswitch->port = arg1 & 0xf;
+ eswitch->active_vports = LSB(arg2);
+ eswitch->max_ucast_filters = MSB(arg2);
+ eswitch->max_active_vlans = LSB(MSW(arg2));
+ if (arg1 & BIT_6)
+ eswitch->flags |= QLCNIC_SWITCH_VLAN_FILTERING;
+ if (arg1 & BIT_7)
+ eswitch->flags |= QLCNIC_SWITCH_PROMISC_MODE;
+ if (arg1 & BIT_8)
+ eswitch->flags |= QLCNIC_SWITCH_PORT_MIRRORING;
+ } else {
+ dev_err(&adapter->pdev->dev,
+ "Failed to get eswitch capabilities%d\n", err);
+ }
+
+ return err;
+}
+
+/* Get current status of eswitch */
+int qlcnic_get_eswitch_status(struct qlcnic_adapter *adapter, u8 port,
+ struct qlcnic_eswitch *eswitch)
+{
+ int err = -EIO;
+ u32 arg1, arg2;
+
+ if (adapter->op_mode != QLCNIC_MGMT_FUNC)
+ return err;
+
+ err = qlcnic_issue_cmd(adapter,
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ port,
+ 0,
+ 0,
+ QLCNIC_CDRP_CMD_GET_ESWITCH_STATUS);
+
+ if (err == QLCNIC_RCODE_SUCCESS) {
+ arg1 = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET);
+ arg2 = QLCRD32(adapter, QLCNIC_ARG2_CRB_OFFSET);
+
+ eswitch->port = arg1 & 0xf;
+ eswitch->active_vports = LSB(arg2);
+ eswitch->active_ucast_filters = MSB(arg2);
+ eswitch->active_vlans = LSB(MSW(arg2));
+ if (arg1 & BIT_6)
+ eswitch->flags |= QLCNIC_SWITCH_VLAN_FILTERING;
+ if (arg1 & BIT_8)
+ eswitch->flags |= QLCNIC_SWITCH_PORT_MIRRORING;
+
+ } else {
+ dev_err(&adapter->pdev->dev,
+ "Failed to get eswitch status%d\n", err);
+ }
+
+ return err;
+}
+
+/* Enable/Disable eSwitch */
+int qlcnic_toggle_eswitch(struct qlcnic_adapter *adapter, u8 id, u8 enable)
+{
+ int err = -EIO;
+ u32 arg1, arg2;
+ struct qlcnic_eswitch *eswitch;
+
+ if (adapter->op_mode != QLCNIC_MGMT_FUNC)
+ return err;
+
+ eswitch = &adapter->eswitch[id];
+ if (!eswitch)
+ return err;
+
+ arg1 = eswitch->port | (enable ? BIT_4 : 0);
+ arg2 = eswitch->active_vports | (eswitch->max_ucast_filters << 8) |
+ (eswitch->max_active_vlans << 16);
+ err = qlcnic_issue_cmd(adapter,
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ arg1,
+ arg2,
+ 0,
+ QLCNIC_CDRP_CMD_TOGGLE_ESWITCH);
+
+ if (err != QLCNIC_RCODE_SUCCESS) {
+ dev_err(&adapter->pdev->dev,
+ "Failed to enable eswitch%d\n", eswitch->port);
+ eswitch->flags &= ~QLCNIC_SWITCH_ENABLE;
+ err = -EIO;
+ } else {
+ eswitch->flags |= QLCNIC_SWITCH_ENABLE;
+ dev_info(&adapter->pdev->dev,
+ "Enabled eSwitch for port %d\n", eswitch->port);
+ }
+
+ return err;
+}
+
+/* Configure eSwitch for port mirroring */
+int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id,
+ u8 enable_mirroring, u8 pci_func)
+{
+ int err = -EIO;
+ u32 arg1;
+
+ if (adapter->op_mode != QLCNIC_MGMT_FUNC ||
+ !(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE))
+ return err;
+
+ arg1 = id | (enable_mirroring ? BIT_4 : 0);
+ arg1 |= pci_func << 8;
+
+ err = qlcnic_issue_cmd(adapter,
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ arg1,
+ 0,
+ 0,
+ QLCNIC_CDRP_CMD_SET_PORTMIRRORING);
+
+ if (err != QLCNIC_RCODE_SUCCESS) {
+ dev_err(&adapter->pdev->dev,
+ "Failed to configure port mirroring%d on eswitch:%d\n",
+ pci_func, id);
+ } else {
+ dev_info(&adapter->pdev->dev,
+ "Configured eSwitch %d for port mirroring:%d\n",
+ id, pci_func);
+ }
+
+ return err;
+}
+
+/* Configure eSwitch port */
+int qlcnic_config_switch_port(struct qlcnic_adapter *adapter, u8 id,
+ int vlan_tagging, u8 discard_tagged, u8 promsc_mode,
+ u8 mac_learn, u8 pci_func, u16 vlan_id)
+{
+ int err = -EIO;
+ u32 arg1;
+ struct qlcnic_eswitch *eswitch;
+
+ if (adapter->op_mode != QLCNIC_MGMT_FUNC)
+ return err;
+
+ eswitch = &adapter->eswitch[id];
+ if (!(eswitch->flags & QLCNIC_SWITCH_ENABLE))
+ return err;
+
+ arg1 = eswitch->port | (discard_tagged ? BIT_4 : 0);
+ arg1 |= (promsc_mode ? BIT_6 : 0) | (mac_learn ? BIT_7 : 0);
+ arg1 |= pci_func << 8;
+ if (vlan_tagging)
+ arg1 |= BIT_5 | (vlan_id << 16);
+
+ err = qlcnic_issue_cmd(adapter,
+ adapter->ahw.pci_func,
+ adapter->fw_hal_version,
+ arg1,
+ 0,
+ 0,
+ QLCNIC_CDRP_CMD_CONFIGURE_ESWITCH);
+
+ if (err != QLCNIC_RCODE_SUCCESS) {
+ dev_err(&adapter->pdev->dev,
+ "Failed to configure eswitch port%d\n", eswitch->port);
+ eswitch->flags |= QLCNIC_SWITCH_ENABLE;
+ } else {
+ eswitch->flags &= ~QLCNIC_SWITCH_ENABLE;
+ dev_info(&adapter->pdev->dev,
+ "Configured eSwitch for port %d\n", eswitch->port);
+ }
+
+ return err;
+}
diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/qlcnic/qlcnic_ethtool.c
index 3bd514e..3e4822a 100644
--- a/drivers/net/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/qlcnic/qlcnic_ethtool.c
@@ -683,13 +683,13 @@ static int qlcnic_loopback_test(struct net_device *netdev)
if (ret)
goto clear_it;
- ret = qlcnic_set_ilb_mode(adapter);
+ ret = adapter->nic_ops->set_ilb_mode(adapter);
if (ret)
goto done;
ret = qlcnic_do_ilb_test(adapter);
- qlcnic_clear_ilb_mode(adapter);
+ adapter->nic_ops->clear_ilb_mode(adapter);
done:
qlcnic_diag_free_res(netdev, max_sds_rings);
@@ -715,7 +715,8 @@ static int qlcnic_irq_test(struct net_device *netdev)
adapter->diag_cnt = 0;
ret = qlcnic_issue_cmd(adapter, adapter->ahw.pci_func,
- QLCHAL_VERSION, adapter->portnum, 0, 0, 0x00000011);
+ adapter->fw_hal_version, adapter->portnum,
+ 0, 0, 0x00000011);
if (ret)
goto done;
@@ -834,7 +835,7 @@ static int qlcnic_blink_led(struct net_device *dev, u32 val)
struct qlcnic_adapter *adapter = netdev_priv(dev);
int ret;
- ret = qlcnic_config_led(adapter, 1, 0xf);
+ ret = adapter->nic_ops->config_led(adapter, 1, 0xf);
if (ret) {
dev_err(&adapter->pdev->dev,
"Failed to set LED blink state.\n");
@@ -843,7 +844,7 @@ static int qlcnic_blink_led(struct net_device *dev, u32 val)
msleep_interruptible(val * 1000);
- ret = qlcnic_config_led(adapter, 0, 0xf);
+ ret = adapter->nic_ops->config_led(adapter, 0, 0xf);
if (ret) {
dev_err(&adapter->pdev->dev,
"Failed to reset LED blink state.\n");
diff --git a/drivers/net/qlcnic/qlcnic_hdr.h b/drivers/net/qlcnic/qlcnic_hdr.h
index ad9d167..1bcfb12 100644
--- a/drivers/net/qlcnic/qlcnic_hdr.h
+++ b/drivers/net/qlcnic/qlcnic_hdr.h
@@ -208,6 +208,39 @@ enum {
QLCNIC_HW_PX_MAP_CRB_PGR0
};
+#define BIT_0 0x1
+#define BIT_1 0x2
+#define BIT_2 0x4
+#define BIT_3 0x8
+#define BIT_4 0x10
+#define BIT_5 0x20
+#define BIT_6 0x40
+#define BIT_7 0x80
+#define BIT_8 0x100
+#define BIT_9 0x200
+#define BIT_10 0x400
+#define BIT_11 0x800
+#define BIT_12 0x1000
+#define BIT_13 0x2000
+#define BIT_14 0x4000
+#define BIT_15 0x8000
+#define BIT_16 0x10000
+#define BIT_17 0x20000
+#define BIT_18 0x40000
+#define BIT_19 0x80000
+#define BIT_20 0x100000
+#define BIT_21 0x200000
+#define BIT_22 0x400000
+#define BIT_23 0x800000
+#define BIT_24 0x1000000
+#define BIT_25 0x2000000
+#define BIT_26 0x4000000
+#define BIT_27 0x8000000
+#define BIT_28 0x10000000
+#define BIT_29 0x20000000
+#define BIT_30 0x40000000
+#define BIT_31 0x80000000
+
/* This field defines CRB adr [31:20] of the agents */
#define QLCNIC_HW_CRB_HUB_AGT_ADR_MN \
@@ -684,12 +717,20 @@ enum {
#define QLCNIC_DEV_FAILED 0x6
#define QLCNIC_DEV_QUISCENT 0x7
+#define QLC_DEV_CHECK_ACTIVE(VAL, FN) ((VAL) &= (1 << (FN * 4)))
#define QLC_DEV_SET_REF_CNT(VAL, FN) ((VAL) |= (1 << (FN * 4)))
#define QLC_DEV_CLR_REF_CNT(VAL, FN) ((VAL) &= ~(1 << (FN * 4)))
#define QLC_DEV_SET_RST_RDY(VAL, FN) ((VAL) |= (1 << (FN * 4)))
#define QLC_DEV_SET_QSCNT_RDY(VAL, FN) ((VAL) |= (2 << (FN * 4)))
#define QLC_DEV_CLR_RST_QSCNT(VAL, FN) ((VAL) &= ~(3 << (FN * 4)))
+#define QLC_DEV_GET_DRV(VAL, FN) (0xf & ((VAL) >> (FN * 4)))
+#define QLC_DEV_SET_DRV(VAL, FN) ((VAL) << (FN * 4))
+
+#define QLCNIC_TYPE_NIC 1
+#define QLCNIC_TYPE_FCOE 2
+#define QLCNIC_TYPE_ISCSI 3
+
#define QLCNIC_RCODE_DRIVER_INFO 0x20000000
#define QLCNIC_RCODE_DRIVER_CAN_RELOAD 0x40000000
#define QLCNIC_RCODE_FATAL_ERROR 0x80000000
@@ -721,6 +762,35 @@ struct qlcnic_legacy_intr_set {
u32 pci_int_reg;
};
+#define QLCNIC_FW_API 0x1b216c
+#define QLCNIC_DRV_OP_MODE 0x1b2170
+#define QLCNIC_MSIX_BASE 0x132110
+#define QLCNIC_MAX_PCI_FUNC 8
+
+/* PCI function operational mode */
+enum {
+ QLCNIC_MGMT_FUNC = 0,
+ QLCNIC_PRIV_FUNC = 1,
+ QLCNIC_NON_PRIV_FUNC = 2
+};
+
+/* FW HAL api version */
+enum {
+ QLCNIC_FW_BASE = 1,
+ QLCNIC_FW_NPAR = 2
+};
+
+#define QLC_DEV_DRV_DEFAULT 0x11111111
+
+#define LSB(x) ((uint8_t)(x))
+#define MSB(x) ((uint8_t)((uint16_t)(x) >> 8))
+
+#define LSW(x) ((uint16_t)((uint32_t)(x)))
+#define MSW(x) ((uint16_t)((uint32_t)(x) >> 16))
+
+#define LSD(x) ((uint32_t)((uint64_t)(x)))
+#define MSD(x) ((uint32_t)((((uint64_t)(x)) >> 16) >> 16))
+
#define QLCNIC_LEGACY_INTR_CONFIG \
{ \
{ \
diff --git a/drivers/net/qlcnic/qlcnic_hw.c b/drivers/net/qlcnic/qlcnic_hw.c
index 0c2e1f0..f776956 100644
--- a/drivers/net/qlcnic/qlcnic_hw.c
+++ b/drivers/net/qlcnic/qlcnic_hw.c
@@ -538,7 +538,7 @@ int qlcnic_config_hw_lro(struct qlcnic_adapter *adapter, int enable)
return rv;
}
-int qlcnic_config_bridged_mode(struct qlcnic_adapter *adapter, int enable)
+int qlcnic_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable)
{
struct qlcnic_nic_req req;
u64 word;
@@ -704,21 +704,15 @@ int qlcnic_change_mtu(struct net_device *netdev, int mtu)
return rc;
}
-int qlcnic_get_mac_addr(struct qlcnic_adapter *adapter, u64 *mac)
+int qlcnic_get_mac_addr(struct qlcnic_adapter *adapter, u8 *mac)
{
- u32 crbaddr, mac_hi, mac_lo;
+ u32 crbaddr;
int pci_func = adapter->ahw.pci_func;
crbaddr = CRB_MAC_BLOCK_START +
(4 * ((pci_func/2) * 3)) + (4 * (pci_func & 1));
- mac_lo = QLCRD32(adapter, crbaddr);
- mac_hi = QLCRD32(adapter, crbaddr+4);
-
- if (pci_func & 1)
- *mac = le64_to_cpu((mac_lo >> 16) | ((u64)mac_hi << 16));
- else
- *mac = le64_to_cpu((u64)mac_lo | ((u64)mac_hi << 32));
+ qlcnic_fetch_mac(adapter, crbaddr, crbaddr+4, pci_func & 1, mac);
return 0;
}
diff --git a/drivers/net/qlcnic/qlcnic_init.c b/drivers/net/qlcnic/qlcnic_init.c
index 71a4e66..635c990 100644
--- a/drivers/net/qlcnic/qlcnic_init.c
+++ b/drivers/net/qlcnic/qlcnic_init.c
@@ -520,17 +520,16 @@ qlcnic_setup_idc_param(struct qlcnic_adapter *adapter) {
int timeo;
u32 val;
- val = QLCRD32(adapter, QLCNIC_CRB_DEV_PARTITION_INFO);
- val = (val >> (adapter->portnum * 4)) & 0xf;
-
- if ((val & 0x3) != 1) {
- dev_err(&adapter->pdev->dev, "Not an Ethernet NIC func=%u\n",
- val);
- return -EIO;
+ if (adapter->fw_hal_version == QLCNIC_FW_BASE) {
+ val = QLCRD32(adapter, QLCNIC_CRB_DEV_PARTITION_INFO);
+ val = QLC_DEV_GET_DRV(val, adapter->portnum);
+ if ((val & 0x3) != QLCNIC_TYPE_NIC) {
+ dev_err(&adapter->pdev->dev,
+ "Not an Ethernet NIC func=%u\n", val);
+ return -EIO;
+ }
+ adapter->physical_port = (val >> 2);
}
-
- adapter->physical_port = (val >> 2);
-
if (qlcnic_rom_fast_read(adapter, QLCNIC_ROM_DEV_INIT_TIMEOUT, &timeo))
timeo = 30;
@@ -1701,3 +1700,24 @@ qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring)
sds_ring->consumer = consumer;
writel(consumer, sds_ring->crb_sts_consumer);
}
+
+void
+qlcnic_fetch_mac(struct qlcnic_adapter *adapter, u32 off1, u32 off2,
+ u8 alt_mac, u8 *mac)
+{
+ u32 mac_low, mac_high;
+ int i;
+
+ mac_low = QLCRD32(adapter, off1);
+ mac_high = QLCRD32(adapter, off2);
+
+ if (alt_mac) {
+ mac_low |= (mac_low >> 16) | (mac_high << 16);
+ mac_high >>= 16;
+ }
+
+ for (i = 0; i < 2; i++)
+ mac[i] = (u8)(mac_high >> ((1 - i) * 8));
+ for (i = 2; i < 6; i++)
+ mac[i] = (u8)(mac_low >> ((5 - i) * 8));
+}
diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c
index 1003eb7..3b4fdb3 100644
--- a/drivers/net/qlcnic/qlcnic_main.c
+++ b/drivers/net/qlcnic/qlcnic_main.c
@@ -65,6 +65,10 @@ static int load_fw_file;
module_param(load_fw_file, int, 0644);
MODULE_PARM_DESC(load_fw_file, "Load firmware from (0=flash, 1=file");
+static int qlcnic_config_npars;
+module_param(qlcnic_config_npars, int, 0644);
+MODULE_PARM_DESC(qlcnic_config_npars, "Configure NPARs (0=disabled, 1=enabled");
+
static int __devinit qlcnic_probe(struct pci_dev *pdev,
const struct pci_device_id *ent);
static void __devexit qlcnic_remove(struct pci_dev *pdev);
@@ -307,19 +311,14 @@ static void qlcnic_init_msix_entries(struct qlcnic_adapter *adapter, int count)
static int
qlcnic_read_mac_addr(struct qlcnic_adapter *adapter)
{
- int i;
- unsigned char *p;
- u64 mac_addr;
+ u8 mac_addr[ETH_ALEN];
struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
- if (qlcnic_get_mac_addr(adapter, &mac_addr) != 0)
+ if (adapter->nic_ops->get_mac_addr(adapter, mac_addr) != 0)
return -EIO;
- p = (unsigned char *)&mac_addr;
- for (i = 0; i < 6; i++)
- netdev->dev_addr[i] = *(p + 5 - i);
-
+ memcpy(netdev->dev_addr, mac_addr, ETH_ALEN);
memcpy(netdev->perm_addr, netdev->dev_addr, netdev->addr_len);
memcpy(adapter->mac_addr, netdev->dev_addr, netdev->addr_len);
@@ -371,6 +370,22 @@ static const struct net_device_ops qlcnic_netdev_ops = {
#endif
};
+static struct qlcnic_nic_template qlcnic_ops = {
+ .get_mac_addr = qlcnic_get_mac_addr,
+ .config_bridged_mode = qlcnic_config_bridged_mode,
+ .config_led = qlcnic_config_led,
+ .set_ilb_mode = qlcnic_set_ilb_mode,
+ .clear_ilb_mode = qlcnic_clear_ilb_mode
+};
+
+static struct qlcnic_nic_template qlcnic_pf_ops = {
+ .get_mac_addr = qlcnic_get_mac_address,
+ .config_bridged_mode = qlcnic_config_bridged_mode,
+ .config_led = qlcnic_config_led,
+ .set_ilb_mode = qlcnic_set_ilb_mode,
+ .clear_ilb_mode = qlcnic_clear_ilb_mode
+};
+
static void
qlcnic_setup_intr(struct qlcnic_adapter *adapter)
{
@@ -452,6 +467,125 @@ qlcnic_cleanup_pci_map(struct qlcnic_adapter *adapter)
iounmap(adapter->ahw.pci_base0);
}
+/* Use api lock to access this function */
+static int
+qlcnic_set_function_modes(struct qlcnic_adapter *adapter)
+{
+ u8 id;
+ u32 ref_count;
+ int i, ret = 1;
+ u32 data = QLCNIC_MGMT_FUNC;
+ void __iomem *priv_op = adapter->ahw.pci_base0 + QLCNIC_DRV_OP_MODE;
+
+ /* If other drivers are not in use set their privilege level */
+ ref_count = QLCRD32(adapter, QLCNIC_CRB_DEV_REF_COUNT);
+ ret = qlcnic_api_lock(adapter);
+ if (ret)
+ goto err_lock;
+ if (QLC_DEV_CLR_REF_CNT(ref_count, adapter->ahw.pci_func))
+ goto err_npar;
+
+ for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
+ id = adapter->npars[i].id;
+ if (adapter->npars[i].type != QLCNIC_TYPE_NIC ||
+ id == adapter->ahw.pci_func)
+ continue;
+ data |= (qlcnic_config_npars & QLC_DEV_SET_DRV(0xf, id));
+ }
+ writel(data, priv_op);
+
+err_npar:
+ qlcnic_api_unlock(adapter);
+err_lock:
+ return ret;
+}
+
+static u8
+qlcnic_set_mgmt_driver(struct qlcnic_adapter *adapter)
+{
+ u8 i, ret = 0;
+
+ if (qlcnic_get_pci_info(adapter))
+ return ret;
+ /* Set the eswitch */
+ for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++) {
+ if (!qlcnic_get_eswitch_capabilities(adapter, i,
+ &adapter->eswitch[i])) {
+ ret++;
+ qlcnic_toggle_eswitch(adapter, i, ret);
+ }
+ }
+ return ret;
+}
+
+static u32
+qlcnic_get_driver_mode(struct qlcnic_adapter *adapter)
+{
+ void __iomem *msix_base_addr;
+ void __iomem *priv_op;
+ u32 func;
+ u32 msix_base;
+ u32 op_mode, priv_level;
+
+ /* Determine FW API version */
+ adapter->fw_hal_version = readl(adapter->ahw.pci_base0 + QLCNIC_FW_API);
+ if (adapter->fw_hal_version == ~0) {
+ adapter->nic_ops = &qlcnic_ops;
+ adapter->fw_hal_version = QLCNIC_FW_BASE;
+ adapter->ahw.pci_func = PCI_FUNC(adapter->pdev->devfn);
+ dev_info(&adapter->pdev->dev,
+ "FW does not support nic partion\n");
+ return adapter->fw_hal_version;
+ }
+
+ /* Find PCI function number */
+ pci_read_config_dword(adapter->pdev, QLCNIC_MSIX_TABLE_OFFSET, &func);
+ msix_base_addr = adapter->ahw.pci_base0 + QLCNIC_MSIX_BASE;
+ msix_base = readl(msix_base_addr);
+ func = (func - msix_base)/QLCNIC_MSIX_TBL_PGSIZE;
+ adapter->ahw.pci_func = func;
+
+ /* Determine function privilege level */
+ priv_op = adapter->ahw.pci_base0 + QLCNIC_DRV_OP_MODE;
+ op_mode = readl(priv_op);
+ if (op_mode == QLC_DEV_DRV_DEFAULT) {
+ priv_level = QLCNIC_MGMT_FUNC;
+ if (qlcnic_api_lock(adapter))
+ return 0;
+ op_mode = (op_mode & ~QLC_DEV_SET_DRV(0xf, func)) |
+ (QLC_DEV_SET_DRV(QLCNIC_MGMT_FUNC, func));
+ writel(op_mode, priv_op);
+ qlcnic_api_unlock(adapter);
+
+ } else
+ priv_level = QLC_DEV_GET_DRV(op_mode, adapter->ahw.pci_func);
+
+ switch (priv_level) {
+ case QLCNIC_MGMT_FUNC:
+ adapter->op_mode = QLCNIC_MGMT_FUNC;
+ adapter->nic_ops = &qlcnic_pf_ops;
+ /* Set privilege level for other functions */
+ if (qlcnic_config_npars)
+ qlcnic_set_function_modes(adapter);
+ dev_info(&adapter->pdev->dev,
+ "HAL Version: %d, Management function\n",
+ adapter->fw_hal_version);
+ break;
+ case QLCNIC_PRIV_FUNC:
+ adapter->op_mode = QLCNIC_PRIV_FUNC;
+ dev_info(&adapter->pdev->dev,
+ "HAL Version: %d, Privileged function\n",
+ adapter->fw_hal_version);
+ adapter->nic_ops = &qlcnic_pf_ops;
+ break;
+ default:
+ dev_info(&adapter->pdev->dev, "Unknown function mode: %d\n",
+ priv_level);
+ return 0;
+ }
+ return adapter->fw_hal_version;
+}
+
static int
qlcnic_setup_pci_map(struct qlcnic_adapter *adapter)
{
@@ -460,7 +594,6 @@ qlcnic_setup_pci_map(struct qlcnic_adapter *adapter)
unsigned long mem_len, pci_len0 = 0;
struct pci_dev *pdev = adapter->pdev;
- int pci_func = adapter->ahw.pci_func;
/* remap phys address */
mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */
@@ -483,8 +616,13 @@ qlcnic_setup_pci_map(struct qlcnic_adapter *adapter)
adapter->ahw.pci_base0 = mem_ptr0;
adapter->ahw.pci_len0 = pci_len0;
+ if (!qlcnic_get_driver_mode(adapter)) {
+ iounmap(adapter->ahw.pci_base0);
+ return -EIO;
+ }
+
adapter->ahw.ocm_win_crb = qlcnic_get_ioaddr(adapter,
- QLCNIC_PCIX_PS_REG(PCIX_OCM_WINDOW_REG(pci_func)));
+ QLCNIC_PCIX_PS_REG(PCIX_OCM_WINDOW_REG(adapter->ahw.pci_func)));
return 0;
}
@@ -553,7 +691,10 @@ qlcnic_check_options(struct qlcnic_adapter *adapter)
dev_info(&pdev->dev, "firmware v%d.%d.%d\n",
fw_major, fw_minor, fw_build);
- adapter->capabilities = QLCRD32(adapter, CRB_FW_CAPABILITIES_1);
+ if (adapter->fw_hal_version == QLCNIC_FW_NPAR)
+ qlcnic_get_nic_info(adapter, adapter->ahw.pci_func);
+ else
+ adapter->capabilities = QLCRD32(adapter, CRB_FW_CAPABILITIES_1);
adapter->flags &= ~QLCNIC_LRO_ENABLED;
@@ -633,6 +774,10 @@ wait_init:
qlcnic_check_options(adapter);
+ if (adapter->fw_hal_version != QLCNIC_FW_BASE &&
+ adapter->op_mode == QLCNIC_MGMT_FUNC)
+ qlcnic_set_mgmt_driver(adapter);
+
adapter->need_fw_reset = 0;
qlcnic_release_firmware(adapter);
@@ -977,12 +1122,11 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter,
SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_ops);
- netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO);
- netdev->features |= (NETIF_F_GRO);
- netdev->vlan_features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO);
+ netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM | NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6);
- netdev->features |= (NETIF_F_IPV6_CSUM | NETIF_F_TSO6);
- netdev->vlan_features |= (NETIF_F_IPV6_CSUM | NETIF_F_TSO6);
+ netdev->vlan_features |= (NETIF_F_SG | NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6);
if (pci_using_dac) {
netdev->features |= NETIF_F_HIGHDMA;
@@ -1036,7 +1180,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
struct net_device *netdev = NULL;
struct qlcnic_adapter *adapter = NULL;
int err;
- int pci_func_id = PCI_FUNC(pdev->devfn);
uint8_t revision_id;
uint8_t pci_using_dac;
@@ -1072,7 +1215,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
adapter->netdev = netdev;
adapter->pdev = pdev;
adapter->dev_rst_time = jiffies;
- adapter->ahw.pci_func = pci_func_id;
revision_id = pdev->revision;
adapter->ahw.revision_id = revision_id;
@@ -1088,7 +1230,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_free_netdev;
/* This will be reset for mezz cards */
- adapter->portnum = pci_func_id;
+ adapter->portnum = adapter->ahw.pci_func;
err = qlcnic_get_board_info(adapter);
if (err) {
@@ -1175,6 +1317,11 @@ static void __devexit qlcnic_remove(struct pci_dev *pdev)
qlcnic_detach(adapter);
+ if (adapter->npars != NULL)
+ kfree(adapter->npars);
+ if (adapter->eswitch != NULL)
+ kfree(adapter->eswitch);
+
qlcnic_clr_all_drv_state(adapter);
clear_bit(__QLCNIC_RESETTING, &adapter->state);
@@ -1340,11 +1487,11 @@ qlcnic_tso_check(struct net_device *netdev,
u8 opcode = TX_ETHER_PKT;
__be16 protocol = skb->protocol;
u16 flags = 0, vid = 0;
- u32 producer;
int copied, offset, copy_len, hdr_len = 0, tso = 0, vlan_oob = 0;
struct cmd_desc_type0 *hwdesc;
struct vlan_ethhdr *vh;
struct qlcnic_adapter *adapter = netdev_priv(netdev);
+ u32 producer = tx_ring->producer;
if (protocol == cpu_to_be16(ETH_P_8021Q)) {
@@ -1360,6 +1507,11 @@ qlcnic_tso_check(struct net_device *netdev,
vlan_oob = 1;
}
+ if (*(skb->data) & BIT_0) {
+ flags |= BIT_0;
+ memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN);
+ }
+
if ((netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) &&
skb_shinfo(skb)->gso_size > 0) {
@@ -1409,7 +1561,6 @@ qlcnic_tso_check(struct net_device *netdev,
/* For LSO, we need to copy the MAC/IP/TCP headers into
* the descriptor ring
*/
- producer = tx_ring->producer;
copied = 0;
offset = 2;
@@ -2382,7 +2533,7 @@ qlcnic_store_bridged_mode(struct device *dev,
if (strict_strtoul(buf, 2, &new))
goto err_out;
- if (!qlcnic_config_bridged_mode(adapter, !!new))
+ if (!adapter->nic_ops->config_bridged_mode(adapter, !!new))
ret = len;
err_out:
--
1.6.0.2
^ permalink raw reply related
* Re: [patch v2] caif: cleanup: remove duplicate checks
From: Sjur Brændeland @ 2010-05-24 20:47 UTC (permalink / raw)
To: Dan Carpenter
Cc: walter harms, netdev, Sjur Braendeland, Stephen Rothwell,
kernel-janitors, David S. Miller
In-Reply-To: <20100524162815.GA22515@bicker>
Dan Carpenter <error27@gmail.com> wrote:
> "phyinfo" can never be null here because we assigned it an address, so I
> removed both the assert and the second check inside the if statement. I
> removed the "phyinfo->phy_layer != NULL" check as well because that was
> asserted earlier.
>
> Walter Harms suggested I move the "phyinfo->phy_ref_count++;" outside
> the if condition for readability, so I have done that.
>
> Signed-off-by: Dan Carpenter <error27@gmail.com>
Acked-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
Thanks, this looks good.
^ permalink raw reply
* Re: [PATCH 1/2] IPv6: keep route for tentative address
From: David Miller @ 2010-05-24 20:52 UTC (permalink / raw)
To: greg; +Cc: shemminger, emils.tantilov, netdev, emil.s.tantilov, stable
In-Reply-To: <20100524184713.GA8627@kroah.com>
From: Greg KH <greg@kroah.com>
Date: Mon, 24 May 2010 11:47:13 -0700
> On Mon, May 24, 2010 at 11:31:18AM -0700, Stephen Hemminger wrote:
>> Recent changes preserve IPv6 address when link goes down (good).
>> But would cause address to point to dead dst entry (bad).
>> The simplest fix is to just not delete route if address is
>> being held for later use.
>>
>> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
>> Signed-off-by: David S. Miller <davem@davemloft.net>
>> (cherry picked from commit 93fa159abe50d3c55c7f83622d3f5c09b6e06f4b)
>> ---
>> Patch is for 2.6.34-stable (problem doesn't exist in earlier kernel)
>
> Normally I wait for David to send networking patches for the stable
> trees, so I would like to get David's ack on these two before I am
> willing to apply them.
These two are fine to go to -stable, Greg please integrate.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox