* Re: [PATCH 0/5] Candidate fix for increased number of GFP_ATOMIC failures V2
From: Mel Gorman @ 2009-10-27 10:40 UTC (permalink / raw)
To: reinette chatre
Cc: Frans Pop, Jiri Kosina, Sven Geggus, Karol Lewandowski,
Tobias Oetiker, Rafael J. Wysocki, David Miller, Kalle Valo,
David Rientjes, KOSAKI Motohiro, Abbas, Mohamed, Jens Axboe,
John W. Linville, Pekka Enberg, Bartlomiej Zolnierkiewicz,
Greg Kroah-Hartman, Stephan von Krawczynski, Kernel Testers List,
netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-mm-Bw31MaZKKs3YtjvyW6yDsg@public.gmane.org
In-Reply-To: <1256226219.21134.1493.camel@rc-desk>
On Thu, Oct 22, 2009 at 08:43:38AM -0700, reinette chatre wrote:
> On Thu, 2009-10-22 at 07:22 -0700, Mel Gorman wrote:
> > [Bug #14141] order 2 page allocation failures in iwlagn
> > Commit 4752c93c30441f98f7ed723001b1a5e3e5619829 introduced GFP_ATOMIC
> > allocations within the wireless driver. This has caused large numbers
> > of failure reports to occur as reported by Frans Pop. Fixing this
> > requires changes to the driver if it wants to use GFP_ATOMIC which
> > is in the hands of Mohamed Abbas and Reinette Chatre. However,
> > it is very likely that it has being compounded by core mm changes
> > that this series is aimed at.
>
> Driver has been changed to allocate paged skb for its receive buffers.
> This reduces amount of memory needed from order-2 to order-1. This work
> is significant and will thus be in 2.6.33.
>
What do you want to do for -stable in 2.6.31?
--
Mel Gorman
Part-time Phd Student Linux Technology Center
University of Limerick IBM Dublin Software Lab
^ permalink raw reply
* phy_connect_direct problem
From: Kristoffer Glembo @ 2009-10-27 10:28 UTC (permalink / raw)
To: netdev
Hi,
I'm in the process of converting an ethernet driver to using the PHY
layer, but ran into a problem where my adjust_state handler was never
called and the link did not go up ...
I use phy_connect to connect the PHY device with my ethernet driver. In
phy_connect_direct I found the following code:
phy_prepare_link(phydev, handler);
phy_start_machine(phydev, NULL);
So first the adjust_state is set to handler by phy_prepare_link and then
it is overwritten with the NULL parameter passed to phy_start_machine.
Maybe I'm abusing the PHY layer somehow since there are plenty of other
drivers using this interface. However the patch below solved the issue
for me.
Best regards,
Kristoffer Glembo
---
drivers/net/phy/phy_device.c | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index b10fedd..d27ca80 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -311,8 +311,7 @@ int phy_connect_direct(struct net_device *dev,
struct phy_device *phydev,
if (rc)
return rc;
- phy_prepare_link(phydev, handler);
- phy_start_machine(phydev, NULL);
+ phy_start_machine(phydev, handler);
if (phydev->irq > 0)
phy_start_interrupts(phydev);
--
1.5.2.2
^ permalink raw reply related
* netlink: symantics for NLM_F_MULTI and NLMSG_DONE ?
From: Roger Willcocks @ 2009-10-27 11:43 UTC (permalink / raw)
To: netdev
The netlink rfc (3549) says that in multi-part messages (messages split
over multiple netlink packets), the first and all following headers
have the NLM_F_MULTI flag set except for the last header which has the
header type NLMSG_DONE.
afaict NLMSG_DONE is redundant, since one could simply not set the
NLM_F_MULTI flag for the final header; and if a multi-part message
can't cross a datagram boundary - which is assumed by e.g. the reading
example in
http://www.kernel.org/doc/man-pages/online/pages/man7/netlink.7.html
- NLMSG_DONE is redundant too.
And the rfc doesn't say what NLMSG_DONE /and/ NLM_F_MULTI should mean,
although net/netlink/af_netlink.c always sets both.
So, what are the symantics for NLM_F_MULTI and NLMSG_DONE?
^ permalink raw reply
* [net-next-2.6 v4 0/4] TCPCT part1: cookie option exchange
From: William Allen Simpson @ 2009-10-27 12:11 UTC (permalink / raw)
To: Linux Kernel Network Developers
Updated to patch cleanly with recent commits.
Changes from previous version:
* Strongly type otherwise void parameter with empty struct.
* Combined two shorter patches (b + c), dropping update of 2 drivers,
as nobody has gotten back to me about testing results.
* Added a little Documentation for new sysctl.
^ permalink raw reply
* [net-next-2.6 PATCH v4 1/3] TCPCT part 1a: add request_values parameter for sending SYNACK
From: William Allen Simpson @ 2009-10-27 12:15 UTC (permalink / raw)
To: Linux Kernel Network Developers
In-Reply-To: <4AE6E35C.2050101@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 933 bytes --]
Add optional function parameters associated with sending SYNACK.
These parameters are not needed after sending SYNACK, and are not
used for retransmission. Avoids extending struct tcp_request_sock,
and avoids allocating kernel memory.
Also affects DCCP as it uses common struct request_sock_ops,
but this parameter is currently reserved for future use.
Signed-off-by: William.Allen.Simpson@gmail.com
---
include/net/request_sock.h | 8 +++++++-
include/net/tcp.h | 1 +
net/dccp/ipv4.c | 5 +++--
net/dccp/ipv6.c | 5 +++--
net/dccp/minisocks.c | 2 +-
net/ipv4/inet_connection_sock.c | 2 +-
net/ipv4/tcp_ipv4.c | 11 +++++++----
net/ipv4/tcp_minisocks.c | 2 +-
net/ipv4/tcp_output.c | 1 +
net/ipv6/tcp_ipv6.c | 14 +++++++-------
10 files changed, 32 insertions(+), 19 deletions(-)
[-- Attachment #2: TCPCT+1a4.patch --]
[-- Type: text/plain, Size: 7357 bytes --]
diff --git a/include/net/request_sock.h b/include/net/request_sock.h
index c719084..c9b50eb 100644
--- a/include/net/request_sock.h
+++ b/include/net/request_sock.h
@@ -27,13 +27,19 @@ struct sk_buff;
struct dst_entry;
struct proto;
+/* empty to "strongly type" an otherwise void parameter.
+ */
+struct request_values {
+};
+
struct request_sock_ops {
int family;
int obj_size;
struct kmem_cache *slab;
char *slab_name;
int (*rtx_syn_ack)(struct sock *sk,
- struct request_sock *req);
+ struct request_sock *req,
+ struct request_values *rvp);
void (*send_ack)(struct sock *sk, struct sk_buff *skb,
struct request_sock *req);
void (*send_reset)(struct sock *sk,
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 03a49c7..4abdb4d 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -443,6 +443,7 @@ extern int tcp_connect(struct sock *sk);
extern struct sk_buff * tcp_make_synack(struct sock *sk,
struct dst_entry *dst,
+ struct request_values *rvp,
struct request_sock *req);
extern int tcp_disconnect(struct sock *sk, int flags);
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 00028d4..8d4a88a 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -477,7 +477,8 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk,
return &rt->u.dst;
}
-static int dccp_v4_send_response(struct sock *sk, struct request_sock *req)
+static int dccp_v4_send_response(struct sock *sk, struct request_sock *req,
+ struct request_values *rv_unused)
{
int err = -1;
struct sk_buff *skb;
@@ -626,7 +627,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
dreq->dreq_iss = dccp_v4_init_sequence(skb);
dreq->dreq_service = service;
- if (dccp_v4_send_response(sk, req))
+ if (dccp_v4_send_response(sk, req, NULL))
goto drop_and_free;
inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 6d89f9f..f64981e 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -241,7 +241,8 @@ out:
}
-static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
+static int dccp_v6_send_response(struct sock *sk, struct request_sock *req,
+ struct request_values *rv_unused)
{
struct inet6_request_sock *ireq6 = inet6_rsk(req);
struct ipv6_pinfo *np = inet6_sk(sk);
@@ -468,7 +469,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
dreq->dreq_iss = dccp_v6_init_sequence(skb);
dreq->dreq_service = service;
- if (dccp_v6_send_response(sk, req))
+ if (dccp_v6_send_response(sk, req, NULL))
goto drop_and_free;
inet6_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index 5ca49ce..af226a0 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -184,7 +184,7 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
* counter (backoff, monitored by dccp_response_timer).
*/
req->retrans++;
- req->rsk_ops->rtx_syn_ack(sk, req);
+ req->rsk_ops->rtx_syn_ack(sk, req, NULL);
}
/* Network Duplicate, discard packet */
return NULL;
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index f6a0af7..b81d3c9 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -504,7 +504,7 @@ void inet_csk_reqsk_queue_prune(struct sock *parent,
if (time_after_eq(now, req->expires)) {
if ((req->retrans < thresh ||
(inet_rsk(req)->acked && req->retrans < max_retries))
- && !req->rsk_ops->rtx_syn_ack(parent, req)) {
+ && !req->rsk_ops->rtx_syn_ack(parent, req, NULL)) {
unsigned long timeo;
if (req->retrans++ == 0)
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index a4a3390..6edc5e2 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -743,6 +743,7 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
* socket.
*/
static int __tcp_v4_send_synack(struct sock *sk, struct request_sock *req,
+ struct request_values *rvp,
struct dst_entry *dst)
{
const struct inet_request_sock *ireq = inet_rsk(req);
@@ -753,7 +754,7 @@ static int __tcp_v4_send_synack(struct sock *sk, struct request_sock *req,
if (!dst && (dst = inet_csk_route_req(sk, req)) == NULL)
return -1;
- skb = tcp_make_synack(sk, dst, req);
+ skb = tcp_make_synack(sk, dst, rvp, req);
if (skb) {
struct tcphdr *th = tcp_hdr(skb);
@@ -774,9 +775,10 @@ static int __tcp_v4_send_synack(struct sock *sk, struct request_sock *req,
return err;
}
-static int tcp_v4_send_synack(struct sock *sk, struct request_sock *req)
+static int tcp_v4_send_synack(struct sock *sk, struct request_sock *req,
+ struct request_values *rvp)
{
- return __tcp_v4_send_synack(sk, req, NULL);
+ return __tcp_v4_send_synack(sk, req, rvp, NULL);
}
/*
@@ -1334,7 +1336,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
}
tcp_rsk(req)->snt_isn = isn;
- if (__tcp_v4_send_synack(sk, req, dst) || want_cookie)
+ if (__tcp_v4_send_synack(sk, req, NULL, dst) ||
+ want_cookie)
goto drop_and_free;
inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index e320afe..8819882 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -537,7 +537,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
* Enforce "SYN-ACK" according to figure 8, figure 6
* of RFC793, fixed by RFC1122.
*/
- req->rsk_ops->rtx_syn_ack(sk, req);
+ req->rsk_ops->rtx_syn_ack(sk, req, NULL);
return NULL;
}
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 2e2eb74..79042f4 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2219,6 +2219,7 @@ int tcp_send_synack(struct sock *sk)
/* Prepare a SYN-ACK. */
struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
+ struct request_values *rvp,
struct request_sock *req)
{
struct inet_request_sock *ireq = inet_rsk(req);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index c54ec36..3960d72 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -461,7 +461,8 @@ out:
}
-static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req)
+static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
+ struct request_values *rvp)
{
struct inet6_request_sock *treq = inet6_rsk(req);
struct ipv6_pinfo *np = inet6_sk(sk);
@@ -499,7 +500,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req)
if ((err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0)
goto done;
- skb = tcp_make_synack(sk, dst, req);
+ skb = tcp_make_synack(sk, dst, rvp, req);
if (skb) {
struct tcphdr *th = tcp_hdr(skb);
@@ -1243,13 +1244,12 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
security_inet_conn_request(sk, skb, req);
- if (tcp_v6_send_synack(sk, req))
+ if (tcp_v6_send_synack(sk, req, NULL) ||
+ want_cookie)
goto drop;
- if (!want_cookie) {
- inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
- return 0;
- }
+ inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+ return 0;
drop:
if (req)
--
1.6.0.4
^ permalink raw reply related
* [net-next-2.6 PATCH v4 2/3] TCPCT part 1b: sysctl_tcp_cookie_size, socket option TCP_COOKIE_TRANSACTIONS, functions
From: William Allen Simpson @ 2009-10-27 12:18 UTC (permalink / raw)
To: Linux Kernel Network Developers
In-Reply-To: <4AE6E35C.2050101@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1294 bytes --]
Define sysctl (tcp_cookie_size) to turn on and off the cookie option
default globally, instead of a compiled configuration option.
Define per socket option (TCP_COOKIE_TRANSACTIONS) for setting constant
data values, retrieving variable cookie values, and other facilities.
Redefine two TCP header functions to accept TCP header pointer.
When subtracting, return signed int to allow error checking.
Move inline tcp_clear_options() unchanged from net/tcp.h to linux/tcp.h,
near its corresponding struct tcp_options_received (prior to changes).
This is a straightforward re-implementation of an earlier (year-old)
patch that no longer applies cleanly, with permission of the original
author (Adam Langley). The patch was previously reviewed:
http://thread.gmane.org/gmane.linux.network/102586
These functions will also be used in subsequent patches that implement
additional features.
Signed-off-by: William.Allen.Simpson@gmail.com
---
Documentation/networking/ip-sysctl.txt | 8 +++++
include/linux/tcp.h | 47 +++++++++++++++++++++++++++++++-
include/net/tcp.h | 6 +---
net/ipv4/sysctl_net_ipv4.c | 8 +++++
net/ipv4/tcp_output.c | 8 +++++
5 files changed, 71 insertions(+), 6 deletions(-)
[-- Attachment #2: TCPCT+1b4.patch --]
[-- Type: text/plain, Size: 5734 bytes --]
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index a0e134d..d47c000 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -164,6 +164,14 @@ tcp_congestion_control - STRING
additional choices may be available based on kernel configuration.
Default is set as part of kernel configuration.
+tcp_cookie_size - INTEGER
+ Default size of TCP Cookie Transactions (TCPCT) option, that may be
+ overridden on a per socket basis by the TCPCT socket option.
+ Values greater than the maximum (16) are interpreted as the maximum.
+ Values greater than zero and less than the minimum (8) are interpreted
+ as the minimum.
+ Default: 0 (off).
+
tcp_dsack - BOOLEAN
Allows TCP to send "duplicate" SACKs.
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 61723a7..6fd59d1 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -96,6 +96,7 @@ enum {
#define TCP_QUICKACK 12 /* Block/reenable quick acks */
#define TCP_CONGESTION 13 /* Congestion control algorithm */
#define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */
+#define TCP_COOKIE_TRANSACTIONS 15 /* TCP Cookie Transactions */
#define TCPI_OPT_TIMESTAMPS 1
#define TCPI_OPT_SACK 2
@@ -170,6 +171,34 @@ struct tcp_md5sig {
__u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */
};
+/* for TCP_COOKIE_TRANSACTIONS (TCPCT) socket option */
+#define TCP_COOKIE_MAX 16 /* 128-bits */
+#define TCP_COOKIE_MIN 8 /* 64-bits */
+#define TCP_COOKIE_PAIR_SIZE (2*TCP_COOKIE_MAX)
+
+#define TCP_S_DATA_MAX 64U /* after TCP+IP options */
+#define TCP_S_DATA_MSS_DEFAULT 536U /* default MSS (RFC1122) */
+
+/* Flags for both getsockopt and setsockopt */
+#define TCP_COOKIE_IN_ALWAYS (1 << 0) /* Discard SYN without cookie */
+#define TCP_COOKIE_OUT_NEVER (1 << 1) /* Prohibit outgoing cookies,
+ * supercedes everything else. */
+#define TCP_EXTEND_TIMESTAMP (1 << 4) /* Initiate 64-bit timestamps */
+
+/* Flags for getsockopt */
+#define TCP_S_DATA_IN (1 << 2) /* Was data received? */
+#define TCP_S_DATA_OUT (1 << 3) /* Was data sent? */
+
+/* TCP_COOKIE_TRANSACTIONS data */
+struct tcp_cookie_transactions {
+ __u16 tcpct_flags; /* see above */
+ __u8 __tcpct_pad1; /* zero */
+ __u8 tcpct_cookie_desired; /* bytes */
+ __u16 tcpct_s_data_desired; /* bytes of variable data */
+ __u16 tcpct_used; /* bytes in value */
+ __u8 tcpct_value[TCP_S_DATA_MSS_DEFAULT];
+};
+
#ifdef __KERNEL__
#include <linux/skbuff.h>
@@ -193,6 +222,17 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb)
return (tcp_hdr(skb)->doff - 5) * 4;
}
+static inline unsigned int tcp_header_len_th(const struct tcphdr *th)
+{
+ return th->doff * 4;
+}
+
+/* When doff is bad, this could be negative. */
+static inline int tcp_option_len_th(const struct tcphdr *th)
+{
+ return (int)(th->doff * 4) - sizeof(*th);
+}
+
/* This defines a selective acknowledgement block. */
struct tcp_sack_block_wire {
__be32 start_seq;
@@ -223,6 +263,11 @@ struct tcp_options_received {
u16 mss_clamp; /* Maximal mss, negotiated at connection setup */
};
+static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
+{
+ rx_opt->tstamp_ok = rx_opt->sack_ok = rx_opt->wscale_ok = rx_opt->snd_wscale = 0;
+}
+
/* This is the max number of SACKS that we'll generate and process. It's safe
* to increse this, although since:
* size = TCPOLEN_SACK_BASE_ALIGNED (4) + n * TCPOLEN_SACK_PERBLOCK (8)
@@ -431,6 +476,6 @@ static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk)
return (struct tcp_timewait_sock *)sk;
}
-#endif
+#endif /* __KERNEL__ */
#endif /* _LINUX_TCP_H */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 4abdb4d..142f32e 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -237,6 +237,7 @@ extern int sysctl_tcp_base_mss;
extern int sysctl_tcp_workaround_signed_windows;
extern int sysctl_tcp_slow_start_after_idle;
extern int sysctl_tcp_max_ssthresh;
+extern int sysctl_tcp_cookie_size;
extern atomic_t tcp_memory_allocated;
extern struct percpu_counter tcp_sockets_allocated;
@@ -343,11 +344,6 @@ static inline void tcp_dec_quickack_mode(struct sock *sk,
extern void tcp_enter_quickack_mode(struct sock *sk);
-static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
-{
- rx_opt->tstamp_ok = rx_opt->sack_ok = rx_opt->wscale_ok = rx_opt->snd_wscale = 0;
-}
-
#define TCP_ECN_OK 1
#define TCP_ECN_QUEUE_CWR 2
#define TCP_ECN_DEMAND_CWR 4
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 2dcf04d..3422c54 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -714,6 +714,14 @@ static struct ctl_table ipv4_table[] = {
},
{
.ctl_name = CTL_UNNUMBERED,
+ .procname = "tcp_cookie_size",
+ .data = &sysctl_tcp_cookie_size,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {
+ .ctl_name = CTL_UNNUMBERED,
.procname = "udp_mem",
.data = &sysctl_udp_mem,
.maxlen = sizeof(sysctl_udp_mem),
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 79042f4..bcfbe41 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -59,6 +59,14 @@ int sysctl_tcp_base_mss __read_mostly = 512;
/* By default, RFC2861 behavior. */
int sysctl_tcp_slow_start_after_idle __read_mostly = 1;
+#ifdef CONFIG_SYSCTL
+/* By default, let the user enable it. */
+int sysctl_tcp_cookie_size __read_mostly = 0;
+#else
+int sysctl_tcp_cookie_size __read_mostly = TCP_COOKIE_MAX;
+#endif
+
+
/* Account for new data that has been sent to the network. */
static void tcp_event_new_data_sent(struct sock *sk, struct sk_buff *skb)
{
--
1.6.0.4
^ permalink raw reply related
* Re: [PATCH 1/5] page allocator: Always wake kswapd when restarting an allocation attempt after direct reclaim failed
From: Mel Gorman @ 2009-10-27 12:27 UTC (permalink / raw)
To: KOSAKI Motohiro
Cc: David Rientjes, Frans Pop, Jiri Kosina, Sven Geggus,
Karol Lewandowski, Tobias Oetiker, Rafael J. Wysocki,
David Miller, Reinette Chatre, Kalle Valo, Mohamed Abbas,
Jens Axboe, John W. Linville, Pekka Enberg,
Bartlomiej Zolnierkiewicz, Greg Kroah-Hartman,
Stephan von Krawczynski, Kernel Testers List, netdev,
linux-kernel, linux-mm@kvack.org
In-Reply-To: <20091026222159.2F72.A69D9226@jp.fujitsu.com>
On Tue, Oct 27, 2009 at 11:42:55AM +0900, KOSAKI Motohiro wrote:
> > On Mon, 26 Oct 2009, KOSAKI Motohiro wrote:
> >
> > > diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> > > index bf72055..5a27896 100644
> > > --- a/mm/page_alloc.c
> > > +++ b/mm/page_alloc.c
> > > @@ -1899,6 +1899,12 @@ rebalance:
> > > if (should_alloc_retry(gfp_mask, order, pages_reclaimed)) {
> > > /* Wait for some write requests to complete then retry */
> > > congestion_wait(BLK_RW_ASYNC, HZ/50);
> > > +
> > > + /*
> > > + * While we wait congestion wait, Amount of free memory can
> > > + * be changed dramatically. Thus, we kick kswapd again.
> > > + */
> > > + wake_all_kswapd(order, zonelist, high_zoneidx);
> > > goto rebalance;
> > > }
> > >
> >
> > We're blocking to finish writeback of the directly reclaimed memory, why
> > do we need to wake kswapd afterwards?
>
> the same reason of "goto restart" case. that's my intention.
> if following scenario occur, it is equivalent that we didn't call wake_all_kswapd().
>
> 1. call congestion_wait()
> 2. kswapd reclaimed lots memory and sleep
> 3. another task consume lots memory
> 4. wakeup from congestion_wait()
>
> IOW, if we falled into __alloc_pages_slowpath(), we naturally expect
> next page_alloc() don't fall into slowpath. however if kswapd end to
> its work too early, this assumption isn't true.
>
> Is this too pessimistic assumption?
>
hmm.
The reason it's not woken in both cases a second time was to match the
behaviour of 2.6.30. If the direct reclaimer goes asleep and another task
consumes the memory the direct reclaimer freed then the greedy process should
kick kswapd back awake again as free memory goes below the low watermark.
However, if the greedy process was allocating order-0, it's possible that
the watermarks for order-0 are being met leaving kswapd alone where as the
high-order ones are not leaving kswapd to go back asleep or to reclaim at
the wrong order.
It's a functional change but I can add it to the list of things to
consider. Thanks
--
Mel Gorman
Part-time Phd Student Linux Technology Center
University of Limerick IBM Dublin Software Lab
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* [net-next-2.6 PATCH v4 3/3] TCPCT part 1c: initial SYN exchange with SYNACK data
From: William Allen Simpson @ 2009-10-27 12:29 UTC (permalink / raw)
To: Linux Kernel Network Developers
In-Reply-To: <4AE6E35C.2050101@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2272 bytes --]
This is a significantly revised implementation of an earlier (year-old)
patch that no longer applies cleanly, with permission of the original
author (Adam Langley). That patch was previously reviewed:
http://thread.gmane.org/gmane.linux.network/102586
The principle difference is using a TCP option to carry the cookie nonce,
instead of a user configured offset in the data. This is more flexible and
less subject to user configuration error. Such a cookie option has been
suggested for many years, and is also useful without SYN data, allowing
several related concepts to use the same extension option.
"Re: SYN floods (was: does history repeat itself?)", September 9, 1996.
http://www.merit.net/mail.archives/nanog/1996-09/msg00235.html
"Re: what a new TCP header might look like", May 12, 1998.
ftp://ftp.isi.edu/end2end/end2end-interest-1998.mail
Data structures are carefully composed to require minimal additions.
For example, the struct tcp_options_received cookie_plus variable fits
between existing 16-bit and 8-bit variables, requiring no additional
space (taking alignment into consideration). There are no additions to
tcp_request_sock, and only 1 pointer and 1 flag byte in tcp_sock.
Allocations have been rearranged to avoid requiring GFP_ATOMIC, with
only one unavoidable exception in tcp_create_openreq_child(), where the
tcp_sock itself is created GFP_ATOMIC.
These functions will also be used in subsequent patches that implement
additional features.
Requires:
TCPCT part 1a: add request_values parameter for sending SYNACK
TCPCT part 1b: sysctl_tcp_cookie_size, socket option TCP_COOKIE_TRANSACTIONS, functions
Signed-off-by: William.Allen.Simpson@gmail.com
---
include/linux/tcp.h | 34 ++++++-
include/net/tcp.h | 67 +++++++++++++-
net/ipv4/syncookies.c | 5 +-
net/ipv4/tcp.c | 128 +++++++++++++++++++++++++-
net/ipv4/tcp_input.c | 84 +++++++++++++++--
net/ipv4/tcp_ipv4.c | 62 +++++++++++--
net/ipv4/tcp_minisocks.c | 43 +++++++--
net/ipv4/tcp_output.c | 227 +++++++++++++++++++++++++++++++++++++++++++---
net/ipv6/syncookies.c | 5 +-
net/ipv6/tcp_ipv6.c | 47 +++++++++-
10 files changed, 639 insertions(+), 63 deletions(-)
[-- Attachment #2: TCPCT+1c4.patch --]
[-- Type: text/plain, Size: 39173 bytes --]
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 6fd59d1..5da3ef4 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -257,31 +257,36 @@ struct tcp_options_received {
sack_ok : 4, /* SACK seen on SYN packet */
snd_wscale : 4, /* Window scaling received from sender */
rcv_wscale : 4; /* Window scaling to send to receiver */
-/* SACKs data */
+ u8 cookie_plus:6; /* bytes in authenticator/cookie option */
u8 num_sacks; /* Number of SACK blocks */
- u16 user_mss; /* mss requested by user in ioctl */
+ u16 user_mss; /* mss requested by user in ioctl */
u16 mss_clamp; /* Maximal mss, negotiated at connection setup */
};
static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
{
- rx_opt->tstamp_ok = rx_opt->sack_ok = rx_opt->wscale_ok = rx_opt->snd_wscale = 0;
+ rx_opt->tstamp_ok = rx_opt->sack_ok = 0;
+ rx_opt->wscale_ok = rx_opt->snd_wscale = 0;
+ rx_opt->cookie_plus = 0;
}
/* This is the max number of SACKS that we'll generate and process. It's safe
- * to increse this, although since:
+ * to increase this, although since:
* size = TCPOLEN_SACK_BASE_ALIGNED (4) + n * TCPOLEN_SACK_PERBLOCK (8)
* only four options will fit in a standard TCP header */
#define TCP_NUM_SACKS 4
+struct tcp_cookie_values;
+struct tcp_request_sock_ops;
+
struct tcp_request_sock {
struct inet_request_sock req;
#ifdef CONFIG_TCP_MD5SIG
/* Only used by TCP MD5 Signature so far. */
const struct tcp_request_sock_ops *af_specific;
#endif
- u32 rcv_isn;
- u32 snt_isn;
+ u32 rcv_isn;
+ u32 snt_isn;
};
static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req)
@@ -451,6 +456,19 @@ struct tcp_sock {
/* TCP MD5 Signature Option information */
struct tcp_md5sig_info *md5sig_info;
#endif
+
+ /* When the cookie options are generated and exchanged, then this
+ * object holds a reference to them (cookie_values->kref). Also
+ * contains related tcp_cookie_transactions fields.
+ */
+ struct tcp_cookie_values *cookie_values;
+
+ u8 cookie_in_always:1,
+ cookie_out_never:1,
+ extend_timestamp:1,
+ s_data_constant:1,
+ s_data_in:1,
+ s_data_out:1;
};
static inline struct tcp_sock *tcp_sk(const struct sock *sk)
@@ -469,6 +487,10 @@ struct tcp_timewait_sock {
u16 tw_md5_keylen;
u8 tw_md5_key[TCP_MD5SIG_MAXKEYLEN];
#endif
+ /* Few sockets in timewait have cookies; in that case, then this
+ * object holds a reference to it (tw_cookie_values->kref)
+ */
+ struct tcp_cookie_values *tw_cookie_values;
};
static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 142f32e..51b7426 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -30,6 +30,7 @@
#include <linux/dmaengine.h>
#include <linux/crypto.h>
#include <linux/cryptohash.h>
+#include <linux/kref.h>
#include <net/inet_connection_sock.h>
#include <net/inet_timewait_sock.h>
@@ -167,6 +168,7 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
#define TCPOPT_SACK 5 /* SACK Block */
#define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */
#define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */
+#define TCPOPT_COOKIE 253 /* Cookie extension (experimental) */
/*
* TCP option lengths
@@ -177,6 +179,10 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
#define TCPOLEN_SACK_PERM 2
#define TCPOLEN_TIMESTAMP 10
#define TCPOLEN_MD5SIG 18
+#define TCPOLEN_COOKIE_BASE 2 /* Cookie-less header extension */
+#define TCPOLEN_COOKIE_PAIR 3 /* Cookie pair header extension */
+#define TCPOLEN_COOKIE_MAX (TCPOLEN_COOKIE_BASE+TCP_COOKIE_MAX)
+#define TCPOLEN_COOKIE_MIN (TCPOLEN_COOKIE_BASE+TCP_COOKIE_MIN)
/* But this is what stacks really send out. */
#define TCPOLEN_TSTAMP_ALIGNED 12
@@ -405,7 +411,7 @@ extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk,
extern void tcp_parse_options(struct sk_buff *skb,
struct tcp_options_received *opt_rx,
- int estab);
+ u8 **cryptic, int estab);
extern u8 *tcp_parse_md5sig_option(struct tcphdr *th);
@@ -1477,6 +1483,65 @@ struct tcp_request_sock_ops {
#endif
};
+/**
+ * A tcp_sock contains a pointer to the current value, and this is cloned to
+ * the tcp_timewait_sock.
+ *
+ * @cookie_pair: variable data from the option exchange.
+ *
+ * @cookie_desired: user specified tcpct_cookie_desired. Zero
+ * indicates default (sysctl_tcp_cookie_size).
+ * After cookie sent, remembers size of cookie.
+ *
+ * @s_data_desired: user specified tcpct_s_data_desired. When the
+ * constant payload is specified (s_data_constant),
+ * holds its length instead.
+ *
+ * @s_data_payload: constant data that is to be included in the
+ * payload of SYN or SYNACK segments when the
+ * cookie option is present.
+ */
+struct tcp_cookie_values {
+ struct kref kref;
+ u8 cookie_pair[TCP_COOKIE_PAIR_SIZE];
+ u8 cookie_pair_size;
+ u8 cookie_desired;
+ u16 s_data_desired;
+ u8 s_data_payload[0];
+};
+
+static inline void tcp_cookie_values_release(struct kref *kref)
+{
+ kfree(container_of(kref, struct tcp_cookie_values, kref));
+}
+
+/* The length of constant payload data. Note that s_data_desired is
+ * overloaded, depending on s_data_constant: either the length of constant
+ * data (returned here) or the limit on variable data.
+ */
+static inline int tcp_s_data_size(const struct tcp_sock *tp)
+{
+ return (NULL != tp->cookie_values && tp->s_data_constant)
+ ? tp->cookie_values->s_data_desired
+ : 0;
+}
+
+/* As tcp_request_sock has already been extended in other places, the
+ * only remaining method is to pass stack values along as function
+ * parameters. These parameters are not needed after sending SYNACK.
+ */
+struct tcp_extend_values {
+ u8 cookie_bakery[TCP_COOKIE_MAX];
+ u8 cookie_plus;
+ u8 cookie_in_always:1,
+ cookie_out_never:1;
+};
+
+static inline struct tcp_extend_values *tcp_xv(const struct request_values *rvp)
+{
+ return (struct tcp_extend_values *)rvp;
+}
+
extern void tcp_v4_init(void);
extern void tcp_init(void);
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 5ec678a..cdab491 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -253,6 +253,8 @@ EXPORT_SYMBOL(cookie_check_timestamp);
struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
struct ip_options *opt)
{
+ struct tcp_options_received tcp_opt;
+ u8 *cryptic_value;
struct inet_request_sock *ireq;
struct tcp_request_sock *treq;
struct tcp_sock *tp = tcp_sk(sk);
@@ -263,7 +265,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
int mss;
struct rtable *rt;
__u8 rcv_wscale;
- struct tcp_options_received tcp_opt;
if (!sysctl_tcp_syncookies || !th->ack)
goto out;
@@ -278,7 +279,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
/* check for timestamp cookie support */
memset(&tcp_opt, 0, sizeof(tcp_opt));
- tcp_parse_options(skb, &tcp_opt, 0);
+ tcp_parse_options(skb, &tcp_opt, &cryptic_value, 0);
if (tcp_opt.saw_tstamp)
cookie_check_timestamp(&tcp_opt);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 206a291..12409df 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2039,8 +2039,8 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
int val;
int err = 0;
- /* This is a string value all the others are int's */
- if (optname == TCP_CONGESTION) {
+ /* These are data/string values, all the others are ints */
+ if (TCP_CONGESTION == optname) {
char name[TCP_CA_NAME_MAX];
if (optlen < 1)
@@ -2056,6 +2056,92 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
err = tcp_set_congestion_control(sk, name);
release_sock(sk);
return err;
+ } else if (TCP_COOKIE_TRANSACTIONS == optname) {
+ struct tcp_cookie_transactions ctd;
+ struct tcp_cookie_values *cvp = NULL;
+
+ if (sizeof(ctd) > optlen)
+ return -EINVAL;
+ if (copy_from_user(&ctd, optval, sizeof(ctd)))
+ return -EFAULT;
+ if (sizeof(ctd.tcpct_value) < ctd.tcpct_used)
+ return -EINVAL;
+
+ if (0 == ctd.tcpct_cookie_desired) {
+ /* default to global value */
+ } else if ((0x1 & ctd.tcpct_cookie_desired)
+ || TCP_COOKIE_MAX < ctd.tcpct_cookie_desired
+ || TCP_COOKIE_MIN > ctd.tcpct_cookie_desired) {
+ return -EINVAL;
+ }
+
+ if (TCP_COOKIE_OUT_NEVER & ctd.tcpct_flags) {
+ /* Supercedes all other values */
+ lock_sock(sk);
+ if (NULL != tp->cookie_values) {
+ kref_put(&tp->cookie_values->kref,
+ tcp_cookie_values_release);
+ tp->cookie_values = NULL;
+ }
+ tp->cookie_in_always = 0; /* false */
+ tp->cookie_out_never = 1; /* true */
+ tp->extend_timestamp = 0; /* false */
+ tp->s_data_constant = 0; /* false */
+ tp->s_data_in = 0; /* false */
+ tp->s_data_out = 0; /* false */
+ release_sock(sk);
+ return err;
+ }
+
+ /* Allocate ancillary memory before locking.
+ */
+ if (0 < ctd.tcpct_used
+ || (NULL == tp->cookie_values
+ && (0 < sysctl_tcp_cookie_size
+ || 0 < ctd.tcpct_cookie_desired
+ || 0 < ctd.tcpct_s_data_desired))) {
+ cvp = kmalloc(sizeof(*cvp) + ctd.tcpct_used,
+ GFP_KERNEL);
+ if (NULL == cvp)
+ return -ENOMEM;
+ }
+
+ lock_sock(sk);
+ tp->cookie_in_always = (TCP_COOKIE_IN_ALWAYS & ctd.tcpct_flags);
+ tp->cookie_out_never = 0; /* false */
+ tp->extend_timestamp = (TCP_EXTEND_TIMESTAMP & ctd.tcpct_flags);
+ tp->s_data_in = 0; /* false */
+ tp->s_data_out = 0; /* false */
+
+ if (NULL == cvp) {
+ /* No cookies by default. */
+ tp->s_data_constant = 0; /* false */
+ } else if (0 == ctd.tcpct_used) {
+ /* No constant payload data. */
+ cvp->cookie_desired = ctd.tcpct_cookie_desired;
+ cvp->s_data_desired = ctd.tcpct_s_data_desired;
+ tp->cookie_values = cvp;
+ tp->s_data_constant = 0; /* false */
+ } else {
+ /* Changes in values are recorded by a change in
+ * pointer, ensuring that the cookie will differ,
+ * without separately hashing each value later.
+ */
+ if (unlikely(NULL != tp->cookie_values)) {
+ kref_put(&tp->cookie_values->kref,
+ tcp_cookie_values_release);
+ }
+ kref_init(&cvp->kref);
+ memcpy(cvp->s_data_payload, ctd.tcpct_value,
+ ctd.tcpct_used);
+ cvp->cookie_desired = ctd.tcpct_cookie_desired;
+ cvp->s_data_desired = ctd.tcpct_used;
+ tp->cookie_values = cvp;
+ tp->s_data_constant = 1; /* true */
+ }
+
+ release_sock(sk);
+ return err;
}
if (optlen < sizeof(int))
@@ -2387,6 +2473,44 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
return -EFAULT;
return 0;
+
+ case TCP_COOKIE_TRANSACTIONS: {
+ struct tcp_cookie_transactions ctd;
+ struct tcp_cookie_values *cvp = tp->cookie_values;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+ if (len < sizeof(ctd))
+ return -EINVAL;
+
+ memset(&ctd, 0, sizeof(ctd));
+ ctd.tcpct_flags =
+ (tp->cookie_in_always ? TCP_COOKIE_IN_ALWAYS : 0)
+ | (tp->cookie_out_never ? TCP_COOKIE_OUT_NEVER : 0)
+ | (tp->extend_timestamp ? TCP_EXTEND_TIMESTAMP : 0)
+ | (tp->s_data_in ? TCP_S_DATA_IN : 0)
+ | (tp->s_data_out ? TCP_S_DATA_OUT : 0);
+
+ if (NULL != cvp) {
+ /* Cookie(s) saved, return as nonce */
+ if (sizeof(ctd.tcpct_value) < cvp->cookie_pair_size) {
+ /* impossible? */
+ return -EINVAL;
+ }
+ memcpy(&ctd.tcpct_value[0], &cvp->cookie_pair[0],
+ cvp->cookie_pair_size);
+ ctd.tcpct_used = cvp->cookie_pair_size;
+
+ ctd.tcpct_cookie_desired = cvp->cookie_desired;
+ ctd.tcpct_s_data_desired = cvp->s_data_desired;
+ }
+
+ if (put_user(sizeof(ctd), optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &ctd, sizeof(ctd)))
+ return -EFAULT;
+ return 0;
+ }
default:
return -ENOPROTOOPT;
}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index d86784b..b2a2da1 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3698,11 +3698,11 @@ old_ack:
* the fast version below fails.
*/
void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx,
- int estab)
+ u8 **cryptic, int estab)
{
unsigned char *ptr;
struct tcphdr *th = tcp_hdr(skb);
- int length = (th->doff * 4) - sizeof(struct tcphdr);
+ int length = tcp_option_len_th(th);
ptr = (unsigned char *)(th + 1);
opt_rx->saw_tstamp = 0;
@@ -3782,6 +3782,19 @@ void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx,
*/
break;
#endif
+ case TCPOPT_COOKIE:
+ /* This option carries 3 different lengths.
+ */
+ if (TCPOLEN_COOKIE_MAX >= opsize
+ && TCPOLEN_COOKIE_MIN <= opsize) {
+ opt_rx->cookie_plus = opsize;
+ *cryptic = ptr;
+ } else if (TCPOLEN_COOKIE_PAIR == opsize) {
+ /* not yet implemented */
+ } else if (TCPOLEN_COOKIE_BASE == opsize) {
+ /* not yet implemented */
+ }
+ break;
}
ptr += opsize-2;
@@ -3810,17 +3823,21 @@ static int tcp_parse_aligned_timestamp(struct tcp_sock *tp, struct tcphdr *th)
* If it is wrong it falls back on tcp_parse_options().
*/
static int tcp_fast_parse_options(struct sk_buff *skb, struct tcphdr *th,
- struct tcp_sock *tp)
+ struct tcp_sock *tp, u8 **cryptic)
{
- if (th->doff == sizeof(struct tcphdr) >> 2) {
+ /* In the spirit of fast parsing, compare doff directly to shifted
+ * constant values. Because equality is used, short doff can be
+ * ignored here, and checked later.
+ */
+ if ((sizeof(*th) >> 2) == th->doff) {
tp->rx_opt.saw_tstamp = 0;
return 0;
} else if (tp->rx_opt.tstamp_ok &&
- th->doff == (sizeof(struct tcphdr)>>2)+(TCPOLEN_TSTAMP_ALIGNED>>2)) {
+ ((sizeof(*th)+TCPOLEN_TSTAMP_ALIGNED)>>2) == th->doff) {
if (tcp_parse_aligned_timestamp(tp, th))
return 1;
}
- tcp_parse_options(skb, &tp->rx_opt, 1);
+ tcp_parse_options(skb, &tp->rx_opt, cryptic, 1);
return 1;
}
@@ -3830,7 +3847,7 @@ static int tcp_fast_parse_options(struct sk_buff *skb, struct tcphdr *th,
*/
u8 *tcp_parse_md5sig_option(struct tcphdr *th)
{
- int length = (th->doff << 2) - sizeof (*th);
+ int length = tcp_option_len_th(th);
u8 *ptr = (u8*)(th + 1);
/* If the TCP option is too short, we can short cut */
@@ -5070,10 +5087,11 @@ out:
static int tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
struct tcphdr *th, int syn_inerr)
{
+ u8 *cv;
struct tcp_sock *tp = tcp_sk(sk);
/* RFC1323: H1. Apply PAWS check first. */
- if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp &&
+ if (tcp_fast_parse_options(skb, th, tp, &cv) && tp->rx_opt.saw_tstamp &&
tcp_paws_discard(sk, skb)) {
if (!th->rst) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
@@ -5361,11 +5379,14 @@ discard:
static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
struct tcphdr *th, unsigned len)
{
- struct tcp_sock *tp = tcp_sk(sk);
+ u8 *cryptic_value;
struct inet_connection_sock *icsk = inet_csk(sk);
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct tcp_cookie_values *cvp = tp->cookie_values;
int saved_clamp = tp->rx_opt.mss_clamp;
+ int queued = 0;
- tcp_parse_options(skb, &tp->rx_opt, 0);
+ tcp_parse_options(skb, &tp->rx_opt, &cryptic_value, 0);
if (th->ack) {
/* rfc793:
@@ -5462,6 +5483,44 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
* Change state from SYN-SENT only after copied_seq
* is initialized. */
tp->copied_seq = tp->rcv_nxt;
+
+ if (NULL != cvp
+ && 0 < cvp->cookie_pair_size
+ && 0 < tp->rx_opt.cookie_plus) {
+ int cookie_size = tp->rx_opt.cookie_plus
+ - TCPOLEN_COOKIE_BASE;
+ int cookie_pair_size = cookie_size
+ + cvp->cookie_desired;
+
+ /* A cookie extension option was sent and returned.
+ * Note that each incoming SYNACK replaces the
+ * Responder cookie. The initial exchange is most
+ * fragile, as protection against spoofing relies
+ * entirely upon the sequence and timestamp (above).
+ * This replacement strategy allows the correct pair to
+ * pass through, while any others will be filtered via
+ * Responder verification later.
+ */
+ if (sizeof(cvp->cookie_pair) >= cookie_pair_size) {
+ memcpy(&cvp->cookie_pair[cvp->cookie_desired],
+ cryptic_value, cookie_size);
+ cvp->cookie_pair_size = cookie_pair_size;
+ }
+
+ if (tcp_header_len_th(th) < skb->len) {
+ /* Queue incoming transaction data. */
+ __skb_pull(skb, tcp_header_len_th(th));
+ __skb_queue_tail(&sk->sk_receive_queue, skb);
+ skb_set_owner_r(skb, sk);
+ sk->sk_data_ready(sk, 0);
+ tp->s_data_in = 1; /* true */
+ queued = 1; /* should be amount? */
+ tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
+ tp->rcv_wup = TCP_SKB_CB(skb)->end_seq;
+ tp->copied_seq = TCP_SKB_CB(skb)->seq + 1;
+ }
+ }
+
smp_mb();
tcp_set_state(sk, TCP_ESTABLISHED);
@@ -5513,11 +5572,14 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
TCP_DELACK_MAX, TCP_RTO_MAX);
discard:
- __kfree_skb(skb);
+ if (0 == queued)
+ __kfree_skb(skb);
return 0;
} else {
tcp_send_ack(sk);
}
+ if (0 < queued)
+ return 0; /* amount queued? */
return -1;
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 6edc5e2..1569be9 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -217,7 +217,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
if (inet->opt)
inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen;
- tp->rx_opt.mss_clamp = 536;
+ tp->rx_opt.mss_clamp = TCP_MIN_RCVMSS;
/* Socket identity is still unknown (sport may be zero).
* However we set state to SYN-SENT and not releasing socket
@@ -1213,9 +1213,12 @@ static struct timewait_sock_ops tcp_timewait_sock_ops = {
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
- struct inet_request_sock *ireq;
+ struct tcp_extend_values tmp_ext;
struct tcp_options_received tmp_opt;
+ u8 *cryptic_value;
+ struct inet_request_sock *ireq;
struct request_sock *req;
+ struct tcp_sock *tp = tcp_sk(sk);
__be32 saddr = ip_hdr(skb)->saddr;
__be32 daddr = ip_hdr(skb)->daddr;
__u32 isn = TCP_SKB_CB(skb)->when;
@@ -1260,16 +1263,37 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
#endif
tcp_clear_options(&tmp_opt);
- tmp_opt.mss_clamp = 536;
- tmp_opt.user_mss = tcp_sk(sk)->rx_opt.user_mss;
+ tmp_opt.mss_clamp = TCP_MIN_RCVMSS;
+ tmp_opt.user_mss = tp->rx_opt.user_mss;
- tcp_parse_options(skb, &tmp_opt, 0);
+ tcp_parse_options(skb, &tmp_opt, &cryptic_value, 0);
+
+ if (0 < tmp_opt.cookie_plus
+ && tmp_opt.saw_tstamp
+ && !tp->cookie_out_never
+ && (0 < sysctl_tcp_cookie_size
+ || (NULL != tp->cookie_values
+ && 0 < tp->cookie_values->cookie_desired))) {
+#ifdef CONFIG_SYN_COOKIES
+ want_cookie = 0; /* not our kind of cookie */
+#endif
+ tmp_ext.cookie_out_never = 0; /* false */
+ tmp_ext.cookie_plus = tmp_opt.cookie_plus;
+
+ /* secret recipe not yet implemented */
+ } else if (!tp->cookie_in_always) {
+ /* redundant indications, but ensure initialization. */
+ tmp_ext.cookie_out_never = 1; /* true */
+ tmp_ext.cookie_plus = 0;
+ } else {
+ goto drop_and_free;
+ }
+ tmp_ext.cookie_in_always = tp->cookie_in_always;
if (want_cookie && !tmp_opt.saw_tstamp)
tcp_clear_options(&tmp_opt);
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
-
tcp_openreq_init(req, &tmp_opt, skb);
ireq = inet_rsk(req);
@@ -1336,7 +1360,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
}
tcp_rsk(req)->snt_isn = isn;
- if (__tcp_v4_send_synack(sk, req, NULL, dst) ||
+ if (__tcp_v4_send_synack(sk, req, (struct request_values *)&tmp_ext, dst) ||
want_cookie)
goto drop_and_free;
@@ -1815,7 +1839,7 @@ static int tcp_v4_init_sock(struct sock *sk)
*/
tp->snd_ssthresh = TCP_INFINITE_SSTHRESH;
tp->snd_cwnd_clamp = ~0;
- tp->mss_cache = 536;
+ tp->mss_cache = TCP_MIN_RCVMSS;
tp->reordering = sysctl_tcp_reordering;
icsk->icsk_ca_ops = &tcp_init_congestion_ops;
@@ -1831,6 +1855,19 @@ static int tcp_v4_init_sock(struct sock *sk)
tp->af_specific = &tcp_sock_ipv4_specific;
#endif
+ /* TCP Cookie Transactions */
+ if (0 < sysctl_tcp_cookie_size) {
+ /* Default, cookies without s_data. */
+ tp->cookie_values =
+ kzalloc(sizeof(*tp->cookie_values),
+ sk->sk_allocation);
+ if (NULL != tp->cookie_values)
+ kref_init(&tp->cookie_values->kref);
+ }
+ /* Presumed zeroed, in order of appearance:
+ * cookie_in_always, cookie_out_never, extend_timestamp,
+ * s_data_constant, s_data_in, s_data_out
+ */
sk->sk_sndbuf = sysctl_tcp_wmem[1];
sk->sk_rcvbuf = sysctl_tcp_rmem[1];
@@ -1884,6 +1921,15 @@ void tcp_v4_destroy_sock(struct sock *sk)
sk->sk_sndmsg_page = NULL;
}
+ /*
+ * If cookie or s_data exists, remove it.
+ */
+ if (NULL != tp->cookie_values) {
+ kref_put(&tp->cookie_values->kref,
+ tcp_cookie_values_release);
+ tp->cookie_values = NULL;
+ }
+
percpu_counter_dec(&tcp_sockets_allocated);
}
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 8819882..785e5f4 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -96,13 +96,14 @@ enum tcp_tw_status
tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
const struct tcphdr *th)
{
- struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
struct tcp_options_received tmp_opt;
+ u8 *cryptic_value;
+ struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
int paws_reject = 0;
tmp_opt.saw_tstamp = 0;
if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) {
- tcp_parse_options(skb, &tmp_opt, 0);
+ tcp_parse_options(skb, &tmp_opt, &cryptic_value, 0);
if (tmp_opt.saw_tstamp) {
tmp_opt.ts_recent = tcptw->tw_ts_recent;
@@ -394,9 +395,12 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
/* Now setup tcp_sock */
newtp = tcp_sk(newsk);
newtp->pred_flags = 0;
- newtp->rcv_wup = newtp->copied_seq = newtp->rcv_nxt = treq->rcv_isn + 1;
- newtp->snd_sml = newtp->snd_una = newtp->snd_nxt = treq->snt_isn + 1;
- newtp->snd_up = treq->snt_isn + 1;
+
+ newtp->rcv_wup = newtp->copied_seq =
+ newtp->rcv_nxt = treq->rcv_isn + 1;
+
+ newtp->snd_sml = newtp->snd_una = newtp->snd_nxt =
+ newtp->snd_up = treq->snt_isn + 1 + tcp_s_data_size(tcp_sk(sk));
tcp_prequeue_init(newtp);
@@ -429,9 +433,24 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
tcp_set_ca_state(newsk, TCP_CA_Open);
tcp_init_xmit_timers(newsk);
skb_queue_head_init(&newtp->out_of_order_queue);
- newtp->write_seq = treq->snt_isn + 1;
- newtp->pushed_seq = newtp->write_seq;
+ newtp->write_seq = newtp->pushed_seq =
+ treq->snt_isn + 1 + tcp_s_data_size(tcp_sk(sk));
+ /* TCP Cookie Transactions */
+ if (NULL != tcp_sk(sk)->cookie_values) {
+ /* Instead of reusing the original, replace with
+ * default, cookies without s_data.
+ */
+ newtp->cookie_values =
+ kzalloc(sizeof(*newtp->cookie_values),
+ GFP_ATOMIC);
+ if (NULL != newtp->cookie_values)
+ kref_init(&newtp->cookie_values->kref);
+ }
+ /* Presumed copied, in order of appearance:
+ * cookie_in_always, cookie_out_never, extend_timestamp,
+ * s_data_constant, s_data_in, s_data_out
+ */
newtp->rx_opt.saw_tstamp = 0;
newtp->rx_opt.dsack = 0;
@@ -495,15 +514,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct request_sock **prev)
{
+ struct tcp_options_received tmp_opt;
+ u8 *cryptic_value;
const struct tcphdr *th = tcp_hdr(skb);
__be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK);
int paws_reject = 0;
- struct tcp_options_received tmp_opt;
struct sock *child;
tmp_opt.saw_tstamp = 0;
- if (th->doff > (sizeof(struct tcphdr)>>2)) {
- tcp_parse_options(skb, &tmp_opt, 0);
+ if (th->doff > (sizeof(*th) >> 2)) {
+ tcp_parse_options(skb, &tmp_opt, &cryptic_value, 0);
if (tmp_opt.saw_tstamp) {
tmp_opt.ts_recent = req->ts_recent;
@@ -596,7 +616,8 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
* Invalid ACK: reset will be sent by listening socket
*/
if ((flg & TCP_FLAG_ACK) &&
- (TCP_SKB_CB(skb)->ack_seq != tcp_rsk(req)->snt_isn + 1))
+ (TCP_SKB_CB(skb)->ack_seq != tcp_rsk(req)->snt_isn + 1 +
+ tcp_s_data_size(tcp_sk(sk))))
return sk;
/* Also, it would be not so bad idea to check rcv_tsecr, which
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index bcfbe41..9a901eb 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -370,6 +370,7 @@ static inline int tcp_urg_mode(const struct tcp_sock *tp)
#define OPTION_TS (1 << 1)
#define OPTION_MD5 (1 << 2)
#define OPTION_WSCALE (1 << 3)
+#define OPTION_COOKIE_EXTENSION (1 << 4)
struct tcp_out_options {
u8 options; /* bit field of OPTION_* */
@@ -377,8 +378,37 @@ struct tcp_out_options {
u8 num_sack_blocks; /* number of SACK blocks to include */
u16 mss; /* 0 to disable */
__u32 tsval, tsecr; /* need to include OPTION_TS */
+ u8 *cookie_copy; /* temporary pointer */
+ u8 cookie_size; /* bytes in copy */
};
+/* The sysctl int routines are generic, so check consistency here.
+ */
+static u8 tcp_cookie_size_check(u8 desired)
+{
+ if (0 < desired) {
+ /* previously specified */
+ return desired;
+ }
+ if (0 >= sysctl_tcp_cookie_size) {
+ /* no default specified */
+ return 0;
+ }
+ if (TCP_COOKIE_MIN > sysctl_tcp_cookie_size) {
+ /* value too small, increase to minimum */
+ return TCP_COOKIE_MIN;
+ }
+ if (TCP_COOKIE_MAX < sysctl_tcp_cookie_size) {
+ /* value too large, decrease to maximum */
+ return TCP_COOKIE_MAX;
+ }
+ if (0x1 & sysctl_tcp_cookie_size) {
+ /* 8-bit multiple, illegal, fix it */
+ return (u8)(sysctl_tcp_cookie_size + 0x1);
+ }
+ return (u8)sysctl_tcp_cookie_size;
+}
+
/* Write previously computed TCP options to the packet.
*
* Beware: Something in the Internet is very sensitive to the ordering of
@@ -395,11 +425,22 @@ struct tcp_out_options {
static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,
const struct tcp_out_options *opts,
__u8 **md5_hash) {
- if (unlikely(OPTION_MD5 & opts->options)) {
- *ptr++ = htonl((TCPOPT_NOP << 24) |
- (TCPOPT_NOP << 16) |
- (TCPOPT_MD5SIG << 8) |
- TCPOLEN_MD5SIG);
+ u8 options = opts->options; /* mungable copy */
+
+ if (unlikely(OPTION_MD5 & options)) {
+ if (unlikely(OPTION_COOKIE_EXTENSION & options)) {
+ *ptr++ = htonl((TCPOPT_COOKIE << 24) |
+ (TCPOLEN_COOKIE_BASE << 16) |
+ (TCPOPT_MD5SIG << 8) |
+ TCPOLEN_MD5SIG);
+ } else {
+ *ptr++ = htonl((TCPOPT_NOP << 24) |
+ (TCPOPT_NOP << 16) |
+ (TCPOPT_MD5SIG << 8) |
+ TCPOLEN_MD5SIG);
+ }
+ /* larger cookies are incompatible */
+ options &= ~OPTION_COOKIE_EXTENSION;
*md5_hash = (__u8 *)ptr;
ptr += 4;
} else {
@@ -412,12 +453,13 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,
opts->mss);
}
- if (likely(OPTION_TS & opts->options)) {
- if (unlikely(OPTION_SACK_ADVERTISE & opts->options)) {
+ if (likely(OPTION_TS & options)) {
+ if (unlikely(OPTION_SACK_ADVERTISE & options)) {
*ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
(TCPOLEN_SACK_PERM << 16) |
(TCPOPT_TIMESTAMP << 8) |
TCPOLEN_TIMESTAMP);
+ options &= ~OPTION_SACK_ADVERTISE;
} else {
*ptr++ = htonl((TCPOPT_NOP << 24) |
(TCPOPT_NOP << 16) |
@@ -428,15 +470,48 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,
*ptr++ = htonl(opts->tsecr);
}
- if (unlikely(OPTION_SACK_ADVERTISE & opts->options &&
- !(OPTION_TS & opts->options))) {
+ /* Specification requires after timestamp, so do it now.
+ */
+ if (unlikely(OPTION_COOKIE_EXTENSION & options)) {
+ u8 *cookie_copy = opts->cookie_copy;
+ u8 cookie_size = opts->cookie_size;
+
+ if (unlikely(0x1 & cookie_size)) {
+ /* 8-bit multiple, illegal, ignore */
+ cookie_size = 0;
+ } else if (likely(0x2 & cookie_size)) {
+ __u8 *p = (__u8 *)ptr;
+
+ /* 16-bit multiple */
+ *p++ = TCPOPT_COOKIE;
+ *p++ = TCPOLEN_COOKIE_BASE + cookie_size;
+ *p++ = *cookie_copy++;
+ *p++ = *cookie_copy++;
+ ptr++;
+ cookie_size -= 2;
+ } else {
+ /* 32-bit multiple */
+ *ptr++ = htonl(((TCPOPT_NOP << 24) |
+ (TCPOPT_NOP << 16) |
+ (TCPOPT_COOKIE << 8) |
+ TCPOLEN_COOKIE_BASE) +
+ cookie_size);
+ }
+
+ if (0 < cookie_size) {
+ memcpy(ptr, cookie_copy, cookie_size);
+ ptr += (cookie_size >> 2);
+ }
+ }
+
+ if (unlikely(OPTION_SACK_ADVERTISE & options)) {
*ptr++ = htonl((TCPOPT_NOP << 24) |
(TCPOPT_NOP << 16) |
(TCPOPT_SACK_PERM << 8) |
TCPOLEN_SACK_PERM);
}
- if (unlikely(OPTION_WSCALE & opts->options)) {
+ if (unlikely(OPTION_WSCALE & options)) {
*ptr++ = htonl((TCPOPT_NOP << 24) |
(TCPOPT_WINDOW << 16) |
(TCPOLEN_WINDOW << 8) |
@@ -471,11 +546,19 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb,
struct tcp_out_options *opts,
struct tcp_md5sig_key **md5) {
struct tcp_sock *tp = tcp_sk(sk);
+ struct tcp_cookie_values *cvp = tp->cookie_values;
unsigned size = 0;
+ u8 cookie_size = (!tp->cookie_out_never && NULL != cvp)
+ ? tcp_cookie_size_check(cvp->cookie_desired)
+ : 0;
#ifdef CONFIG_TCP_MD5SIG
*md5 = tp->af_specific->md5_lookup(sk, sk);
if (*md5) {
+ if (0 < cookie_size) {
+ /* cookie-less extension */
+ opts->options |= OPTION_COOKIE_EXTENSION;
+ }
opts->options |= OPTION_MD5;
size += TCPOLEN_MD5SIG_ALIGNED;
}
@@ -512,6 +595,63 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb,
size += TCPOLEN_SACKPERM_ALIGNED;
}
+ /* Having both authentication and cookies for security is redundant,
+ * and there's certainly not enough room. Instead, the cookie-less
+ * variant is proposed above.
+ *
+ * Consider the pessimal case with authentication. The options
+ * could look like:
+ * COOKIE|MD5(20) + MSS(4) + WSCALE(4) + SACK|TS(12) == 40
+ *
+ * (Currently, the timestamps && *MD5 test above prevents this.)
+ *
+ * Note that timestamps are required by the specification.
+ *
+ * Odd numbers of bytes are prohibited by the specification, ensuring
+ * that the cookie is 16-bit aligned, and the resulting cookie pair is
+ * 32-bit aligned.
+ */
+ if (NULL == *md5
+ && (OPTION_TS & opts->options)
+ && 0 < cookie_size) {
+ int need = TCPOLEN_COOKIE_BASE + cookie_size;
+ int remaining = MAX_TCP_OPTION_SPACE - size;
+
+ if (0x2 & need) {
+ /* 32-bit multiple */
+ need += 2; /* NOPs */
+
+ if (need > remaining) {
+ /* try shrinking cookie to fit */
+ cookie_size -= 2;
+ need -= 4;
+ }
+ }
+ while (need > remaining && TCP_COOKIE_MIN <= cookie_size) {
+ cookie_size -= 4;
+ need -= 4;
+ }
+ if (TCP_COOKIE_MIN <= cookie_size) {
+ opts->options |= OPTION_COOKIE_EXTENSION;
+ opts->cookie_copy = &cvp->cookie_pair[0];
+ opts->cookie_size = cookie_size;
+
+ /* Remember for future incarnations. */
+ cvp->cookie_desired = cookie_size;
+
+ if (cvp->cookie_desired != cvp->cookie_pair_size) {
+ /* Currently use random bytes as a nonce,
+ * assuming these are completely unpredictable
+ * by hostile users of the same system.
+ */
+ get_random_bytes(opts->cookie_copy,
+ cookie_size);
+ cvp->cookie_pair_size = cookie_size;
+ }
+
+ size += need;
+ }
+ }
return size;
}
@@ -520,14 +660,23 @@ static unsigned tcp_synack_options(struct sock *sk,
struct request_sock *req,
unsigned mss, struct sk_buff *skb,
struct tcp_out_options *opts,
- struct tcp_md5sig_key **md5) {
- unsigned size = 0;
+ struct tcp_md5sig_key **md5,
+ struct tcp_extend_values *xvp)
+{
struct inet_request_sock *ireq = inet_rsk(req);
+ unsigned size = 0;
+ u8 cookie_plus = (NULL != xvp && !xvp->cookie_out_never)
+ ? xvp->cookie_plus
+ : 0;
char doing_ts;
#ifdef CONFIG_TCP_MD5SIG
*md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req);
if (*md5) {
+ if (0 < cookie_plus) {
+ /* cookie-less extension */
+ opts->options |= OPTION_COOKIE_EXTENSION;
+ }
opts->options |= OPTION_MD5;
size += TCPOLEN_MD5SIG_ALIGNED;
}
@@ -561,6 +710,34 @@ static unsigned tcp_synack_options(struct sock *sk,
size += TCPOLEN_SACKPERM_ALIGNED;
}
+ /* Similar rationale to tcp_syn_options() applies here, too.
+ * If the <SYN> options fit, the same options should fit now!
+ */
+ if (NULL == *md5
+ && doing_ts
+ && 0 < cookie_plus) {
+ int need = cookie_plus; /* has TCPOLEN_COOKIE_BASE */
+ int remaining = MAX_TCP_OPTION_SPACE - size;
+
+ if (0x2 & need) {
+ /* 32-bit multiple */
+ need += 2; /* NOPs */
+ }
+ if (need <= remaining) {
+ opts->options |= OPTION_COOKIE_EXTENSION;
+ opts->cookie_copy = &xvp->cookie_bakery[0];
+ opts->cookie_size = cookie_plus - TCPOLEN_COOKIE_BASE;
+
+ /* secret recipe not yet implemented */
+ get_random_bytes(opts->cookie_copy,
+ opts->cookie_size);
+
+ size += need;
+ } else {
+ /* There's no error return, so flag it. */
+ xvp->cookie_out_never = 1; /* true */
+ }
+ }
return size;
}
@@ -2230,14 +2407,15 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
struct request_values *rvp,
struct request_sock *req)
{
+ struct tcp_out_options opts;
+ struct tcp_extend_values *xvp = tcp_xv(rvp);
struct inet_request_sock *ireq = inet_rsk(req);
struct tcp_sock *tp = tcp_sk(sk);
struct tcphdr *th;
- int tcp_header_size;
- struct tcp_out_options opts;
struct sk_buff *skb;
struct tcp_md5sig_key *md5;
__u8 *md5_hash_location;
+ int tcp_header_size;
int mss;
skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC);
@@ -2275,7 +2453,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
#endif
TCP_SKB_CB(skb)->when = tcp_time_stamp;
tcp_header_size = tcp_synack_options(sk, req, mss,
- skb, &opts, &md5) +
+ skb, &opts, &md5, xvp) +
sizeof(struct tcphdr);
skb_push(skb, tcp_header_size);
@@ -2293,6 +2471,25 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
*/
tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn,
TCPCB_FLAG_SYN | TCPCB_FLAG_ACK);
+
+ /* If cookies are active, and constant data is available, copy it
+ * directly from the listening socket.
+ */
+ if (NULL != xvp
+ && !xvp->cookie_out_never
+ && 0 < xvp->cookie_plus
+ && tp->s_data_constant) {
+ const struct tcp_cookie_values *cvp = tp->cookie_values;
+
+ if (NULL != cvp
+ && 0 < cvp->s_data_desired) {
+ u8 *buf = skb_put(skb, cvp->s_data_desired);
+
+ memcpy(buf, cvp->s_data_payload, cvp->s_data_desired);
+ TCP_SKB_CB(skb)->end_seq += cvp->s_data_desired;
+ }
+ }
+
th->seq = htonl(TCP_SKB_CB(skb)->seq);
th->ack_seq = htonl(tcp_rsk(req)->rcv_isn + 1);
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index c46da53..a0ad07b 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -159,6 +159,8 @@ static inline int cookie_check(struct sk_buff *skb, __u32 cookie)
struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
{
+ struct tcp_options_received tcp_opt;
+ u8 *cryptic_value;
struct inet_request_sock *ireq;
struct inet6_request_sock *ireq6;
struct tcp_request_sock *treq;
@@ -171,7 +173,6 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
int mss;
struct dst_entry *dst;
__u8 rcv_wscale;
- struct tcp_options_received tcp_opt;
if (!sysctl_tcp_syncookies || !th->ack)
goto out;
@@ -186,7 +187,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
/* check for timestamp cookie support */
memset(&tcp_opt, 0, sizeof(tcp_opt));
- tcp_parse_options(skb, &tcp_opt, 0);
+ tcp_parse_options(skb, &tcp_opt, &cryptic_value, 0);
if (tcp_opt.saw_tstamp)
cookie_check_timestamp(&tcp_opt);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 3960d72..f3e44be 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1162,11 +1162,13 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
*/
static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
{
+ struct tcp_extend_values tmp_ext;
+ struct tcp_options_received tmp_opt;
+ u8 *cryptic_value;
struct inet6_request_sock *treq;
struct ipv6_pinfo *np = inet6_sk(sk);
- struct tcp_options_received tmp_opt;
- struct tcp_sock *tp = tcp_sk(sk);
struct request_sock *req = NULL;
+ struct tcp_sock *tp = tcp_sk(sk);
__u32 isn = TCP_SKB_CB(skb)->when;
#ifdef CONFIG_SYN_COOKIES
int want_cookie = 0;
@@ -1206,7 +1208,29 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
tmp_opt.user_mss = tp->rx_opt.user_mss;
- tcp_parse_options(skb, &tmp_opt, 0);
+ tcp_parse_options(skb, &tmp_opt, &cryptic_value, 0);
+
+ if (0 < tmp_opt.cookie_plus
+ && tmp_opt.saw_tstamp
+ && !tp->cookie_out_never
+ && (0 < sysctl_tcp_cookie_size
+ || (NULL != tp->cookie_values
+ && 0 < tp->cookie_values->cookie_desired))) {
+#ifdef CONFIG_SYN_COOKIES
+ want_cookie = 0; /* not our kind of cookie */
+#endif
+ tmp_ext.cookie_out_never = 0; /* false */
+ tmp_ext.cookie_plus = tmp_opt.cookie_plus;
+
+ /* secret recipe not yet implemented */
+ } else if (!tp->cookie_in_always) {
+ /* redundant indications, but ensure initialization. */
+ tmp_ext.cookie_out_never = 1; /* true */
+ tmp_ext.cookie_plus = 0;
+ } else {
+ goto drop;
+ }
+ tmp_ext.cookie_in_always = tp->cookie_in_always;
if (want_cookie && !tmp_opt.saw_tstamp)
tcp_clear_options(&tmp_opt);
@@ -1244,7 +1268,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
security_inet_conn_request(sk, skb, req);
- if (tcp_v6_send_synack(sk, req, NULL) ||
+ if (tcp_v6_send_synack(sk, req, (struct request_values *)&tmp_ext) ||
want_cookie)
goto drop;
@@ -1850,7 +1874,7 @@ static int tcp_v6_init_sock(struct sock *sk)
*/
tp->snd_ssthresh = TCP_INFINITE_SSTHRESH;
tp->snd_cwnd_clamp = ~0;
- tp->mss_cache = 536;
+ tp->mss_cache = TCP_MIN_RCVMSS;
tp->reordering = sysctl_tcp_reordering;
@@ -1866,6 +1890,19 @@ static int tcp_v6_init_sock(struct sock *sk)
tp->af_specific = &tcp_sock_ipv6_specific;
#endif
+ /* TCP Cookie Transactions */
+ if (0 < sysctl_tcp_cookie_size) {
+ /* Default, cookies without s_data. */
+ tp->cookie_values =
+ kzalloc(sizeof(*tp->cookie_values),
+ sk->sk_allocation);
+ if (NULL != tp->cookie_values)
+ kref_init(&tp->cookie_values->kref);
+ }
+ /* Presumed zeroed, in order of appearance:
+ * cookie_in_always, cookie_out_never, extend_timestamp,
+ * s_data_constant, s_data_in, s_data_out
+ */
sk->sk_sndbuf = sysctl_tcp_wmem[1];
sk->sk_rcvbuf = sysctl_tcp_rmem[1];
--
1.6.0.4
^ permalink raw reply related
* Re: phy_connect_direct problem
From: Kristoffer Glembo @ 2009-10-27 13:10 UTC (permalink / raw)
To: netdev
In-Reply-To: <4AE6CB6A.5050600@gaisler.com>
Hi again,
Please forget about this. I'm obviously blind as I can't see the
difference between adjust_state and adjust_link ...
Best regards,
Kristoffer Glembo
Kristoffer Glembo wrote:
> Hi,
>
> I'm in the process of converting an ethernet driver to using the PHY
> layer, but ran into a problem where my adjust_state handler was never
> called and the link did not go up ...
>
> I use phy_connect to connect the PHY device with my ethernet driver. In
> phy_connect_direct I found the following code:
>
> phy_prepare_link(phydev, handler);
> phy_start_machine(phydev, NULL);
>
> So first the adjust_state is set to handler by phy_prepare_link and then
> it is overwritten with the NULL parameter passed to phy_start_machine.
>
> Maybe I'm abusing the PHY layer somehow since there are plenty of other
> drivers using this interface. However the patch below solved the issue
> for me.
>
> Best regards,
> Kristoffer Glembo
>
>
> ---
> drivers/net/phy/phy_device.c | 3 +--
> 1 files changed, 1 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> index b10fedd..d27ca80 100644
> --- a/drivers/net/phy/phy_device.c
> +++ b/drivers/net/phy/phy_device.c
> @@ -311,8 +311,7 @@ int phy_connect_direct(struct net_device *dev,
> struct phy_device *phydev,
> if (rc)
> return rc;
>
> - phy_prepare_link(phydev, handler);
> - phy_start_machine(phydev, NULL);
> + phy_start_machine(phydev, handler);
> if (phydev->irq > 0)
> phy_start_interrupts(phydev);
>
^ permalink raw reply
* Re: [PATCH 0/5] Candidate fix for increased number of GFP_ATOMIC failures V2
From: Mel Gorman @ 2009-10-27 13:27 UTC (permalink / raw)
To: Sven Geggus
Cc: Frans Pop, Jiri Kosina, Karol Lewandowski, Tobias Oetiker,
Rafael J. Wysocki, David Miller, Reinette Chatre, Kalle Valo,
David Rientjes, KOSAKI Motohiro, Mohamed Abbas, Jens Axboe,
John W. Linville, Pekka Enberg, Bartlomiej Zolnierkiewicz,
Greg Kroah-Hartman, Stephan von Krawczynski, Kernel Testers List,
netdev, linux-kernel, linux-mm@kvack.org
In-Reply-To: <20091024140207.GA5102@geggus.net>
On Sat, Oct 24, 2009 at 04:02:09PM +0200, Sven Geggus wrote:
> Mel Gorman schrieb am Donnerstag, den 22. Oktober um 16:22 Uhr:
>
> > Test 1: Verify your problem occurs on 2.6.32-rc5 if you can
>
> Problem persists. RAID resync in progress :(
>
What about the rest of the patches, any luck?
Thanks
--
Mel Gorman
Part-time Phd Student Linux Technology Center
University of Limerick IBM Dublin Software Lab
^ permalink raw reply
* [PATCH] net: Corrected spelling error heurestics->heuristics
From: Andreas Petlund @ 2009-10-27 13:27 UTC (permalink / raw)
To: netdev; +Cc: trivial, linux-kernel, ilpo.jarvinen
Corrected a spelling error in a function name.
Signed-off-by: Andreas Petlund <apetlund@simula.no>
---
net/ipv4/tcp_input.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index d86784b..a0c3700 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2300,7 +2300,7 @@ static inline int tcp_fackets_out(struct tcp_sock *tp)
* they differ. Since neither occurs due to loss, TCP should really
* ignore them.
*/
-static inline int tcp_dupack_heurestics(struct tcp_sock *tp)
+static inline int tcp_dupack_heuristics(struct tcp_sock *tp)
{
return tcp_is_fack(tp) ? tp->fackets_out : tp->sacked_out + 1;
}
@@ -2425,7 +2425,7 @@ static int tcp_time_to_recover(struct sock *sk)
return 1;
/* Not-A-Trick#2 : Classic rule... */
- if (tcp_dupack_heurestics(tp) > tp->reordering)
+ if (tcp_dupack_heuristics(tp) > tp->reordering)
return 1;
/* Trick#3 : when we use RFC2988 timer restart, fast
--
1.6.0.4
^ permalink raw reply related
* [PATCH V2]NET/KS8695: add support NAPI for Rx
From: Figo.zhang @ 2009-10-27 14:23 UTC (permalink / raw)
To: Daniel Silverstone, David S. Miller; +Cc: netdev, Ben Dooks
Add support NAPI Rx API for KS8695NET driver.
v2, change the Rx function to NAPI.
in <KS8695X Integrated Multi-port Gateway Solution Register Description
v1.0>:
Interrupt Enable Register (offset 0xE204)
Bit29 : WAN MAC Receive Interrupt Enable
Bit16 : LAN MAC Receive Interrupt Enable
Interrupt Status Register (Offset 0xF208)
Bit29: WAN MAC Receive Status
Bit16: LAN MAC Receive Status
see arch/arm/mach-ks8695/devices.c:
ks8695_wan_resources[] and ks8695_lan_resources[]
have IORESOURCE_IRQ , it have define the RX irq,
for wan, irq = 29; for lan ,irq = 16.
so we can do this read the interrupt status:
unsigned long mask_bit = 1 << ksp->rx_irq;
status = readl(KS8695_IRQ_VA + KS8695_INTST);
Signed-off-by: Figo.zhang <figo1802@gmail.com>
---
drivers/net/arm/ks8695net.c | 103 ++++++++++++++++++++++++++++++++----------
1 files changed, 78 insertions(+), 25 deletions(-)
diff --git a/drivers/net/arm/ks8695net.c b/drivers/net/arm/ks8695net.c
index 2a7b774..ed0b0f3 100644
--- a/drivers/net/arm/ks8695net.c
+++ b/drivers/net/arm/ks8695net.c
@@ -35,12 +35,15 @@
#include <mach/regs-switch.h>
#include <mach/regs-misc.h>
+#include <asm/mach/irq.h>
+#include <mach/regs-irq.h>
#include "ks8695net.h"
#define MODULENAME "ks8695_ether"
#define MODULEVERSION "1.01"
+
/*
* Transmit and device reset timeout, default 5 seconds.
*/
@@ -152,6 +155,8 @@ struct ks8695_priv {
enum ks8695_dtype dtype;
void __iomem *io_regs;
+ struct napi_struct napi;
+
const char *rx_irq_name, *tx_irq_name, *link_irq_name;
int rx_irq, tx_irq, link_irq;
@@ -172,6 +177,7 @@ struct ks8695_priv {
dma_addr_t rx_ring_dma;
struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
int next_rx_desc_read;
+ spinlock_t rx_lock;
int msg_enable;
};
@@ -396,25 +402,53 @@ ks8695_tx_irq(int irq, void *dev_id)
* @irq: The IRQ which went off (ignored)
* @dev_id: The net_device for the interrupt
*
- * Process the RX ring, passing any received packets up to the
- * host. If we received anything other than errors, we then
- * refill the ring.
+ * Use NAPI to receive packets.
*/
+
static irqreturn_t
ks8695_rx_irq(int irq, void *dev_id)
{
struct net_device *ndev = (struct net_device *)dev_id;
struct ks8695_priv *ksp = netdev_priv(ndev);
+ unsigned long status;
+
+ unsigned long mask_bit = 1 << ksp->rx_irq;
+
+ spin_lock(&ksp->rx_lock);
+
+ status = readl(KS8695_IRQ_VA + KS8695_INTST);
+
+ /*clean rx status bit*/
+ writel(status | mask_bit , KS8695_IRQ_VA + KS8695_INTST);
+
+ if (status & mask_bit) {
+ if (napi_schedule_prep(&ksp->napi)) {
+ /*disable rx interrupt*/
+ status &= ~mask_bit;
+ writel(status , KS8695_IRQ_VA + KS8695_INTEN);
+ __napi_schedule(&ksp->napi);
+ }
+ }
+
+ spin_unlock(&ksp->rx_lock);
+ return IRQ_HANDLED;
+}
+
+static int ks8695_rx(struct net_device *ndev, int budget)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
struct sk_buff *skb;
int buff_n;
u32 flags;
int pktlen;
int last_rx_processed = -1;
+ int received = 0;
buff_n = ksp->next_rx_desc_read;
- do {
- if (ksp->rx_buffers[buff_n].skb &&
- !(ksp->rx_ring[buff_n].status & cpu_to_le32(RDES_OWN))) {
+ while (received < budget
+ && ksp->rx_buffers[buff_n].skb
+ && (!(ksp->rx_ring[buff_n].status &
+ cpu_to_le32(RDES_OWN)))) {
rmb();
flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
/* Found an SKB which we own, this means we
@@ -464,7 +498,7 @@ ks8695_rx_irq(int irq, void *dev_id)
/* Relinquish the SKB to the network layer */
skb_put(skb, pktlen);
skb->protocol = eth_type_trans(skb, ndev);
- netif_rx(skb);
+ netif_receive_skb(skb);
/* Record stats */
ndev->stats.rx_packets++;
@@ -478,29 +512,44 @@ rx_failure:
/* Give the ring entry back to the hardware */
ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
rx_finished:
+ received++;
/* And note this as processed so we can start
* from here next time
*/
last_rx_processed = buff_n;
- } else {
- /* Ran out of things to process, stop now */
- break;
- }
- buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
- } while (buff_n != ksp->next_rx_desc_read);
-
- /* And note which RX descriptor we last did anything with */
- if (likely(last_rx_processed != -1))
- ksp->next_rx_desc_read =
- (last_rx_processed + 1) & MAX_RX_DESC_MASK;
-
- /* And refill the buffers */
- ks8695_refill_rxbuffers(ksp);
-
- /* Kick the RX DMA engine, in case it became suspended */
- ks8695_writereg(ksp, KS8695_DRSC, 0);
+ buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
+ /*And note which RX descriptor we last did */
+ if (likely(last_rx_processed != -1))
+ ksp->next_rx_desc_read =
+ (last_rx_processed + 1) &
+ MAX_RX_DESC_MASK;
+
+ /* And refill the buffers */
+ ks8695_refill_rxbuffers(ksp);
+ }
+ return received;
+}
- return IRQ_HANDLED;
+static int ks8695_poll(struct napi_struct *napi, int budget)
+{
+ struct ks8695_priv *ksp = container_of(napi, struct ks8695_priv, napi);
+ struct net_device *dev = ksp->ndev;
+ unsigned long mask_bit = 1 << ksp->rx_irq;
+ unsigned long isr = readl(KS8695_IRQ_VA + KS8695_INTEN);
+
+ unsigned long work_done ;
+
+ work_done = ks8695_rx(dev, budget);
+
+ if (work_done < budget) {
+ unsigned long flags;
+ spin_lock_irqsave(&ksp->rx_lock, flags);
+ /*enable rx interrupt*/
+ writel(isr | mask_bit, KS8695_IRQ_VA + KS8695_INTEN);
+ __napi_complete(napi);
+ spin_unlock_irqrestore(&ksp->rx_lock, flags);
+ }
+ return work_done;
}
/**
@@ -1472,6 +1521,8 @@ ks8695_probe(struct platform_device *pdev)
SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+ netif_napi_add(ndev, &ksp->napi, ks8695_poll, 64);
+
/* Retrieve the default MAC addr from the chip. */
/* The bootloader should have left it in there for us. */
@@ -1505,6 +1556,7 @@ ks8695_probe(struct platform_device *pdev)
/* And initialise the queue's lock */
spin_lock_init(&ksp->txq_lock);
+ spin_lock_init(&ksp->rx_lock);
/* Specify the RX DMA ring buffer */
ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE;
@@ -1626,6 +1678,7 @@ ks8695_drv_remove(struct platform_device *pdev)
struct ks8695_priv *ksp = netdev_priv(ndev);
platform_set_drvdata(pdev, NULL);
+ netif_napi_del(&ksp->napi);
unregister_netdev(ndev);
ks8695_release_device(ksp);
^ permalink raw reply related
* Re: [RFC PATCH] fib_hash: improve route deletion scaling on interface drop with lots of interfaces
From: Benjamin LaHaise @ 2009-10-27 14:24 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20091026.171749.106971240.davem@davemloft.net>
On Mon, Oct 26, 2009 at 05:17:49PM -0700, David Miller wrote:
> > bottleneck. Next up in the network code is rt_cache_flush(). Comments?
>
> On a real router adding and removing routes is happening a lot
> whereas interface changes are rare. You're making a more common
> operation more expensive for the sake of a less common one.
It's not a question of more common vs less common, but if the system can
recover from an adverse event within a reasonable amount of time. Tunnel
flaps occur in the real world, and this results in the change of state of
a large number of interfaces at the same time. Would it be okay if this
is wrapped in a config option? I agree that the extra overhead is not
for everyone.
> Please put local variable lines that are longer at the beginning of
> the list of variable declarations at the top of a function, not the
> other way around which stands out like a sore thumb and looks ugly.
Whoops, will fix.
-ben
^ permalink raw reply
* [PATCH] hso: fix debug routines
From: Antti Kaijanmäki @ 2009-10-27 14:26 UTC (permalink / raw)
To: Greg KH; +Cc: linux-kernel, netdev, Jan Dumon
In-Reply-To: <20091026194058.GA25263@kroah.com>
On Mon, 2009-10-26 at 12:40 -0700, Greg KH wrote:
> Yes, that should be a new patch, especially as it would not be needed
> to fix older kernels for the original bug.
>
> So, care to send 2 patches? The debug one isn't needed to be sent to
> the stable@kernel.org address.
Signed-off-by: Antti Kaijanmäki <antti.kaijanmaki@nomovok.com>
---
drivers/net/usb/hso.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index fa4e581..746839b 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -378,7 +378,7 @@ static void dbg_dump(int line_count, const char *func_name, unsigned char *buf,
}
#define DUMP(buf_, len_) \
- dbg_dump(__LINE__, __func__, buf_, len_)
+ dbg_dump(__LINE__, __func__, (unsigned char *)buf_, len_)
#define DUMP1(buf_, len_) \
do { \
@@ -1527,7 +1527,7 @@ static void tiocmget_intr_callback(struct urb *urb)
dev_warn(&usb->dev,
"hso received invalid serial state notification\n");
DUMP(serial_state_notification,
- sizeof(hso_serial_state_notifation))
+ sizeof(struct hso_serial_state_notification));
} else {
UART_state_bitmap = le16_to_cpu(serial_state_notification->
--
1.6.3.3
^ permalink raw reply related
* [PATCH] hso: fix soft-lockup
From: Antti Kaijanmäki @ 2009-10-27 14:26 UTC (permalink / raw)
To: Greg KH; +Cc: linux-kernel, netdev, stable, Jan Dumon
In-Reply-To: <20091026194058.GA25263@kroah.com>
On Mon, 2009-10-26 at 12:40 -0700, Greg KH wrote:
> Yes, that should be a new patch, especially as it would not be needed
> to fix older kernels for the original bug.
>
> So, care to send 2 patches? The debug one isn't needed to be sent to
> the stable@kernel.org address.
Fix soft-lockup in hso.c which is triggered on SMP machine when
modem is removed while file descriptor(s) under /dev are still open:
old version called kref_put() too early which resulted in destroying
hso_serial and hso_device objects which were still used later on.
Signed-off-by: Antti Kaijanmäki <antti.kaijanmaki@nomovok.com>
---
drivers/net/usb/hso.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index 746839b..43bc3fc 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -1363,7 +1363,7 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp)
/* reset the rts and dtr */
/* do the actual close */
serial->open_count--;
- kref_put(&serial->parent->ref, hso_serial_ref_free);
+
if (serial->open_count <= 0) {
serial->open_count = 0;
spin_lock_irq(&serial->serial_lock);
@@ -1383,6 +1383,8 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp)
usb_autopm_put_interface(serial->parent->interface);
mutex_unlock(&serial->parent->mutex);
+
+ kref_put(&serial->parent->ref, hso_serial_ref_free);
}
/* close the requested serial port */
--
1.6.3.3
^ permalink raw reply related
* Re: next tree for October 27: znet build failure
From: Stephen Rothwell @ 2009-10-27 14:37 UTC (permalink / raw)
To: Sachin Sant; +Cc: linux-next, netdev, David Miller, John W. Linville, Greg KH
In-Reply-To: <4AE6BEC7.2030703@in.ibm.com>
[-- Attachment #1: Type: text/plain, Size: 1076 bytes --]
Just adding John and Greg to the cc list as this is caused by patches in
the staging tree.
On Tue, 27 Oct 2009 15:05:03 +0530 Sachin Sant <sachinp@in.ibm.com> wrote:
>
> Fails to build on x86_32 with lots of errors related to znet
>
> drivers/net/znet.c:107:29: error: wireless/i82593.h: No such file or directory
> drivers/net/znet.c:133: error: field 'i593_init' has incomplete type
> drivers/net/znet.c: In function 'znet_set_multicast_list':
> drivers/net/znet.c:235: error: invalid application of 'sizeof' to incomplete type 'struct i82593_conf_block'
> drivers/net/znet.c:247: error: dereferencing pointer to incomplete type
> .....
>
> Commit 879e9304... moved wireless/i82593.h to drivers/staging/wavelan/i82593.h
>
> Thanks
> -Sachin
>
>
> --
>
> ---------------------------------
> Sachin Sant
> IBM Linux Technology Center
> India Systems and Technology Labs
> Bangalore, India
> ---------------------------------
>
--
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
* [PATCH net-2.6] sfc: Set ip_summed correctly for page buffers passed to GRO
From: Ben Hutchings @ 2009-10-27 14:50 UTC (permalink / raw)
To: David Miller; +Cc: netdev, linux-net-drivers
Page buffers containing packets with an incorrect checksum or using a
protocol not handled by hardware checksum offload were previously not
passed to LRO. The conversion to GRO changed this, but did not set
the ip_summed value accordingly.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Cc: stable@kernel.org
---
This affects 2.6.31 and seems like a candidate for a stable update.
Ben.
drivers/net/sfc/rx.c | 9 ++++++---
1 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c
index ea59ed2..4b65c62 100644
--- a/drivers/net/sfc/rx.c
+++ b/drivers/net/sfc/rx.c
@@ -441,7 +441,8 @@ static void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue,
* the appropriate LRO method
*/
static void efx_rx_packet_lro(struct efx_channel *channel,
- struct efx_rx_buffer *rx_buf)
+ struct efx_rx_buffer *rx_buf,
+ bool checksummed)
{
struct napi_struct *napi = &channel->napi_str;
@@ -463,7 +464,8 @@ static void efx_rx_packet_lro(struct efx_channel *channel,
skb->len = rx_buf->len;
skb->data_len = rx_buf->len;
skb->truesize += rx_buf->len;
- skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->ip_summed =
+ checksummed ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
napi_gro_frags(napi);
@@ -472,6 +474,7 @@ out:
rx_buf->page = NULL;
} else {
EFX_BUG_ON_PARANOID(!rx_buf->skb);
+ EFX_BUG_ON_PARANOID(!checksummed);
napi_gro_receive(napi, rx_buf->skb);
rx_buf->skb = NULL;
@@ -567,7 +570,7 @@ void __efx_rx_packet(struct efx_channel *channel,
}
if (likely(checksummed || rx_buf->page)) {
- efx_rx_packet_lro(channel, rx_buf);
+ efx_rx_packet_lro(channel, rx_buf, checksummed);
goto done;
}
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply related
* Re: [PATCH 2/5] page allocator: Do not allow interrupts to use ALLOC_HARDER
From: Mel Gorman @ 2009-10-27 15:19 UTC (permalink / raw)
To: Christoph Lameter
Cc: Stephan von Krawczynski, Frans Pop, Jiri Kosina, Sven Geggus,
Karol Lewandowski, Tobias Oetiker, Rafael J. Wysocki,
David Miller, Reinette Chatre, Kalle Valo, David Rientjes,
KOSAKI Motohiro, Mohamed Abbas, Jens Axboe, John W. Linville,
Pekka Enberg, Bartlomiej Zolnierkiewicz, Greg Kroah-Hartman,
Kernel Testers List, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-mm-Bw31MaZKKs3YtjvyW6yDsg@public.gmane.org
In-Reply-To: <alpine.DEB.1.10.0910232201520.9557@V090114053VZO-1>
On Fri, Oct 23, 2009 at 10:03:05PM -0400, Christoph Lameter wrote:
> There are now rt dependencies in the page allocator that screw things up?
>
The rt logic was present before.
> And an rt flag causes the page allocator to try harder meaning it adds
> latency.
>
The harder term in the flag is a bit misleading. The effect of ALLOC_HARDER
is that the watermark is lower for the task. i.e. a rt task is less likely
to enter direct reclaim than normal tasks so it should have less latency.
> ?
>
>
--
Mel Gorman
Part-time Phd Student Linux Technology Center
University of Limerick IBM Dublin Software Lab
^ permalink raw reply
* [PATCH v2 1/7] gianfar: Add per queue structure support
From: Sandeep Gopalpet @ 2009-10-27 16:54 UTC (permalink / raw)
To: netdev; +Cc: davem, Sandeep Gopalpet
This patch introduces per tx and per rx queue structures.
Earlier the members of these structures were inside the
gfar_private structure.
Moving forward if we want to support multiple queues, we need
to refactor the gfar_private structure so that introduction of
multiple queues is easier.
Signed-off-by: Sandeep Gopalpet <sandeep.kumar@freescale.com>
---
drivers/net/gianfar.c | 384 ++++++++++++++++++++++++-----------------
drivers/net/gianfar.h | 116 ++++++++-----
drivers/net/gianfar_ethtool.c | 100 +++++++----
drivers/net/gianfar_sysfs.c | 43 ++++--
4 files changed, 398 insertions(+), 245 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index c6f6d3b..4b1c023 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -8,9 +8,10 @@
*
* Author: Andy Fleming
* Maintainer: Kumar Gala
+ * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright (c) 2002-2006 Freescale Semiconductor, Inc.
- * Copyright (c) 2007 MontaVista Software, Inc.
+ * Copyright 2002-2009 Freescale Semiconductor, Inc.
+ * Copyright 2007 MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -109,7 +110,7 @@ static void gfar_reset_task(struct work_struct *work);
static void gfar_timeout(struct net_device *dev);
static int gfar_close(struct net_device *dev);
struct sk_buff *gfar_new_skb(struct net_device *dev);
-static void gfar_new_rxbdp(struct net_device *dev, struct rxbd8 *bdp,
+static void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp,
struct sk_buff *skb);
static int gfar_set_mac_address(struct net_device *dev);
static int gfar_change_mtu(struct net_device *dev, int new_mtu);
@@ -130,8 +131,8 @@ static int gfar_poll(struct napi_struct *napi, int budget);
#ifdef CONFIG_NET_POLL_CONTROLLER
static void gfar_netpoll(struct net_device *dev);
#endif
-int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
-static int gfar_clean_tx_ring(struct net_device *dev);
+int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit);
+static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue);
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
int amount_pull);
static void gfar_vlan_rx_register(struct net_device *netdev,
@@ -147,16 +148,16 @@ MODULE_AUTHOR("Freescale Semiconductor, Inc");
MODULE_DESCRIPTION("Gianfar Ethernet Driver");
MODULE_LICENSE("GPL");
-static void gfar_init_rxbdp(struct net_device *dev, struct rxbd8 *bdp,
+static void gfar_init_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp,
dma_addr_t buf)
{
- struct gfar_private *priv = netdev_priv(dev);
+ struct net_device *dev = rx_queue->dev;
u32 lstatus;
bdp->bufPtr = buf;
lstatus = BD_LFLAG(RXBD_EMPTY | RXBD_INTERRUPT);
- if (bdp == priv->rx_bd_base + priv->rx_ring_size - 1)
+ if (bdp == rx_queue->rx_bd_base + rx_queue->rx_ring_size - 1)
lstatus |= BD_LFLAG(RXBD_WRAP);
eieio();
@@ -167,20 +168,25 @@ static void gfar_init_rxbdp(struct net_device *dev, struct rxbd8 *bdp,
static int gfar_init_bds(struct net_device *ndev)
{
struct gfar_private *priv = netdev_priv(ndev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
struct txbd8 *txbdp;
struct rxbd8 *rxbdp;
int i;
+ tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue;
+
/* Initialize some variables in our dev structure */
- priv->num_txbdfree = priv->tx_ring_size;
- priv->dirty_tx = priv->cur_tx = priv->tx_bd_base;
- priv->cur_rx = priv->rx_bd_base;
- priv->skb_curtx = priv->skb_dirtytx = 0;
- priv->skb_currx = 0;
+ tx_queue->num_txbdfree = tx_queue->tx_ring_size;
+ tx_queue->dirty_tx = tx_queue->cur_tx = tx_queue->tx_bd_base;
+ rx_queue->cur_rx = rx_queue->rx_bd_base;
+ tx_queue->skb_curtx = tx_queue->skb_dirtytx = 0;
+ rx_queue->skb_currx = 0;
/* Initialize Transmit Descriptor Ring */
- txbdp = priv->tx_bd_base;
- for (i = 0; i < priv->tx_ring_size; i++) {
+ txbdp = tx_queue->tx_bd_base;
+ for (i = 0; i < tx_queue->tx_ring_size; i++) {
txbdp->lstatus = 0;
txbdp->bufPtr = 0;
txbdp++;
@@ -190,12 +196,12 @@ static int gfar_init_bds(struct net_device *ndev)
txbdp--;
txbdp->status |= TXBD_WRAP;
- rxbdp = priv->rx_bd_base;
- for (i = 0; i < priv->rx_ring_size; i++) {
- struct sk_buff *skb = priv->rx_skbuff[i];
+ rxbdp = rx_queue->rx_bd_base;
+ for (i = 0; i < rx_queue->rx_ring_size; i++) {
+ struct sk_buff *skb = rx_queue->rx_skbuff[i];
if (skb) {
- gfar_init_rxbdp(ndev, rxbdp, rxbdp->bufPtr);
+ gfar_init_rxbdp(rx_queue, rxbdp, rxbdp->bufPtr);
} else {
skb = gfar_new_skb(ndev);
if (!skb) {
@@ -203,9 +209,9 @@ static int gfar_init_bds(struct net_device *ndev)
ndev->name);
return -ENOMEM;
}
- priv->rx_skbuff[i] = skb;
+ rx_queue->rx_skbuff[i] = skb;
- gfar_new_rxbdp(ndev, rxbdp, skb);
+ gfar_new_rxbdp(rx_queue, rxbdp, skb);
}
rxbdp++;
@@ -220,12 +226,17 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
int i;
struct gfar_private *priv = netdev_priv(ndev);
struct device *dev = &priv->ofdev->dev;
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+
+ tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue;
/* Allocate memory for the buffer descriptors */
vaddr = dma_alloc_coherent(dev,
- sizeof(*priv->tx_bd_base) * priv->tx_ring_size +
- sizeof(*priv->rx_bd_base) * priv->rx_ring_size,
- &priv->tx_bd_dma_base, GFP_KERNEL);
+ sizeof(*tx_queue->tx_bd_base) * tx_queue->tx_ring_size +
+ sizeof(*rx_queue->rx_bd_base) * rx_queue->rx_ring_size,
+ &tx_queue->tx_bd_dma_base, GFP_KERNEL);
if (!vaddr) {
if (netif_msg_ifup(priv))
pr_err("%s: Could not allocate buffer descriptors!\n",
@@ -233,36 +244,38 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
return -ENOMEM;
}
- priv->tx_bd_base = vaddr;
+ tx_queue->tx_bd_base = vaddr;
+ tx_queue->dev = ndev;
/* Start the rx descriptor ring where the tx ring leaves off */
- vaddr = vaddr + sizeof(*priv->tx_bd_base) * priv->tx_ring_size;
- priv->rx_bd_base = vaddr;
+ vaddr = vaddr + sizeof(*tx_queue->tx_bd_base) * tx_queue->tx_ring_size;
+ rx_queue->rx_bd_base = vaddr;
+ rx_queue->dev = ndev;
/* Setup the skbuff rings */
- priv->tx_skbuff = kmalloc(sizeof(*priv->tx_skbuff) *
- priv->tx_ring_size, GFP_KERNEL);
- if (!priv->tx_skbuff) {
+ tx_queue->tx_skbuff = kmalloc(sizeof(*tx_queue->tx_skbuff) *
+ tx_queue->tx_ring_size, GFP_KERNEL);
+ if (!tx_queue->tx_skbuff) {
if (netif_msg_ifup(priv))
pr_err("%s: Could not allocate tx_skbuff\n",
ndev->name);
goto cleanup;
}
- for (i = 0; i < priv->tx_ring_size; i++)
- priv->tx_skbuff[i] = NULL;
+ for (i = 0; i < tx_queue->tx_ring_size; i++)
+ tx_queue->tx_skbuff[i] = NULL;
- priv->rx_skbuff = kmalloc(sizeof(*priv->rx_skbuff) *
- priv->rx_ring_size, GFP_KERNEL);
- if (!priv->rx_skbuff) {
+ rx_queue->rx_skbuff = kmalloc(sizeof(*rx_queue->rx_skbuff) *
+ rx_queue->rx_ring_size, GFP_KERNEL);
+ if (!rx_queue->rx_skbuff) {
if (netif_msg_ifup(priv))
pr_err("%s: Could not allocate rx_skbuff\n",
ndev->name);
goto cleanup;
}
- for (i = 0; i < priv->rx_ring_size; i++)
- priv->rx_skbuff[i] = NULL;
+ for (i = 0; i < rx_queue->rx_ring_size; i++)
+ rx_queue->rx_skbuff[i] = NULL;
if (gfar_init_bds(ndev))
goto cleanup;
@@ -278,24 +291,29 @@ static void gfar_init_mac(struct net_device *ndev)
{
struct gfar_private *priv = netdev_priv(ndev);
struct gfar __iomem *regs = priv->regs;
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
u32 rctrl = 0;
u32 tctrl = 0;
u32 attrs = 0;
+ tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue;
+
/* enet DMA only understands physical addresses */
- gfar_write(®s->tbase0, priv->tx_bd_dma_base);
- gfar_write(®s->rbase0, priv->tx_bd_dma_base +
- sizeof(*priv->tx_bd_base) *
- priv->tx_ring_size);
+ gfar_write(®s->tbase0, tx_queue->tx_bd_dma_base);
+ gfar_write(®s->rbase0, tx_queue->tx_bd_dma_base +
+ sizeof(*tx_queue->tx_bd_base) *
+ tx_queue->tx_ring_size);
/* Configure the coalescing support */
gfar_write(®s->txic, 0);
- if (priv->txcoalescing)
- gfar_write(®s->txic, priv->txic);
+ if (tx_queue->txcoalescing)
+ gfar_write(®s->txic, tx_queue->txic);
gfar_write(®s->rxic, 0);
- if (priv->rxcoalescing)
- gfar_write(®s->rxic, priv->rxic);
+ if (rx_queue->rxcoalescing)
+ gfar_write(®s->rxic, rx_queue->rxic);
if (priv->rx_csum_enable)
rctrl |= RCTRL_CHECKSUMMING;
@@ -414,7 +432,7 @@ static int gfar_of_init(struct net_device *dev)
stash = of_get_property(np, "bd-stash", NULL);
- if(stash) {
+ if (stash) {
priv->device_flags |= FSL_GIANFAR_DEV_HAS_BD_STASHING;
priv->bd_stash_en = 1;
}
@@ -519,8 +537,18 @@ static int gfar_probe(struct of_device *ofdev,
if (err)
goto regs_fail;
- spin_lock_init(&priv->txlock);
- spin_lock_init(&priv->rxlock);
+ priv->tx_queue = (struct gfar_priv_tx_q *)kmalloc(
+ sizeof (struct gfar_priv_tx_q), GFP_KERNEL);
+ if (!priv->tx_queue)
+ goto regs_fail;
+
+ priv->rx_queue = (struct gfar_priv_rx_q *)kmalloc(
+ sizeof (struct gfar_priv_rx_q), GFP_KERNEL);
+ if (!priv->rx_queue)
+ goto rx_queue_fail;
+
+ spin_lock_init(&priv->tx_queue->txlock);
+ spin_lock_init(&priv->rx_queue->rxlock);
spin_lock_init(&priv->bflock);
INIT_WORK(&priv->reset_task, gfar_reset_task);
@@ -552,12 +580,13 @@ static int gfar_probe(struct of_device *ofdev,
/* Fill in the dev structure */
dev->watchdog_timeo = TX_TIMEOUT;
- netif_napi_add(dev, &priv->napi, gfar_poll, GFAR_DEV_WEIGHT);
dev->mtu = 1500;
-
dev->netdev_ops = &gfar_netdev_ops;
dev->ethtool_ops = &gfar_ethtool_ops;
+ /* Register for napi ...NAPI is for each rx_queue */
+ netif_napi_add(dev, &priv->rx_queue->napi, gfar_poll, GFAR_DEV_WEIGHT);
+
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
priv->rx_csum_enable = 1;
dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA;
@@ -613,14 +642,16 @@ static int gfar_probe(struct of_device *ofdev,
dev->hard_header_len += GMAC_FCB_LEN;
priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
- priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
- priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
- priv->num_txbdfree = DEFAULT_TX_RING_SIZE;
- priv->txcoalescing = DEFAULT_TX_COALESCE;
- priv->txic = DEFAULT_TXIC;
- priv->rxcoalescing = DEFAULT_RX_COALESCE;
- priv->rxic = DEFAULT_RXIC;
+ /* Initializing some of the rx/tx queue level parameters */
+ priv->tx_queue->tx_ring_size = DEFAULT_TX_RING_SIZE;
+ priv->tx_queue->num_txbdfree = DEFAULT_TX_RING_SIZE;
+ priv->tx_queue->txcoalescing = DEFAULT_TX_COALESCE;
+ priv->tx_queue->txic = DEFAULT_TXIC;
+
+ priv->rx_queue->rx_ring_size = DEFAULT_RX_RING_SIZE;
+ priv->rx_queue->rxcoalescing = DEFAULT_RX_COALESCE;
+ priv->rx_queue->rxic = DEFAULT_RXIC;
/* Enable most messages by default */
priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
@@ -666,12 +697,15 @@ static int gfar_probe(struct of_device *ofdev,
/* provided which set of benchmarks. */
printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name);
printk(KERN_INFO "%s: %d/%d RX/TX BD ring size\n",
- dev->name, priv->rx_ring_size, priv->tx_ring_size);
+ dev->name, priv->rx_queue->rx_ring_size, priv->tx_queue->tx_ring_size);
return 0;
register_fail:
iounmap(priv->regs);
+ kfree(priv->rx_queue);
+rx_queue_fail:
+ kfree(priv->tx_queue);
regs_fail:
if (priv->phy_node)
of_node_put(priv->phy_node);
@@ -705,6 +739,8 @@ static int gfar_suspend(struct device *dev)
{
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
unsigned long flags;
u32 tempval;
@@ -712,10 +748,12 @@ static int gfar_suspend(struct device *dev)
(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
netif_device_detach(ndev);
+ tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue;
if (netif_running(ndev)) {
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
+ spin_lock_irqsave(&tx_queue->txlock, flags);
+ spin_lock(&rx_queue->rxlock);
gfar_halt_nodisable(ndev);
@@ -729,10 +767,10 @@ static int gfar_suspend(struct device *dev)
gfar_write(&priv->regs->maccfg1, tempval);
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock(&rx_queue->rxlock);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
- napi_disable(&priv->napi);
+ napi_disable(&rx_queue->napi);
if (magic_packet) {
/* Enable interrupt on Magic Packet */
@@ -754,6 +792,8 @@ static int gfar_resume(struct device *dev)
{
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
unsigned long flags;
u32 tempval;
int magic_packet = priv->wol_en &&
@@ -770,9 +810,11 @@ static int gfar_resume(struct device *dev)
/* Disable Magic Packet mode, in case something
* else woke us up.
*/
+ rx_queue = priv->rx_queue;
+ tx_queue = priv->tx_queue;
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
+ spin_lock_irqsave(&tx_queue->txlock, flags);
+ spin_lock(&rx_queue->rxlock);
tempval = gfar_read(&priv->regs->maccfg2);
tempval &= ~MACCFG2_MPEN;
@@ -780,12 +822,12 @@ static int gfar_resume(struct device *dev)
gfar_start(ndev);
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock(&rx_queue->rxlock);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
netif_device_attach(ndev);
- napi_enable(&priv->napi);
+ napi_enable(&rx_queue->napi);
return 0;
}
@@ -1060,18 +1102,23 @@ void gfar_halt(struct net_device *dev)
void stop_gfar(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
unsigned long flags;
phy_stop(priv->phydev);
+ tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue;
+
/* Lock it down */
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
+ spin_lock_irqsave(&tx_queue->txlock, flags);
+ spin_lock(&rx_queue->rxlock);
gfar_halt(dev);
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock(&rx_queue->rxlock);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
/* Free the IRQs */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
@@ -1092,46 +1139,50 @@ static void free_skb_resources(struct gfar_private *priv)
struct device *dev = &priv->ofdev->dev;
struct rxbd8 *rxbdp;
struct txbd8 *txbdp;
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
int i, j;
/* Go through all the buffer descriptors and free their data buffers */
- txbdp = priv->tx_bd_base;
+ tx_queue = priv->tx_queue;
+ txbdp = tx_queue->tx_bd_base;
- if (!priv->tx_skbuff)
+ if (!tx_queue->tx_skbuff)
goto skip_tx_skbuff;
- for (i = 0; i < priv->tx_ring_size; i++) {
- if (!priv->tx_skbuff[i])
+ for (i = 0; i < tx_queue->tx_ring_size; i++) {
+ if (!tx_queue->tx_skbuff[i])
continue;
dma_unmap_single(&priv->ofdev->dev, txbdp->bufPtr,
txbdp->length, DMA_TO_DEVICE);
txbdp->lstatus = 0;
- for (j = 0; j < skb_shinfo(priv->tx_skbuff[i])->nr_frags; j++) {
+ for (j = 0; j < skb_shinfo(tx_queue->tx_skbuff[i])->nr_frags; j++) {
txbdp++;
dma_unmap_page(&priv->ofdev->dev, txbdp->bufPtr,
txbdp->length, DMA_TO_DEVICE);
}
txbdp++;
- dev_kfree_skb_any(priv->tx_skbuff[i]);
- priv->tx_skbuff[i] = NULL;
+ dev_kfree_skb_any(tx_queue->tx_skbuff[i]);
+ tx_queue->tx_skbuff[i] = NULL;
}
- kfree(priv->tx_skbuff);
+ kfree(tx_queue->tx_skbuff);
skip_tx_skbuff:
- rxbdp = priv->rx_bd_base;
+ rx_queue = priv->rx_queue;
+ rxbdp = rx_queue->rx_bd_base;
- if (!priv->rx_skbuff)
+ if (!rx_queue->rx_skbuff)
goto skip_rx_skbuff;
- for (i = 0; i < priv->rx_ring_size; i++) {
- if (priv->rx_skbuff[i]) {
+ for (i = 0; i < rx_queue->rx_ring_size; i++) {
+ if (rx_queue->rx_skbuff[i]) {
dma_unmap_single(&priv->ofdev->dev, rxbdp->bufPtr,
priv->rx_buffer_size,
DMA_FROM_DEVICE);
- dev_kfree_skb_any(priv->rx_skbuff[i]);
- priv->rx_skbuff[i] = NULL;
+ dev_kfree_skb_any(rx_queue->rx_skbuff[i]);
+ rx_queue->rx_skbuff[i] = NULL;
}
rxbdp->lstatus = 0;
@@ -1139,17 +1190,19 @@ skip_tx_skbuff:
rxbdp++;
}
- kfree(priv->rx_skbuff);
+ kfree(rx_queue->rx_skbuff);
skip_rx_skbuff:
- dma_free_coherent(dev, sizeof(*txbdp) * priv->tx_ring_size +
- sizeof(*rxbdp) * priv->rx_ring_size,
- priv->tx_bd_base, priv->tx_bd_dma_base);
+ dma_free_coherent(dev, sizeof(*txbdp) * tx_queue->tx_ring_size +
+ sizeof(*rxbdp) * rx_queue->rx_ring_size,
+ tx_queue->tx_bd_base, tx_queue->tx_bd_dma_base);
}
void gfar_start(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue;
+ struct gfar_priv_rx_q *rx_queue;
struct gfar __iomem *regs = priv->regs;
u32 tempval;
@@ -1258,7 +1311,7 @@ static int gfar_enet_open(struct net_device *dev)
struct gfar_private *priv = netdev_priv(dev);
int err;
- napi_enable(&priv->napi);
+ napi_enable(&priv->rx_queue->napi);
skb_queue_head_init(&priv->rx_recycle);
@@ -1269,14 +1322,14 @@ static int gfar_enet_open(struct net_device *dev)
err = init_phy(dev);
- if(err) {
- napi_disable(&priv->napi);
+ if (err) {
+ napi_disable(&priv->rx_queue->napi);
return err;
}
err = startup_gfar(dev);
if (err) {
- napi_disable(&priv->napi);
+ napi_disable(&priv->rx_queue->napi);
return err;
}
@@ -1349,6 +1402,7 @@ static inline struct txbd8 *next_txbd(struct txbd8 *bdp, struct txbd8 *base,
static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
struct txfcb *fcb = NULL;
struct txbd8 *txbdp, *txbdp_start, *base;
u32 lstatus;
@@ -1357,7 +1411,8 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
unsigned long flags;
unsigned int nr_frags, length;
- base = priv->tx_bd_base;
+ tx_queue = priv->tx_queue;
+ base = tx_queue->tx_bd_base;
/* make space for additional header when fcb is needed */
if (((skb->ip_summed == CHECKSUM_PARTIAL) ||
@@ -1378,21 +1433,21 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* total number of fragments in the SKB */
nr_frags = skb_shinfo(skb)->nr_frags;
- spin_lock_irqsave(&priv->txlock, flags);
+ spin_lock_irqsave(&tx_queue->txlock, flags);
/* check if there is space to queue this packet */
- if ((nr_frags+1) > priv->num_txbdfree) {
+ if ((nr_frags+1) > tx_queue->num_txbdfree) {
/* no space, stop the queue */
netif_stop_queue(dev);
dev->stats.tx_fifo_errors++;
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
return NETDEV_TX_BUSY;
}
/* Update transmit stats */
dev->stats.tx_bytes += skb->len;
- txbdp = txbdp_start = priv->cur_tx;
+ txbdp = txbdp_start = tx_queue->cur_tx;
if (nr_frags == 0) {
lstatus = txbdp->lstatus | BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT);
@@ -1400,7 +1455,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Place the fragment addresses and lengths into the TxBDs */
for (i = 0; i < nr_frags; i++) {
/* Point at the next BD, wrapping as needed */
- txbdp = next_txbd(txbdp, base, priv->tx_ring_size);
+ txbdp = next_txbd(txbdp, base, tx_queue->tx_ring_size);
length = skb_shinfo(skb)->frags[i].size;
@@ -1442,7 +1497,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
/* setup the TxBD length and buffer pointer for the first BD */
- priv->tx_skbuff[priv->skb_curtx] = skb;
+ tx_queue->tx_skbuff[tx_queue->skb_curtx] = skb;
txbdp_start->bufPtr = dma_map_single(&priv->ofdev->dev, skb->data,
skb_headlen(skb), DMA_TO_DEVICE);
@@ -1462,19 +1517,19 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Update the current skb pointer to the next entry we will use
* (wrapping if necessary) */
- priv->skb_curtx = (priv->skb_curtx + 1) &
- TX_RING_MOD_MASK(priv->tx_ring_size);
+ tx_queue->skb_curtx = (tx_queue->skb_curtx + 1) &
+ TX_RING_MOD_MASK(tx_queue->tx_ring_size);
- priv->cur_tx = next_txbd(txbdp, base, priv->tx_ring_size);
+ tx_queue->cur_tx = next_txbd(txbdp, base, tx_queue->tx_ring_size);
/* reduce TxBD free count */
- priv->num_txbdfree -= (nr_frags + 1);
+ tx_queue->num_txbdfree -= (nr_frags + 1);
dev->trans_start = jiffies;
/* If the next BD still needs to be cleaned up, then the bds
are full. We need to tell the kernel to stop sending us stuff. */
- if (!priv->num_txbdfree) {
+ if (!tx_queue->num_txbdfree) {
netif_stop_queue(dev);
dev->stats.tx_fifo_errors++;
@@ -1484,7 +1539,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
/* Unlock priv */
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
return NETDEV_TX_OK;
}
@@ -1494,7 +1549,7 @@ static int gfar_close(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- napi_disable(&priv->napi);
+ napi_disable(&priv->rx_queue->napi);
skb_queue_purge(&priv->rx_recycle);
cancel_work_sync(&priv->reset_task);
@@ -1523,10 +1578,12 @@ static void gfar_vlan_rx_register(struct net_device *dev,
struct vlan_group *grp)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_rx_q *rx_queue = NULL;
unsigned long flags;
u32 tempval;
- spin_lock_irqsave(&priv->rxlock, flags);
+ rx_queue = priv->rx_queue;
+ spin_lock_irqsave(&rx_queue->rxlock, flags);
priv->vlgrp = grp;
@@ -1560,7 +1617,7 @@ static void gfar_vlan_rx_register(struct net_device *dev,
gfar_change_mtu(dev, dev->mtu);
- spin_unlock_irqrestore(&priv->rxlock, flags);
+ spin_unlock_irqrestore(&rx_queue->rxlock, flags);
}
static int gfar_change_mtu(struct net_device *dev, int new_mtu)
@@ -1649,24 +1706,27 @@ static void gfar_timeout(struct net_device *dev)
}
/* Interrupt Handler for Transmit complete */
-static int gfar_clean_tx_ring(struct net_device *dev)
+static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
{
+ struct net_device *dev = tx_queue->dev;
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_rx_q *rx_queue = NULL;
struct txbd8 *bdp;
struct txbd8 *lbdp = NULL;
- struct txbd8 *base = priv->tx_bd_base;
+ struct txbd8 *base = tx_queue->tx_bd_base;
struct sk_buff *skb;
int skb_dirtytx;
- int tx_ring_size = priv->tx_ring_size;
+ int tx_ring_size = tx_queue->tx_ring_size;
int frags = 0;
int i;
int howmany = 0;
u32 lstatus;
- bdp = priv->dirty_tx;
- skb_dirtytx = priv->skb_dirtytx;
+ rx_queue = priv->rx_queue;
+ bdp = tx_queue->dirty_tx;
+ skb_dirtytx = tx_queue->skb_dirtytx;
- while ((skb = priv->tx_skbuff[skb_dirtytx])) {
+ while ((skb = tx_queue->tx_skbuff[skb_dirtytx])) {
frags = skb_shinfo(skb)->nr_frags;
lbdp = skip_txbd(bdp, frags, base, tx_ring_size);
@@ -1698,29 +1758,29 @@ static int gfar_clean_tx_ring(struct net_device *dev)
* If there's room in the queue (limit it to rx_buffer_size)
* we add this skb back into the pool, if it's the right size
*/
- if (skb_queue_len(&priv->rx_recycle) < priv->rx_ring_size &&
+ if (skb_queue_len(&priv->rx_recycle) < rx_queue->rx_ring_size &&
skb_recycle_check(skb, priv->rx_buffer_size +
RXBUF_ALIGNMENT))
__skb_queue_head(&priv->rx_recycle, skb);
else
dev_kfree_skb_any(skb);
- priv->tx_skbuff[skb_dirtytx] = NULL;
+ tx_queue->tx_skbuff[skb_dirtytx] = NULL;
skb_dirtytx = (skb_dirtytx + 1) &
TX_RING_MOD_MASK(tx_ring_size);
howmany++;
- priv->num_txbdfree += frags + 1;
+ tx_queue->num_txbdfree += frags + 1;
}
/* If we freed a buffer, we can restart transmission, if necessary */
- if (netif_queue_stopped(dev) && priv->num_txbdfree)
+ if (netif_queue_stopped(dev) && tx_queue->num_txbdfree)
netif_wake_queue(dev);
/* Update dirty indicators */
- priv->skb_dirtytx = skb_dirtytx;
- priv->dirty_tx = bdp;
+ tx_queue->skb_dirtytx = skb_dirtytx;
+ tx_queue->dirty_tx = bdp;
dev->stats.tx_packets += howmany;
@@ -1730,14 +1790,18 @@ static int gfar_clean_tx_ring(struct net_device *dev)
static void gfar_schedule_cleanup(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
unsigned long flags;
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
+ rx_queue = priv->rx_queue;
+ tx_queue = priv->tx_queue;
+ spin_lock_irqsave(&tx_queue->txlock, flags);
+ spin_lock(&rx_queue->rxlock);
- if (napi_schedule_prep(&priv->napi)) {
+ if (napi_schedule_prep(&rx_queue->napi)) {
gfar_write(&priv->regs->imask, IMASK_RTX_DISABLED);
- __napi_schedule(&priv->napi);
+ __napi_schedule(&rx_queue->napi);
} else {
/*
* Clear IEVENT, so interrupts aren't called again
@@ -1746,8 +1810,8 @@ static void gfar_schedule_cleanup(struct net_device *dev)
gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK);
}
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock(&rx_queue->rxlock);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
}
/* Interrupt Handler for Transmit complete */
@@ -1757,15 +1821,16 @@ static irqreturn_t gfar_transmit(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static void gfar_new_rxbdp(struct net_device *dev, struct rxbd8 *bdp,
+static void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp,
struct sk_buff *skb)
{
+ struct net_device *dev = rx_queue->dev;
struct gfar_private *priv = netdev_priv(dev);
dma_addr_t buf;
buf = dma_map_single(&priv->ofdev->dev, skb->data,
priv->rx_buffer_size, DMA_FROM_DEVICE);
- gfar_init_rxbdp(dev, bdp, buf);
+ gfar_init_rxbdp(rx_queue, bdp, buf);
}
@@ -1890,8 +1955,9 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
* until the budget/quota has been reached. Returns the number
* of frames handled
*/
-int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
+int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
{
+ struct net_device *dev = rx_queue->dev;
struct rxbd8 *bdp, *base;
struct sk_buff *skb;
int pkt_len;
@@ -1900,8 +1966,8 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
struct gfar_private *priv = netdev_priv(dev);
/* Get the first full descriptor */
- bdp = priv->cur_rx;
- base = priv->rx_bd_base;
+ bdp = rx_queue->cur_rx;
+ base = rx_queue->rx_bd_base;
amount_pull = (gfar_uses_fcb(priv) ? GMAC_FCB_LEN : 0) +
priv->padding;
@@ -1913,7 +1979,7 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
/* Add another skb for the future */
newskb = gfar_new_skb(dev);
- skb = priv->rx_skbuff[priv->skb_currx];
+ skb = rx_queue->rx_skbuff[rx_queue->skb_currx];
dma_unmap_single(&priv->ofdev->dev, bdp->bufPtr,
priv->rx_buffer_size, DMA_FROM_DEVICE);
@@ -1961,30 +2027,33 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
}
- priv->rx_skbuff[priv->skb_currx] = newskb;
+ rx_queue->rx_skbuff[rx_queue->skb_currx] = newskb;
/* Setup the new bdp */
- gfar_new_rxbdp(dev, bdp, newskb);
+ gfar_new_rxbdp(rx_queue, bdp, newskb);
/* Update to the next pointer */
- bdp = next_bd(bdp, base, priv->rx_ring_size);
+ bdp = next_bd(bdp, base, rx_queue->rx_ring_size);
/* update to point at the next skb */
- priv->skb_currx =
- (priv->skb_currx + 1) &
- RX_RING_MOD_MASK(priv->rx_ring_size);
+ rx_queue->skb_currx =
+ (rx_queue->skb_currx + 1) &
+ RX_RING_MOD_MASK(rx_queue->rx_ring_size);
}
/* Update the current rxbd pointer to be the next one */
- priv->cur_rx = bdp;
+ rx_queue->cur_rx = bdp;
return howmany;
}
static int gfar_poll(struct napi_struct *napi, int budget)
{
- struct gfar_private *priv = container_of(napi, struct gfar_private, napi);
- struct net_device *dev = priv->ndev;
+ struct gfar_priv_rx_q *rx_queue = container_of(napi,
+ struct gfar_priv_rx_q, napi);
+ struct net_device *dev = rx_queue->dev;
+ struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
int tx_cleaned = 0;
int rx_cleaned = 0;
unsigned long flags;
@@ -1992,14 +2061,15 @@ static int gfar_poll(struct napi_struct *napi, int budget)
/* Clear IEVENT, so interrupts aren't called again
* because of the packets that have already arrived */
gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK);
+ tx_queue = priv->tx_queue;
/* If we fail to get the lock, don't bother with the TX BDs */
- if (spin_trylock_irqsave(&priv->txlock, flags)) {
- tx_cleaned = gfar_clean_tx_ring(dev);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ if (spin_trylock_irqsave(&tx_queue->txlock, flags)) {
+ tx_cleaned = gfar_clean_tx_ring(tx_queue);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
}
- rx_cleaned = gfar_clean_rx_ring(dev, budget);
+ rx_cleaned = gfar_clean_rx_ring(rx_queue, budget);
if (tx_cleaned)
return budget;
@@ -2014,13 +2084,13 @@ static int gfar_poll(struct napi_struct *napi, int budget)
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
- if (likely(priv->rxcoalescing)) {
+ if (likely(rx_queue->rxcoalescing)) {
gfar_write(&priv->regs->rxic, 0);
- gfar_write(&priv->regs->rxic, priv->rxic);
+ gfar_write(&priv->regs->rxic, rx_queue->rxic);
}
- if (likely(priv->txcoalescing)) {
+ if (likely(tx_queue->txcoalescing)) {
gfar_write(&priv->regs->txic, 0);
- gfar_write(&priv->regs->txic, priv->txic);
+ gfar_write(&priv->regs->txic, tx_queue->txic);
}
}
@@ -2087,12 +2157,14 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id)
static void adjust_link(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar __iomem *regs = priv->regs;
unsigned long flags;
struct phy_device *phydev = priv->phydev;
int new_state = 0;
- spin_lock_irqsave(&priv->txlock, flags);
+ tx_queue = priv->tx_queue;
+ spin_lock_irqsave(&tx_queue->txlock, flags);
if (phydev->link) {
u32 tempval = gfar_read(®s->maccfg2);
u32 ecntrl = gfar_read(®s->ecntrl);
@@ -2158,7 +2230,7 @@ static void adjust_link(struct net_device *dev)
if (new_state && netif_msg_link(priv))
phy_print_status(phydev);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
}
/* Update the hash table based on the current list of multicast
@@ -2172,7 +2244,7 @@ static void gfar_set_multi(struct net_device *dev)
struct gfar __iomem *regs = priv->regs;
u32 tempval;
- if(dev->flags & IFF_PROMISC) {
+ if (dev->flags & IFF_PROMISC) {
/* Set RCTRL to PROM */
tempval = gfar_read(®s->rctrl);
tempval |= RCTRL_PROM;
@@ -2184,7 +2256,7 @@ static void gfar_set_multi(struct net_device *dev)
gfar_write(®s->rctrl, tempval);
}
- if(dev->flags & IFF_ALLMULTI) {
+ if (dev->flags & IFF_ALLMULTI) {
/* Set the hash to rx all multicast frames */
gfar_write(®s->igaddr0, 0xffffffff);
gfar_write(®s->igaddr1, 0xffffffff);
@@ -2236,7 +2308,7 @@ static void gfar_set_multi(struct net_device *dev)
em_num = 0;
}
- if(dev->mc_count == 0)
+ if (dev->mc_count == 0)
return;
/* Parse the list, and set the appropriate bits */
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 05732fa..a60f93f 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -7,8 +7,9 @@
*
* Author: Andy Fleming
* Maintainer: Kumar Gala
+ * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
+ * Copyright 2002-2009 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -699,6 +700,76 @@ struct gfar {
#define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200
#define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400
+/**
+ * struct gfar_priv_tx_q - per tx queue structure
+ * @txlock: per queue tx spin lock
+ * @tx_skbuff:skb pointers
+ * @skb_curtx: to be used skb pointer
+ * @skb_dirtytx:the last used skb pointer
+ * @qindex: index of this queue
+ * @dev: back pointer to the dev structure
+ * @grp: back pointer to the group to which this queue belongs
+ * @tx_bd_base: First tx buffer descriptor
+ * @cur_tx: Next free ring entry
+ * @dirty_tx: First buffer in line to be transmitted
+ * @tx_ring_size: Tx ring size
+ * @num_txbdfree: number of free TxBds
+ * @txcoalescing: enable/disable tx coalescing
+ * @txic: transmit interrupt coalescing value
+ * @txcount: coalescing value if based on tx frame count
+ * @txtime: coalescing value if based on time
+ */
+struct gfar_priv_tx_q {
+ spinlock_t txlock __attribute__ ((aligned (SMP_CACHE_BYTES)));
+ struct sk_buff ** tx_skbuff;
+ /* Buffer descriptor pointers */
+ dma_addr_t tx_bd_dma_base;
+ struct txbd8 *tx_bd_base;
+ struct txbd8 *cur_tx;
+ struct txbd8 *dirty_tx;
+ struct net_device *dev;
+ u16 skb_curtx;
+ u16 skb_dirtytx;
+ u16 qindex;
+ unsigned int tx_ring_size;
+ unsigned int num_txbdfree;
+ /* Configuration info for the coalescing features */
+ unsigned char txcoalescing;
+ unsigned long txic;
+ unsigned short txcount;
+ unsigned short txtime;
+};
+
+/**
+ * struct gfar_priv_rx_q - per rx queue structure
+ * @rxlock: per queue rx spin lock
+ * @napi: the napi poll function
+ * @rx_skbuff: skb pointers
+ * @skb_currx: currently use skb pointer
+ * @rx_bd_base: First rx buffer descriptor
+ * @cur_rx: Next free rx ring entry
+ * @qindex: index of this queue
+ * @dev: back pointer to the dev structure
+ * @rx_ring_size: Rx ring size
+ * @rxcoalescing: enable/disable rx-coalescing
+ * @rxic: receive interrupt coalescing vlaue
+ */
+
+struct gfar_priv_rx_q {
+ spinlock_t rxlock __attribute__ ((aligned (SMP_CACHE_BYTES)));
+ struct napi_struct napi;
+ struct sk_buff ** rx_skbuff;
+ struct rxbd8 *rx_bd_base;
+ struct rxbd8 *cur_rx;
+ struct net_device *dev;
+ u16 skb_currx;
+ u16 qindex;
+ unsigned int rx_ring_size;
+ /* RX Coalescing values */
+ unsigned char rxcoalescing;
+ unsigned long rxic;
+};
+
/* Struct stolen almost completely (and shamelessly) from the FCC enet source
* (Ok, that's not so true anymore, but there is a family resemblence)
* The GFAR buffer descriptors track the ring buffers. The rx_bd_base
@@ -709,52 +780,15 @@ struct gfar {
* the buffer descriptor determines the actual condition.
*/
struct gfar_private {
- /* Fields controlled by TX lock */
- spinlock_t txlock;
-
- /* Pointer to the array of skbuffs */
- struct sk_buff ** tx_skbuff;
-
- /* next free skb in the array */
- u16 skb_curtx;
-
- /* First skb in line to be transmitted */
- u16 skb_dirtytx;
-
- /* Configuration info for the coalescing features */
- unsigned char txcoalescing;
- unsigned long txic;
-
- /* Buffer descriptor pointers */
- dma_addr_t tx_bd_dma_base;
- struct txbd8 *tx_bd_base; /* First tx buffer descriptor */
- struct txbd8 *cur_tx; /* Next free ring entry */
- struct txbd8 *dirty_tx; /* First buffer in line
- to be transmitted */
- unsigned int tx_ring_size;
- unsigned int num_txbdfree; /* number of TxBDs free */
-
- /* RX Locked fields */
- spinlock_t rxlock;
struct device_node *node;
struct net_device *ndev;
struct of_device *ofdev;
- struct napi_struct napi;
-
- /* skb array and index */
- struct sk_buff ** rx_skbuff;
- u16 skb_currx;
-
- /* RX Coalescing values */
- unsigned char rxcoalescing;
- unsigned long rxic;
- struct rxbd8 *rx_bd_base; /* First Rx buffers */
- struct rxbd8 *cur_rx; /* Next free rx ring entry */
+ struct gfar_priv_tx_q *tx_queue;
+ struct gfar_priv_rx_q *rx_queue;
- /* RX parameters */
- unsigned int rx_ring_size;
+ /* RX per device parameters */
unsigned int rx_buffer_size;
unsigned int rx_stash_size;
unsigned int rx_stash_index;
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index 6c144b5..6d0d171 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -7,8 +7,9 @@
*
* Author: Andy Fleming
* Maintainer: Kumar Gala
+ * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright (c) 2003,2004 Freescale Semiconductor, Inc.
+ * Copyright 2003-2006, 2008-2009 Freescale Semiconductor, Inc.
*
* This software may be used and distributed according to
* the terms of the GNU Public License, Version 2, incorporated herein
@@ -41,7 +42,7 @@
#include "gianfar.h"
extern void gfar_start(struct net_device *dev);
-extern int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
+extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit);
#define GFAR_MAX_COAL_USECS 0xffff
#define GFAR_MAX_COAL_FRAMES 0xff
@@ -197,12 +198,16 @@ static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct gfar_private *priv = netdev_priv(dev);
struct phy_device *phydev = priv->phydev;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar_priv_tx_q *tx_queue = NULL;
if (NULL == phydev)
return -ENODEV;
+ tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue;
- cmd->maxtxpkt = get_icft_value(priv->txic);
- cmd->maxrxpkt = get_icft_value(priv->rxic);
+ cmd->maxtxpkt = get_icft_value(tx_queue->txic);
+ cmd->maxrxpkt = get_icft_value(rx_queue->rxic);
return phy_ethtool_gset(phydev, cmd);
}
@@ -279,6 +284,8 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic
static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar_priv_tx_q *tx_queue = NULL;
unsigned long rxtime;
unsigned long rxcount;
unsigned long txtime;
@@ -290,10 +297,13 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
if (NULL == priv->phydev)
return -ENODEV;
- rxtime = get_ictt_value(priv->rxic);
- rxcount = get_icft_value(priv->rxic);
- txtime = get_ictt_value(priv->txic);
- txcount = get_icft_value(priv->txic);
+ rx_queue = priv->rx_queue;
+ tx_queue = priv->tx_queue;
+
+ rxtime = get_ictt_value(rx_queue->rxic);
+ rxcount = get_icft_value(rx_queue->rxic);
+ txtime = get_ictt_value(tx_queue->txic);
+ txcount = get_icft_value(tx_queue->txic);
cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, rxtime);
cvals->rx_max_coalesced_frames = rxcount;
@@ -339,16 +349,21 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
+ tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue;
+
/* Set up rx coalescing */
if ((cvals->rx_coalesce_usecs == 0) ||
(cvals->rx_max_coalesced_frames == 0))
- priv->rxcoalescing = 0;
+ rx_queue->rxcoalescing = 0;
else
- priv->rxcoalescing = 1;
+ rx_queue->rxcoalescing = 1;
if (NULL == priv->phydev)
return -ENODEV;
@@ -366,15 +381,15 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- priv->rxic = mk_ic_value(cvals->rx_max_coalesced_frames,
+ rx_queue->rxic = mk_ic_value(cvals->rx_max_coalesced_frames,
gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs));
/* Set up tx coalescing */
if ((cvals->tx_coalesce_usecs == 0) ||
(cvals->tx_max_coalesced_frames == 0))
- priv->txcoalescing = 0;
+ tx_queue->txcoalescing = 0;
else
- priv->txcoalescing = 1;
+ tx_queue->txcoalescing = 1;
/* Check the bounds of the values */
if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
@@ -389,16 +404,16 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- priv->txic = mk_ic_value(cvals->tx_max_coalesced_frames,
+ tx_queue->txic = mk_ic_value(cvals->tx_max_coalesced_frames,
gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
gfar_write(&priv->regs->rxic, 0);
- if (priv->rxcoalescing)
- gfar_write(&priv->regs->rxic, priv->rxic);
+ if (rx_queue->rxcoalescing)
+ gfar_write(&priv->regs->rxic, rx_queue->rxic);
gfar_write(&priv->regs->txic, 0);
- if (priv->txcoalescing)
- gfar_write(&priv->regs->txic, priv->txic);
+ if (tx_queue->txcoalescing)
+ gfar_write(&priv->regs->txic, tx_queue->txic);
return 0;
}
@@ -409,6 +424,11 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+
+ tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue;
rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
@@ -418,10 +438,10 @@ static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rv
/* Values changeable by the user. The valid values are
* in the range 1 to the "*_max_pending" counterpart above.
*/
- rvals->rx_pending = priv->rx_ring_size;
- rvals->rx_mini_pending = priv->rx_ring_size;
- rvals->rx_jumbo_pending = priv->rx_ring_size;
- rvals->tx_pending = priv->tx_ring_size;
+ rvals->rx_pending = rx_queue->rx_ring_size;
+ rvals->rx_mini_pending = rx_queue->rx_ring_size;
+ rvals->rx_jumbo_pending = rx_queue->rx_ring_size;
+ rvals->tx_pending = tx_queue->tx_ring_size;
}
/* Change the current ring parameters, stopping the controller if
@@ -431,6 +451,8 @@ static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rv
static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
int err = 0;
if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
@@ -451,29 +473,32 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva
return -EINVAL;
}
+ tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue;
+
if (dev->flags & IFF_UP) {
unsigned long flags;
/* Halt TX and RX, and process the frames which
* have already been received */
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
+ spin_lock_irqsave(&tx_queue->txlock, flags);
+ spin_lock(&rx_queue->rxlock);
gfar_halt(dev);
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock(&rx_queue->rxlock);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
- gfar_clean_rx_ring(dev, priv->rx_ring_size);
+ gfar_clean_rx_ring(rx_queue, rx_queue->rx_ring_size);
/* Now we take down the rings to rebuild them */
stop_gfar(dev);
}
/* Change the size */
- priv->rx_ring_size = rvals->rx_pending;
- priv->tx_ring_size = rvals->tx_pending;
- priv->num_txbdfree = priv->tx_ring_size;
+ rx_queue->rx_ring_size = rvals->rx_pending;
+ tx_queue->tx_ring_size = rvals->tx_pending;
+ tx_queue->num_txbdfree = tx_queue->tx_ring_size;
/* Rebuild the rings with the new size */
if (dev->flags & IFF_UP) {
@@ -486,24 +511,29 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva
static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar_priv_tx_q *tx_queue = NULL;
unsigned long flags;
int err = 0;
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return -EOPNOTSUPP;
+ tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue;
+
if (dev->flags & IFF_UP) {
/* Halt TX and RX, and process the frames which
* have already been received */
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
+ spin_lock_irqsave(&tx_queue->txlock, flags);
+ spin_lock(&rx_queue->rxlock);
gfar_halt(dev);
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock(&rx_queue->rxlock);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
- gfar_clean_rx_ring(dev, priv->rx_ring_size);
+ gfar_clean_rx_ring(rx_queue, rx_queue->rx_ring_size);
/* Now we take down the rings to rebuild them */
stop_gfar(dev);
diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c
index dd26da7..9c664f8 100644
--- a/drivers/net/gianfar_sysfs.c
+++ b/drivers/net/gianfar_sysfs.c
@@ -8,8 +8,9 @@
*
* Author: Andy Fleming
* Maintainer: Kumar Gala (galak@kernel.crashing.org)
+ * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright (c) 2002-2005 Freescale Semiconductor, Inc.
+ * Copyright 2002-2009 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -49,6 +50,7 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar_priv_rx_q *rx_queue = NULL;
int new_setting = 0;
u32 temp;
unsigned long flags;
@@ -56,6 +58,8 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BD_STASHING))
return count;
+ rx_queue = priv->rx_queue;
+
/* Find out the new setting */
if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
new_setting = 1;
@@ -65,7 +69,7 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
else
return count;
- spin_lock_irqsave(&priv->rxlock, flags);
+ spin_lock_irqsave(&rx_queue->rxlock, flags);
/* Set the new stashing value */
priv->bd_stash_en = new_setting;
@@ -79,7 +83,7 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
gfar_write(&priv->regs->attr, temp);
- spin_unlock_irqrestore(&priv->rxlock, flags);
+ spin_unlock_irqrestore(&rx_queue->rxlock, flags);
return count;
}
@@ -99,6 +103,7 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar_priv_rx_q *rx_queue = NULL;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -106,7 +111,9 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
return count;
- spin_lock_irqsave(&priv->rxlock, flags);
+ rx_queue = priv->rx_queue;
+
+ spin_lock_irqsave(&rx_queue->rxlock, flags);
if (length > priv->rx_buffer_size)
goto out;
@@ -131,7 +138,7 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
gfar_write(&priv->regs->attr, temp);
out:
- spin_unlock_irqrestore(&priv->rxlock, flags);
+ spin_unlock_irqrestore(&rx_queue->rxlock, flags);
return count;
}
@@ -154,6 +161,7 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar_priv_rx_q *rx_queue = NULL;
unsigned short index = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -161,7 +169,9 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
return count;
- spin_lock_irqsave(&priv->rxlock, flags);
+ rx_queue = priv->rx_queue;
+
+ spin_lock_irqsave(&rx_queue->rxlock, flags);
if (index > priv->rx_stash_size)
goto out;
@@ -176,7 +186,7 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
gfar_write(&priv->regs->attreli, flags);
out:
- spin_unlock_irqrestore(&priv->rxlock, flags);
+ spin_unlock_irqrestore(&rx_queue->rxlock, flags);
return count;
}
@@ -198,6 +208,7 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -205,7 +216,9 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
if (length > GFAR_MAX_FIFO_THRESHOLD)
return count;
- spin_lock_irqsave(&priv->txlock, flags);
+ tx_queue = priv->tx_queue;
+
+ spin_lock_irqsave(&tx_queue->txlock, flags);
priv->fifo_threshold = length;
@@ -214,7 +227,7 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
temp |= length;
gfar_write(&priv->regs->fifo_tx_thr, temp);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
return count;
}
@@ -235,6 +248,7 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -242,7 +256,8 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
if (num > GFAR_MAX_FIFO_STARVE)
return count;
- spin_lock_irqsave(&priv->txlock, flags);
+ tx_queue = priv->tx_queue;
+ spin_lock_irqsave(&tx_queue->txlock, flags);
priv->fifo_starve = num;
@@ -251,7 +266,7 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
temp |= num;
gfar_write(&priv->regs->fifo_tx_starve, temp);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
return count;
}
@@ -273,6 +288,7 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -280,7 +296,8 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
if (num > GFAR_MAX_FIFO_STARVE_OFF)
return count;
- spin_lock_irqsave(&priv->txlock, flags);
+ tx_queue = priv->tx_queue;
+ spin_lock_irqsave(&tx_queue->txlock, flags);
priv->fifo_starve_off = num;
@@ -289,7 +306,7 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
temp |= num;
gfar_write(&priv->regs->fifo_tx_starve_shutoff, temp);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
return count;
}
--
1.5.2.2
^ permalink raw reply related
* [PATCH v2 2/7] gianfar: Introduce logical group support.
From: Sandeep Gopalpet @ 2009-10-27 16:54 UTC (permalink / raw)
To: netdev; +Cc: davem, Sandeep Gopalpet
This patch introduces the group structure. The elements of this
structure are the interrupt lines, their corresponding names,
the register memory map.
The elements for this group are factored out from the gfar_private
structure. The introduction of group structure will help in
providing support for newer versions of etsec.
Currently, the support is present only for single group and
single tx/rx queues.
Signed-off-by: Sandeep Gopalpet <sandeep.kumar@freescale.com>
---
drivers/net/gianfar.c | 364 ++++++++++++++++++++++-------------------
drivers/net/gianfar.h | 38 +++--
drivers/net/gianfar_ethtool.c | 14 +-
drivers/net/gianfar_sysfs.c | 34 +++--
4 files changed, 250 insertions(+), 200 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 4b1c023..1ebbbb7 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -151,7 +151,6 @@ MODULE_LICENSE("GPL");
static void gfar_init_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp,
dma_addr_t buf)
{
- struct net_device *dev = rx_queue->dev;
u32 lstatus;
bdp->bufPtr = buf;
@@ -290,9 +289,9 @@ cleanup:
static void gfar_init_mac(struct net_device *ndev)
{
struct gfar_private *priv = netdev_priv(ndev);
- struct gfar __iomem *regs = priv->regs;
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar __iomem *regs = priv->gfargrp.regs;
u32 rctrl = 0;
u32 tctrl = 0;
u32 attrs = 0;
@@ -407,24 +406,25 @@ static int gfar_of_init(struct net_device *dev)
/* get a pointer to the register memory */
addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
- priv->regs = ioremap(addr, size);
+ priv->gfargrp.regs = ioremap(addr, size);
- if (priv->regs == NULL)
+ if (priv->gfargrp.regs == NULL)
return -ENOMEM;
- priv->interruptTransmit = irq_of_parse_and_map(np, 0);
+ priv->gfargrp.priv = priv; /* back pointer from group to priv */
+ priv->gfargrp.interruptTransmit = irq_of_parse_and_map(np, 0);
model = of_get_property(np, "model", NULL);
/* If we aren't the FEC we have multiple interrupts */
if (model && strcasecmp(model, "FEC")) {
- priv->interruptReceive = irq_of_parse_and_map(np, 1);
+ priv->gfargrp.interruptReceive = irq_of_parse_and_map(np, 1);
- priv->interruptError = irq_of_parse_and_map(np, 2);
+ priv->gfargrp.interruptError = irq_of_parse_and_map(np, 2);
- if (priv->interruptTransmit < 0 ||
- priv->interruptReceive < 0 ||
- priv->interruptError < 0) {
+ if (priv->gfargrp.interruptTransmit < 0 ||
+ priv->gfargrp.interruptReceive < 0 ||
+ priv->gfargrp.interruptError < 0) {
err = -EINVAL;
goto err_out;
}
@@ -491,7 +491,7 @@ static int gfar_of_init(struct net_device *dev)
return 0;
err_out:
- iounmap(priv->regs);
+ iounmap(priv->gfargrp.regs);
return err;
}
@@ -517,6 +517,7 @@ static int gfar_probe(struct of_device *ofdev,
u32 tempval;
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
+ struct gfar __iomem *regs = NULL;
int err = 0;
int len_devname;
@@ -549,32 +550,34 @@ static int gfar_probe(struct of_device *ofdev,
spin_lock_init(&priv->tx_queue->txlock);
spin_lock_init(&priv->rx_queue->rxlock);
+ spin_lock_init(&priv->gfargrp.grplock);
spin_lock_init(&priv->bflock);
INIT_WORK(&priv->reset_task, gfar_reset_task);
dev_set_drvdata(&ofdev->dev, priv);
+ regs = priv->gfargrp.regs;
/* Stop the DMA engine now, in case it was running before */
/* (The firmware could have used it, and left it running). */
gfar_halt(dev);
/* Reset MAC layer */
- gfar_write(&priv->regs->maccfg1, MACCFG1_SOFT_RESET);
+ gfar_write(®s->maccfg1, MACCFG1_SOFT_RESET);
/* We need to delay at least 3 TX clocks */
udelay(2);
tempval = (MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
- gfar_write(&priv->regs->maccfg1, tempval);
+ gfar_write(®s->maccfg1, tempval);
/* Initialize MACCFG2. */
- gfar_write(&priv->regs->maccfg2, MACCFG2_INIT_SETTINGS);
+ gfar_write(®s->maccfg2, MACCFG2_INIT_SETTINGS);
/* Initialize ECNTRL */
- gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS);
+ gfar_write(®s->ecntrl, ECNTRL_INIT_SETTINGS);
/* Set the dev->base_addr to the gfar reg region */
- dev->base_addr = (unsigned long) (priv->regs);
+ dev->base_addr = (unsigned long) regs;
SET_NETDEV_DEV(dev, &ofdev->dev);
@@ -602,35 +605,35 @@ static int gfar_probe(struct of_device *ofdev,
priv->extended_hash = 1;
priv->hash_width = 9;
- priv->hash_regs[0] = &priv->regs->igaddr0;
- priv->hash_regs[1] = &priv->regs->igaddr1;
- priv->hash_regs[2] = &priv->regs->igaddr2;
- priv->hash_regs[3] = &priv->regs->igaddr3;
- priv->hash_regs[4] = &priv->regs->igaddr4;
- priv->hash_regs[5] = &priv->regs->igaddr5;
- priv->hash_regs[6] = &priv->regs->igaddr6;
- priv->hash_regs[7] = &priv->regs->igaddr7;
- priv->hash_regs[8] = &priv->regs->gaddr0;
- priv->hash_regs[9] = &priv->regs->gaddr1;
- priv->hash_regs[10] = &priv->regs->gaddr2;
- priv->hash_regs[11] = &priv->regs->gaddr3;
- priv->hash_regs[12] = &priv->regs->gaddr4;
- priv->hash_regs[13] = &priv->regs->gaddr5;
- priv->hash_regs[14] = &priv->regs->gaddr6;
- priv->hash_regs[15] = &priv->regs->gaddr7;
+ priv->hash_regs[0] = ®s->igaddr0;
+ priv->hash_regs[1] = ®s->igaddr1;
+ priv->hash_regs[2] = ®s->igaddr2;
+ priv->hash_regs[3] = ®s->igaddr3;
+ priv->hash_regs[4] = ®s->igaddr4;
+ priv->hash_regs[5] = ®s->igaddr5;
+ priv->hash_regs[6] = ®s->igaddr6;
+ priv->hash_regs[7] = ®s->igaddr7;
+ priv->hash_regs[8] = ®s->gaddr0;
+ priv->hash_regs[9] = ®s->gaddr1;
+ priv->hash_regs[10] = ®s->gaddr2;
+ priv->hash_regs[11] = ®s->gaddr3;
+ priv->hash_regs[12] = ®s->gaddr4;
+ priv->hash_regs[13] = ®s->gaddr5;
+ priv->hash_regs[14] = ®s->gaddr6;
+ priv->hash_regs[15] = ®s->gaddr7;
} else {
priv->extended_hash = 0;
priv->hash_width = 8;
- priv->hash_regs[0] = &priv->regs->gaddr0;
- priv->hash_regs[1] = &priv->regs->gaddr1;
- priv->hash_regs[2] = &priv->regs->gaddr2;
- priv->hash_regs[3] = &priv->regs->gaddr3;
- priv->hash_regs[4] = &priv->regs->gaddr4;
- priv->hash_regs[5] = &priv->regs->gaddr5;
- priv->hash_regs[6] = &priv->regs->gaddr6;
- priv->hash_regs[7] = &priv->regs->gaddr7;
+ priv->hash_regs[0] = ®s->gaddr0;
+ priv->hash_regs[1] = ®s->gaddr1;
+ priv->hash_regs[2] = ®s->gaddr2;
+ priv->hash_regs[3] = ®s->gaddr3;
+ priv->hash_regs[4] = ®s->gaddr4;
+ priv->hash_regs[5] = ®s->gaddr5;
+ priv->hash_regs[6] = ®s->gaddr6;
+ priv->hash_regs[7] = ®s->gaddr7;
}
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_PADDING)
@@ -672,20 +675,20 @@ static int gfar_probe(struct of_device *ofdev,
/* fill out IRQ number and name fields */
len_devname = strlen(dev->name);
- strncpy(&priv->int_name_tx[0], dev->name, len_devname);
+ strncpy(&priv->gfargrp.int_name_tx[0], dev->name, len_devname);
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- strncpy(&priv->int_name_tx[len_devname],
+ strncpy(&priv->gfargrp.int_name_tx[len_devname],
"_tx", sizeof("_tx") + 1);
- strncpy(&priv->int_name_rx[0], dev->name, len_devname);
- strncpy(&priv->int_name_rx[len_devname],
+ strncpy(&priv->gfargrp.int_name_rx[0], dev->name, len_devname);
+ strncpy(&priv->gfargrp.int_name_rx[len_devname],
"_rx", sizeof("_rx") + 1);
- strncpy(&priv->int_name_er[0], dev->name, len_devname);
- strncpy(&priv->int_name_er[len_devname],
+ strncpy(&priv->gfargrp.int_name_er[0], dev->name, len_devname);
+ strncpy(&priv->gfargrp.int_name_er[len_devname],
"_er", sizeof("_er") + 1);
} else
- priv->int_name_tx[len_devname] = '\0';
+ priv->gfargrp.int_name_tx[len_devname] = '\0';
/* Create all the sysfs files */
gfar_init_sysfs(dev);
@@ -702,7 +705,7 @@ static int gfar_probe(struct of_device *ofdev,
return 0;
register_fail:
- iounmap(priv->regs);
+ iounmap(priv->gfargrp.regs);
kfree(priv->rx_queue);
rx_queue_fail:
kfree(priv->tx_queue);
@@ -727,7 +730,7 @@ static int gfar_remove(struct of_device *ofdev)
dev_set_drvdata(&ofdev->dev, NULL);
unregister_netdev(priv->ndev);
- iounmap(priv->regs);
+ iounmap(priv->gfargrp.regs);
free_netdev(priv->ndev);
return 0;
@@ -741,6 +744,7 @@ static int gfar_suspend(struct device *dev)
struct net_device *ndev = priv->ndev;
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar __iomem *regs = NULL;
unsigned long flags;
u32 tempval;
@@ -750,6 +754,7 @@ static int gfar_suspend(struct device *dev)
netif_device_detach(ndev);
tx_queue = priv->tx_queue;
rx_queue = priv->rx_queue;
+ regs = priv->gfargrp.regs;
if (netif_running(ndev)) {
spin_lock_irqsave(&tx_queue->txlock, flags);
@@ -758,14 +763,14 @@ static int gfar_suspend(struct device *dev)
gfar_halt_nodisable(ndev);
/* Disable Tx, and Rx if wake-on-LAN is disabled. */
- tempval = gfar_read(&priv->regs->maccfg1);
+ tempval = gfar_read(®s->maccfg1);
tempval &= ~MACCFG1_TX_EN;
if (!magic_packet)
tempval &= ~MACCFG1_RX_EN;
- gfar_write(&priv->regs->maccfg1, tempval);
+ gfar_write(®s->maccfg1, tempval);
spin_unlock(&rx_queue->rxlock);
spin_unlock_irqrestore(&tx_queue->txlock, flags);
@@ -774,12 +779,12 @@ static int gfar_suspend(struct device *dev)
if (magic_packet) {
/* Enable interrupt on Magic Packet */
- gfar_write(&priv->regs->imask, IMASK_MAG);
+ gfar_write(®s->imask, IMASK_MAG);
/* Enable Magic Packet mode */
- tempval = gfar_read(&priv->regs->maccfg2);
+ tempval = gfar_read(®s->maccfg2);
tempval |= MACCFG2_MPEN;
- gfar_write(&priv->regs->maccfg2, tempval);
+ gfar_write(®s->maccfg2, tempval);
} else {
phy_stop(priv->phydev);
}
@@ -794,6 +799,7 @@ static int gfar_resume(struct device *dev)
struct net_device *ndev = priv->ndev;
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar __iomem *regs = NULL;
unsigned long flags;
u32 tempval;
int magic_packet = priv->wol_en &&
@@ -812,13 +818,14 @@ static int gfar_resume(struct device *dev)
*/
rx_queue = priv->rx_queue;
tx_queue = priv->tx_queue;
+ regs = priv->gfargrp.regs;
spin_lock_irqsave(&tx_queue->txlock, flags);
spin_lock(&rx_queue->rxlock);
- tempval = gfar_read(&priv->regs->maccfg2);
+ tempval = gfar_read(®s->maccfg2);
tempval &= ~MACCFG2_MPEN;
- gfar_write(&priv->regs->maccfg2, tempval);
+ gfar_write(®s->maccfg2, tempval);
gfar_start(ndev);
@@ -893,7 +900,11 @@ static int gfar_legacy_resume(struct of_device *ofdev)
static phy_interface_t gfar_get_interface(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- u32 ecntrl = gfar_read(&priv->regs->ecntrl);
+ struct gfar __iomem *regs = NULL;
+ u32 ecntrl;
+
+ regs = priv->gfargrp.regs;
+ ecntrl = gfar_read(®s->ecntrl);
if (ecntrl & ECNTRL_SGMII_MODE)
return PHY_INTERFACE_MODE_SGMII;
@@ -1015,46 +1026,48 @@ static void gfar_configure_serdes(struct net_device *dev)
static void init_registers(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = NULL;
+ regs = priv->gfargrp.regs;
/* Clear IEVENT */
- gfar_write(&priv->regs->ievent, IEVENT_INIT_CLEAR);
+ gfar_write(®s->ievent, IEVENT_INIT_CLEAR);
/* Initialize IMASK */
- gfar_write(&priv->regs->imask, IMASK_INIT_CLEAR);
+ gfar_write(®s->imask, IMASK_INIT_CLEAR);
/* Init hash registers to zero */
- gfar_write(&priv->regs->igaddr0, 0);
- gfar_write(&priv->regs->igaddr1, 0);
- gfar_write(&priv->regs->igaddr2, 0);
- gfar_write(&priv->regs->igaddr3, 0);
- gfar_write(&priv->regs->igaddr4, 0);
- gfar_write(&priv->regs->igaddr5, 0);
- gfar_write(&priv->regs->igaddr6, 0);
- gfar_write(&priv->regs->igaddr7, 0);
-
- gfar_write(&priv->regs->gaddr0, 0);
- gfar_write(&priv->regs->gaddr1, 0);
- gfar_write(&priv->regs->gaddr2, 0);
- gfar_write(&priv->regs->gaddr3, 0);
- gfar_write(&priv->regs->gaddr4, 0);
- gfar_write(&priv->regs->gaddr5, 0);
- gfar_write(&priv->regs->gaddr6, 0);
- gfar_write(&priv->regs->gaddr7, 0);
+ gfar_write(®s->igaddr0, 0);
+ gfar_write(®s->igaddr1, 0);
+ gfar_write(®s->igaddr2, 0);
+ gfar_write(®s->igaddr3, 0);
+ gfar_write(®s->igaddr4, 0);
+ gfar_write(®s->igaddr5, 0);
+ gfar_write(®s->igaddr6, 0);
+ gfar_write(®s->igaddr7, 0);
+
+ gfar_write(®s->gaddr0, 0);
+ gfar_write(®s->gaddr1, 0);
+ gfar_write(®s->gaddr2, 0);
+ gfar_write(®s->gaddr3, 0);
+ gfar_write(®s->gaddr4, 0);
+ gfar_write(®s->gaddr5, 0);
+ gfar_write(®s->gaddr6, 0);
+ gfar_write(®s->gaddr7, 0);
/* Zero out the rmon mib registers if it has them */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
- memset_io(&(priv->regs->rmon), 0, sizeof (struct rmon_mib));
+ memset_io(&(regs->rmon), 0, sizeof (struct rmon_mib));
/* Mask off the CAM interrupts */
- gfar_write(&priv->regs->rmon.cam1, 0xffffffff);
- gfar_write(&priv->regs->rmon.cam2, 0xffffffff);
+ gfar_write(®s->rmon.cam1, 0xffffffff);
+ gfar_write(®s->rmon.cam2, 0xffffffff);
}
/* Initialize the max receive buffer length */
- gfar_write(&priv->regs->mrblr, priv->rx_buffer_size);
+ gfar_write(®s->mrblr, priv->rx_buffer_size);
/* Initialize the Minimum Frame Length Register */
- gfar_write(&priv->regs->minflr, MINFLR_INIT_SETTINGS);
+ gfar_write(®s->minflr, MINFLR_INIT_SETTINGS);
}
@@ -1062,7 +1075,7 @@ static void init_registers(struct net_device *dev)
static void gfar_halt_nodisable(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->regs;
+ struct gfar __iomem *regs = priv->gfargrp.regs;
u32 tempval;
/* Mask all interrupts */
@@ -1072,13 +1085,13 @@ static void gfar_halt_nodisable(struct net_device *dev)
gfar_write(®s->ievent, IEVENT_INIT_CLEAR);
/* Stop the DMA, and wait for it to stop */
- tempval = gfar_read(&priv->regs->dmactrl);
+ tempval = gfar_read(®s->dmactrl);
if ((tempval & (DMACTRL_GRS | DMACTRL_GTS))
!= (DMACTRL_GRS | DMACTRL_GTS)) {
tempval |= (DMACTRL_GRS | DMACTRL_GTS);
- gfar_write(&priv->regs->dmactrl, tempval);
+ gfar_write(®s->dmactrl, tempval);
- while (!(gfar_read(&priv->regs->ievent) &
+ while (!(gfar_read(®s->ievent) &
(IEVENT_GRSC | IEVENT_GTSC)))
cpu_relax();
}
@@ -1088,7 +1101,7 @@ static void gfar_halt_nodisable(struct net_device *dev)
void gfar_halt(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->regs;
+ struct gfar __iomem *regs = priv->gfargrp.regs;
u32 tempval;
gfar_halt_nodisable(dev);
@@ -1122,11 +1135,11 @@ void stop_gfar(struct net_device *dev)
/* Free the IRQs */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- free_irq(priv->interruptError, dev);
- free_irq(priv->interruptTransmit, dev);
- free_irq(priv->interruptReceive, dev);
+ free_irq(priv->gfargrp.interruptError, &priv->gfargrp);
+ free_irq(priv->gfargrp.interruptTransmit, &priv->gfargrp);
+ free_irq(priv->gfargrp.interruptReceive, &priv->gfargrp);
} else {
- free_irq(priv->interruptTransmit, dev);
+ free_irq(priv->gfargrp.interruptTransmit, &priv->gfargrp);
}
free_skb_resources(priv);
@@ -1201,9 +1214,7 @@ skip_rx_skbuff:
void gfar_start(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_tx_q *tx_queue;
- struct gfar_priv_rx_q *rx_queue;
- struct gfar __iomem *regs = priv->regs;
+ struct gfar __iomem *regs = priv->gfargrp.regs;
u32 tempval;
/* Enable Rx and Tx in MACCFG1 */
@@ -1212,14 +1223,14 @@ void gfar_start(struct net_device *dev)
gfar_write(®s->maccfg1, tempval);
/* Initialize DMACTRL to have WWR and WOP */
- tempval = gfar_read(&priv->regs->dmactrl);
+ tempval = gfar_read(®s->dmactrl);
tempval |= DMACTRL_INIT_SETTINGS;
- gfar_write(&priv->regs->dmactrl, tempval);
+ gfar_write(®s->dmactrl, tempval);
/* Make sure we aren't stopped */
- tempval = gfar_read(&priv->regs->dmactrl);
+ tempval = gfar_read(®s->dmactrl);
tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
- gfar_write(&priv->regs->dmactrl, tempval);
+ gfar_write(®s->dmactrl, tempval);
/* Clear THLT/RHLT, so that the DMA starts polling now */
gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
@@ -1235,7 +1246,7 @@ void gfar_start(struct net_device *dev)
int startup_gfar(struct net_device *ndev)
{
struct gfar_private *priv = netdev_priv(ndev);
- struct gfar __iomem *regs = priv->regs;
+ struct gfar __iomem *regs = priv->gfargrp.regs;
int err;
gfar_write(®s->imask, IMASK_INIT_CLEAR);
@@ -1251,39 +1262,46 @@ int startup_gfar(struct net_device *ndev)
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
/* Install our interrupt handlers for Error,
* Transmit, and Receive */
- err = request_irq(priv->interruptError, gfar_error, 0,
- priv->int_name_er, ndev);
+ err = request_irq(priv->gfargrp.interruptError, gfar_error, 0,
+ priv->gfargrp.int_name_er, &priv->gfargrp);
if (err) {
if (netif_msg_intr(priv))
pr_err("%s: Can't get IRQ %d\n", ndev->name,
- priv->interruptError);
+ priv->gfargrp.interruptError);
goto err_irq_fail;
}
- err = request_irq(priv->interruptTransmit, gfar_transmit, 0,
- priv->int_name_tx, ndev);
+ err = request_irq(priv->gfargrp.interruptTransmit,
+ gfar_transmit, 0,
+ priv->gfargrp.int_name_tx,
+ &priv->gfargrp);
if (err) {
if (netif_msg_intr(priv))
pr_err("%s: Can't get IRQ %d\n", ndev->name,
- priv->interruptTransmit);
+ priv->gfargrp.interruptTransmit);
goto tx_irq_fail;
}
- err = request_irq(priv->interruptReceive, gfar_receive, 0,
- priv->int_name_rx, ndev);
+ err = request_irq(priv->gfargrp.interruptReceive,
+ gfar_receive, 0,
+ priv->gfargrp.int_name_rx,
+ &priv->gfargrp);
if (err) {
if (netif_msg_intr(priv))
pr_err("%s: Can't get IRQ %d (receive0)\n",
- ndev->name, priv->interruptReceive);
+ ndev->name,
+ priv->gfargrp.interruptReceive);
goto rx_irq_fail;
}
} else {
- err = request_irq(priv->interruptTransmit, gfar_interrupt,
- 0, priv->int_name_tx, ndev);
+ err = request_irq(priv->gfargrp.interruptTransmit,
+ gfar_interrupt, 0,
+ priv->gfargrp.int_name_tx,
+ &priv->gfargrp);
if (err) {
if (netif_msg_intr(priv))
pr_err("%s: Can't get IRQ %d\n", ndev->name,
- priv->interruptTransmit);
+ priv->gfargrp.interruptTransmit);
goto err_irq_fail;
}
}
@@ -1296,9 +1314,9 @@ int startup_gfar(struct net_device *ndev)
return 0;
rx_irq_fail:
- free_irq(priv->interruptTransmit, ndev);
+ free_irq(priv->gfargrp.interruptTransmit, &priv->gfargrp);
tx_irq_fail:
- free_irq(priv->interruptError, ndev);
+ free_irq(priv->gfargrp.interruptError, &priv->gfargrp);
err_irq_fail:
free_skb_resources(priv);
return err;
@@ -1403,6 +1421,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar __iomem *regs = NULL;
struct txfcb *fcb = NULL;
struct txbd8 *txbdp, *txbdp_start, *base;
u32 lstatus;
@@ -1413,6 +1432,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_queue = priv->tx_queue;
base = tx_queue->tx_bd_base;
+ regs = priv->gfargrp.regs;
/* make space for additional header when fcb is needed */
if (((skb->ip_summed == CHECKSUM_PARTIAL) ||
@@ -1536,7 +1556,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
/* Tell the DMA to go go go */
- gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
+ gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
/* Unlock priv */
spin_unlock_irqrestore(&tx_queue->txlock, flags);
@@ -1579,40 +1599,42 @@ static void gfar_vlan_rx_register(struct net_device *dev,
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar __iomem *regs = NULL;
unsigned long flags;
u32 tempval;
rx_queue = priv->rx_queue;
+ regs = priv->gfargrp.regs;
spin_lock_irqsave(&rx_queue->rxlock, flags);
priv->vlgrp = grp;
if (grp) {
/* Enable VLAN tag insertion */
- tempval = gfar_read(&priv->regs->tctrl);
+ tempval = gfar_read(®s->tctrl);
tempval |= TCTRL_VLINS;
- gfar_write(&priv->regs->tctrl, tempval);
+ gfar_write(®s->tctrl, tempval);
/* Enable VLAN tag extraction */
- tempval = gfar_read(&priv->regs->rctrl);
+ tempval = gfar_read(®s->rctrl);
tempval |= (RCTRL_VLEX | RCTRL_PRSDEP_INIT);
- gfar_write(&priv->regs->rctrl, tempval);
+ gfar_write(®s->rctrl, tempval);
} else {
/* Disable VLAN tag insertion */
- tempval = gfar_read(&priv->regs->tctrl);
+ tempval = gfar_read(®s->tctrl);
tempval &= ~TCTRL_VLINS;
- gfar_write(&priv->regs->tctrl, tempval);
+ gfar_write(®s->tctrl, tempval);
/* Disable VLAN tag extraction */
- tempval = gfar_read(&priv->regs->rctrl);
+ tempval = gfar_read(®s->rctrl);
tempval &= ~RCTRL_VLEX;
/* If parse is no longer required, then disable parser */
if (tempval & RCTRL_REQ_PARSER)
tempval |= RCTRL_PRSDEP_INIT;
else
tempval &= ~RCTRL_PRSDEP_INIT;
- gfar_write(&priv->regs->rctrl, tempval);
+ gfar_write(®s->rctrl, tempval);
}
gfar_change_mtu(dev, dev->mtu);
@@ -1624,6 +1646,7 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
{
int tempsize, tempval;
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp.regs;
int oldsize = priv->rx_buffer_size;
int frame_size = new_mtu + ETH_HLEN;
@@ -1655,20 +1678,20 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
dev->mtu = new_mtu;
- gfar_write(&priv->regs->mrblr, priv->rx_buffer_size);
- gfar_write(&priv->regs->maxfrm, priv->rx_buffer_size);
+ gfar_write(®s->mrblr, priv->rx_buffer_size);
+ gfar_write(®s->maxfrm, priv->rx_buffer_size);
/* If the mtu is larger than the max size for standard
* ethernet frames (ie, a jumbo frame), then set maccfg2
* to allow huge frames, and to check the length */
- tempval = gfar_read(&priv->regs->maccfg2);
+ tempval = gfar_read(®s->maccfg2);
if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE)
tempval |= (MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
else
tempval &= ~(MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
- gfar_write(&priv->regs->maccfg2, tempval);
+ gfar_write(®s->maccfg2, tempval);
if ((oldsize != tempsize) && (dev->flags & IFF_UP))
startup_gfar(dev);
@@ -1787,9 +1810,9 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
return howmany;
}
-static void gfar_schedule_cleanup(struct net_device *dev)
+static void gfar_schedule_cleanup(struct gfar_priv_grp *gfargrp)
{
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_private *priv = gfargrp->priv;
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
unsigned long flags;
@@ -1800,14 +1823,14 @@ static void gfar_schedule_cleanup(struct net_device *dev)
spin_lock(&rx_queue->rxlock);
if (napi_schedule_prep(&rx_queue->napi)) {
- gfar_write(&priv->regs->imask, IMASK_RTX_DISABLED);
+ gfar_write(&gfargrp->regs->imask, IMASK_RTX_DISABLED);
__napi_schedule(&rx_queue->napi);
} else {
/*
* Clear IEVENT, so interrupts aren't called again
* because of the packets that have already arrived.
*/
- gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK);
+ gfar_write(&gfargrp->regs->ievent, IEVENT_RTX_MASK);
}
spin_unlock(&rx_queue->rxlock);
@@ -1815,9 +1838,9 @@ static void gfar_schedule_cleanup(struct net_device *dev)
}
/* Interrupt Handler for Transmit complete */
-static irqreturn_t gfar_transmit(int irq, void *dev_id)
+static irqreturn_t gfar_transmit(int irq, void *grp_id)
{
- gfar_schedule_cleanup((struct net_device *)dev_id);
+ gfar_schedule_cleanup((struct gfar_priv_grp *)grp_id);
return IRQ_HANDLED;
}
@@ -1897,9 +1920,9 @@ static inline void count_errors(unsigned short status, struct net_device *dev)
}
}
-irqreturn_t gfar_receive(int irq, void *dev_id)
+irqreturn_t gfar_receive(int irq, void *grp_id)
{
- gfar_schedule_cleanup((struct net_device *)dev_id);
+ gfar_schedule_cleanup((struct gfar_priv_grp *)grp_id);
return IRQ_HANDLED;
}
@@ -2053,6 +2076,7 @@ static int gfar_poll(struct napi_struct *napi, int budget)
struct gfar_priv_rx_q, napi);
struct net_device *dev = rx_queue->dev;
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp.regs;
struct gfar_priv_tx_q *tx_queue = NULL;
int tx_cleaned = 0;
int rx_cleaned = 0;
@@ -2060,7 +2084,7 @@ static int gfar_poll(struct napi_struct *napi, int budget)
/* Clear IEVENT, so interrupts aren't called again
* because of the packets that have already arrived */
- gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK);
+ gfar_write(®s->ievent, IEVENT_RTX_MASK);
tx_queue = priv->tx_queue;
/* If we fail to get the lock, don't bother with the TX BDs */
@@ -2078,19 +2102,19 @@ static int gfar_poll(struct napi_struct *napi, int budget)
napi_complete(napi);
/* Clear the halt bit in RSTAT */
- gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
+ gfar_write(®s->rstat, RSTAT_CLEAR_RHALT);
- gfar_write(&priv->regs->imask, IMASK_DEFAULT);
+ gfar_write(®s->imask, IMASK_DEFAULT);
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
if (likely(rx_queue->rxcoalescing)) {
- gfar_write(&priv->regs->rxic, 0);
- gfar_write(&priv->regs->rxic, rx_queue->rxic);
+ gfar_write(®s->rxic, 0);
+ gfar_write(®s->rxic, rx_queue->rxic);
}
if (likely(tx_queue->txcoalescing)) {
- gfar_write(&priv->regs->txic, 0);
- gfar_write(&priv->regs->txic, tx_queue->txic);
+ gfar_write(®s->txic, 0);
+ gfar_write(®s->txic, tx_queue->txic);
}
}
@@ -2109,41 +2133,40 @@ static void gfar_netpoll(struct net_device *dev)
/* If the device has multiple interrupts, run tx/rx */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- disable_irq(priv->interruptTransmit);
- disable_irq(priv->interruptReceive);
- disable_irq(priv->interruptError);
- gfar_interrupt(priv->interruptTransmit, dev);
- enable_irq(priv->interruptError);
- enable_irq(priv->interruptReceive);
- enable_irq(priv->interruptTransmit);
+ disable_irq(priv->gfargrp.interruptTransmit);
+ disable_irq(priv->gfargrp.interruptReceive);
+ disable_irq(priv->gfargrp.interruptError);
+ gfar_interrupt(priv->gfargrp.interruptTransmit, &priv->gfargrp);
+ enable_irq(priv->gfargrp.interruptError);
+ enable_irq(priv->gfargrp.interruptReceive);
+ enable_irq(priv->gfargrp.interruptTransmit);
} else {
- disable_irq(priv->interruptTransmit);
- gfar_interrupt(priv->interruptTransmit, dev);
- enable_irq(priv->interruptTransmit);
+ disable_irq(priv->gfargrp.interruptTransmit);
+ gfar_interrupt(priv->gfargrp.interruptTransmit, &priv->gfargrp);
+ enable_irq(priv->gfargrp.interruptTransmit);
}
}
#endif
/* The interrupt handler for devices with one interrupt */
-static irqreturn_t gfar_interrupt(int irq, void *dev_id)
+static irqreturn_t gfar_interrupt(int irq, void *grp_id)
{
- struct net_device *dev = dev_id;
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_grp *gfargrp = grp_id;
/* Save ievent for future reference */
- u32 events = gfar_read(&priv->regs->ievent);
+ u32 events = gfar_read(&gfargrp->regs->ievent);
/* Check for reception */
if (events & IEVENT_RX_MASK)
- gfar_receive(irq, dev_id);
+ gfar_receive(irq, grp_id);
/* Check for transmit completion */
if (events & IEVENT_TX_MASK)
- gfar_transmit(irq, dev_id);
+ gfar_transmit(irq, grp_id);
/* Check for errors */
if (events & IEVENT_ERR_MASK)
- gfar_error(irq, dev_id);
+ gfar_error(irq, grp_id);
return IRQ_HANDLED;
}
@@ -2158,7 +2181,7 @@ static void adjust_link(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar __iomem *regs = priv->regs;
+ struct gfar __iomem *regs = priv->gfargrp.regs;
unsigned long flags;
struct phy_device *phydev = priv->phydev;
int new_state = 0;
@@ -2241,7 +2264,7 @@ static void gfar_set_multi(struct net_device *dev)
{
struct dev_mc_list *mc_ptr;
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->regs;
+ struct gfar __iomem *regs = priv->gfargrp.regs;
u32 tempval;
if (dev->flags & IFF_PROMISC) {
@@ -2374,10 +2397,11 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr)
static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp.regs;
int idx;
char tmpbuf[MAC_ADDR_LEN];
u32 tempval;
- u32 __iomem *macptr = &priv->regs->macstnaddr1;
+ u32 __iomem *macptr = ®s->macstnaddr1;
macptr += num*2;
@@ -2394,16 +2418,18 @@ static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr)
}
/* GFAR error interrupt handler */
-static irqreturn_t gfar_error(int irq, void *dev_id)
+static irqreturn_t gfar_error(int irq, void *grp_id)
{
- struct net_device *dev = dev_id;
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_grp *gfargrp = grp_id;
+ struct gfar __iomem *regs = gfargrp->regs;
+ struct gfar_private *priv= gfargrp->priv;
+ struct net_device *dev = priv->ndev;
/* Save ievent for future reference */
- u32 events = gfar_read(&priv->regs->ievent);
+ u32 events = gfar_read(®s->ievent);
/* Clear IEVENT */
- gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK);
+ gfar_write(®s->ievent, events & IEVENT_ERR_MASK);
/* Magic Packet is not an error. */
if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
@@ -2413,7 +2439,7 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
/* Hmm... */
if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv))
printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n",
- dev->name, events, gfar_read(&priv->regs->imask));
+ dev->name, events, gfar_read(®s->imask));
/* Update the error counters */
if (events & IEVENT_TXE) {
@@ -2431,7 +2457,7 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
priv->extra_stats.tx_underrun++;
/* Reactivate the Tx Queues */
- gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
+ gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
}
if (netif_msg_tx_err(priv))
printk(KERN_DEBUG "%s: Transmit Error\n", dev->name);
@@ -2440,11 +2466,11 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
dev->stats.rx_errors++;
priv->extra_stats.rx_bsy++;
- gfar_receive(irq, dev_id);
+ gfar_receive(irq, grp_id);
if (netif_msg_rx_err(priv))
printk(KERN_DEBUG "%s: busy error (rstat: %x)\n",
- dev->name, gfar_read(&priv->regs->rstat));
+ dev->name, gfar_read(®s->rstat));
}
if (events & IEVENT_BABR) {
dev->stats.rx_errors++;
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index a60f93f..79e8471 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -770,6 +770,32 @@ struct gfar_priv_rx_q {
unsigned long rxic;
};
+/**
+ * struct gfar_priv_grp - per group structure
+ * @priv: back pointer to the priv structure
+ * @regs: the ioremapped register space for this group
+ * @grp_id: group id for this group
+ * @interruptTransmit: The TX interrupt number for this group
+ * @interruptReceive: The RX interrupt number for this group
+ * @interruptError: The ERROR interrupt number for this group
+ * @int_name_tx: tx interrupt name for this group
+ * @int_name_rx: rx interrupt name for this group
+ * @int_name_er: er interrupt name for this group
+ */
+
+struct gfar_priv_grp {
+ spinlock_t grplock __attribute__ ((aligned (SMP_CACHE_BYTES)));
+ struct gfar_private *priv;
+ struct gfar __iomem *regs;
+ unsigned int interruptTransmit;
+ unsigned int interruptReceive;
+ unsigned int interruptError;
+
+ char int_name_tx[GFAR_INT_NAME_MAX];
+ char int_name_rx[GFAR_INT_NAME_MAX];
+ char int_name_er[GFAR_INT_NAME_MAX];
+};
+
/* Struct stolen almost completely (and shamelessly) from the FCC enet source
* (Ok, that's not so true anymore, but there is a family resemblence)
* The GFAR buffer descriptors track the ring buffers. The rx_bd_base
@@ -785,6 +811,7 @@ struct gfar_private {
struct net_device *ndev;
struct of_device *ofdev;
+ struct gfar_priv_grp gfargrp;
struct gfar_priv_tx_q *tx_queue;
struct gfar_priv_rx_q *rx_queue;
@@ -797,9 +824,6 @@ struct gfar_private {
struct vlan_group *vlgrp;
- /* Unprotected fields */
- /* Pointer to the GFAR memory mapped Registers */
- struct gfar __iomem *regs;
/* Hash registers and their width */
u32 __iomem *hash_regs[16];
@@ -823,10 +847,6 @@ struct gfar_private {
wol_en:1; /* Wake-on-LAN enabled */
unsigned short padding;
- unsigned int interruptTransmit;
- unsigned int interruptReceive;
- unsigned int interruptError;
-
/* PHY stuff */
struct phy_device *phydev;
struct mii_bus *mii_bus;
@@ -838,10 +858,6 @@ struct gfar_private {
struct work_struct reset_task;
- char int_name_tx[GFAR_INT_NAME_MAX];
- char int_name_rx[GFAR_INT_NAME_MAX];
- char int_name_er[GFAR_INT_NAME_MAX];
-
/* Network Statistics */
struct gfar_extra_stats extra_stats;
};
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index 6d0d171..c681b41 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -137,10 +137,11 @@ static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
{
int i;
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp.regs;
u64 *extra = (u64 *) & priv->extra_stats;
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
- u32 __iomem *rmon = (u32 __iomem *) & priv->regs->rmon;
+ u32 __iomem *rmon = (u32 __iomem *) ®s->rmon;
struct gfar_stats *stats = (struct gfar_stats *) buf;
for (i = 0; i < GFAR_RMON_LEN; i++)
@@ -223,7 +224,7 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, voi
{
int i;
struct gfar_private *priv = netdev_priv(dev);
- u32 __iomem *theregs = (u32 __iomem *) priv->regs;
+ u32 __iomem *theregs = (u32 __iomem *) priv->gfargrp.regs;
u32 *buf = (u32 *) regbuf;
for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++)
@@ -349,6 +350,7 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp.regs;
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
@@ -407,13 +409,13 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
tx_queue->txic = mk_ic_value(cvals->tx_max_coalesced_frames,
gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
- gfar_write(&priv->regs->rxic, 0);
+ gfar_write(®s->rxic, 0);
if (rx_queue->rxcoalescing)
- gfar_write(&priv->regs->rxic, rx_queue->rxic);
+ gfar_write(®s->rxic, rx_queue->rxic);
- gfar_write(&priv->regs->txic, 0);
+ gfar_write(®s->txic, 0);
if (tx_queue->txcoalescing)
- gfar_write(&priv->regs->txic, tx_queue->txic);
+ gfar_write(®s->txic, tx_queue->txic);
return 0;
}
diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c
index 9c664f8..adea11e 100644
--- a/drivers/net/gianfar_sysfs.c
+++ b/drivers/net/gianfar_sysfs.c
@@ -50,6 +50,7 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp.regs;
struct gfar_priv_rx_q *rx_queue = NULL;
int new_setting = 0;
u32 temp;
@@ -74,14 +75,14 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
/* Set the new stashing value */
priv->bd_stash_en = new_setting;
- temp = gfar_read(&priv->regs->attr);
+ temp = gfar_read(®s->attr);
if (new_setting)
temp |= ATTR_BDSTASH;
else
temp &= ~(ATTR_BDSTASH);
- gfar_write(&priv->regs->attr, temp);
+ gfar_write(®s->attr, temp);
spin_unlock_irqrestore(&rx_queue->rxlock, flags);
@@ -103,6 +104,7 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp.regs;
struct gfar_priv_rx_q *rx_queue = NULL;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
@@ -122,20 +124,20 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
priv->rx_stash_size = length;
- temp = gfar_read(&priv->regs->attreli);
+ temp = gfar_read(®s->attreli);
temp &= ~ATTRELI_EL_MASK;
temp |= ATTRELI_EL(length);
- gfar_write(&priv->regs->attreli, temp);
+ gfar_write(®s->attreli, temp);
/* Turn stashing on/off as appropriate */
- temp = gfar_read(&priv->regs->attr);
+ temp = gfar_read(®s->attr);
if (length)
temp |= ATTR_BUFSTASH;
else
temp &= ~(ATTR_BUFSTASH);
- gfar_write(&priv->regs->attr, temp);
+ gfar_write(®s->attr, temp);
out:
spin_unlock_irqrestore(&rx_queue->rxlock, flags);
@@ -161,6 +163,7 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp.regs;
struct gfar_priv_rx_q *rx_queue = NULL;
unsigned short index = simple_strtoul(buf, NULL, 0);
u32 temp;
@@ -180,10 +183,10 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
priv->rx_stash_index = index;
- temp = gfar_read(&priv->regs->attreli);
+ temp = gfar_read(®s->attreli);
temp &= ~ATTRELI_EI_MASK;
temp |= ATTRELI_EI(index);
- gfar_write(&priv->regs->attreli, flags);
+ gfar_write(®s->attreli, flags);
out:
spin_unlock_irqrestore(&rx_queue->rxlock, flags);
@@ -208,6 +211,7 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp.regs;
struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
@@ -222,10 +226,10 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
priv->fifo_threshold = length;
- temp = gfar_read(&priv->regs->fifo_tx_thr);
+ temp = gfar_read(®s->fifo_tx_thr);
temp &= ~FIFO_TX_THR_MASK;
temp |= length;
- gfar_write(&priv->regs->fifo_tx_thr, temp);
+ gfar_write(®s->fifo_tx_thr, temp);
spin_unlock_irqrestore(&tx_queue->txlock, flags);
@@ -248,6 +252,7 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp.regs;
struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
@@ -261,10 +266,10 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
priv->fifo_starve = num;
- temp = gfar_read(&priv->regs->fifo_tx_starve);
+ temp = gfar_read(®s->fifo_tx_starve);
temp &= ~FIFO_TX_STARVE_MASK;
temp |= num;
- gfar_write(&priv->regs->fifo_tx_starve, temp);
+ gfar_write(®s->fifo_tx_starve, temp);
spin_unlock_irqrestore(&tx_queue->txlock, flags);
@@ -288,6 +293,7 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp.regs;
struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
@@ -301,10 +307,10 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
priv->fifo_starve_off = num;
- temp = gfar_read(&priv->regs->fifo_tx_starve_shutoff);
+ temp = gfar_read(®s->fifo_tx_starve_shutoff);
temp &= ~FIFO_TX_STARVE_OFF_MASK;
temp |= num;
- gfar_write(&priv->regs->fifo_tx_starve_shutoff, temp);
+ gfar_write(®s->fifo_tx_starve_shutoff, temp);
spin_unlock_irqrestore(&tx_queue->txlock, flags);
--
1.5.2.2
^ permalink raw reply related
* [PATCH v2 3/7] gianfar: Add Multiple Queue Support
From: Sandeep Gopalpet @ 2009-10-27 16:55 UTC (permalink / raw)
To: netdev; +Cc: davem, Sandeep Gopalpet
This patch introduces multiple Tx and Rx queues.
The incoming packets can be classified into different queues
based on filer rules (out of scope of this patch). The number
of queues enabled will be based on a DTS entries fsl,num_tx_queues
and fsl,num_rx_queues.
Although we are enabling multiple queues, the interrupt coalescing
is on per device level (etsec-1.7 doesn't support multiple rxics
and txics).
Signed-off-by: Sandeep Gopalpet <sandeep.kumar@freescale.com>
---
drivers/net/gianfar.c | 682 +++++++++++++++++++++++++++--------------
drivers/net/gianfar.h | 95 ++++++-
drivers/net/gianfar_ethtool.c | 70 +++--
drivers/net/gianfar_sysfs.c | 50 ++--
4 files changed, 606 insertions(+), 291 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 1ebbbb7..6b18736 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -143,6 +143,7 @@ void gfar_start(struct net_device *dev);
static void gfar_clear_exact_match(struct net_device *dev);
static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr);
static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+u16 gfar_select_queue(struct net_device *dev, struct sk_buff *skb);
MODULE_AUTHOR("Freescale Semiconductor, Inc");
MODULE_DESCRIPTION("Gianfar Ethernet Driver");
@@ -171,71 +172,91 @@ static int gfar_init_bds(struct net_device *ndev)
struct gfar_priv_rx_q *rx_queue = NULL;
struct txbd8 *txbdp;
struct rxbd8 *rxbdp;
- int i;
-
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
+ int i, j;
- /* Initialize some variables in our dev structure */
- tx_queue->num_txbdfree = tx_queue->tx_ring_size;
- tx_queue->dirty_tx = tx_queue->cur_tx = tx_queue->tx_bd_base;
- rx_queue->cur_rx = rx_queue->rx_bd_base;
- tx_queue->skb_curtx = tx_queue->skb_dirtytx = 0;
- rx_queue->skb_currx = 0;
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ /* Initialize some variables in our dev structure */
+ tx_queue->num_txbdfree = tx_queue->tx_ring_size;
+ tx_queue->dirty_tx = tx_queue->tx_bd_base;
+ tx_queue->cur_tx = tx_queue->tx_bd_base;
+ tx_queue->skb_curtx = 0;
+ tx_queue->skb_dirtytx = 0;
+
+ /* Initialize Transmit Descriptor Ring */
+ txbdp = tx_queue->tx_bd_base;
+ for (j = 0; j < tx_queue->tx_ring_size; j++) {
+ txbdp->lstatus = 0;
+ txbdp->bufPtr = 0;
+ txbdp++;
+ }
- /* Initialize Transmit Descriptor Ring */
- txbdp = tx_queue->tx_bd_base;
- for (i = 0; i < tx_queue->tx_ring_size; i++) {
- txbdp->lstatus = 0;
- txbdp->bufPtr = 0;
- txbdp++;
+ /* Set the last descriptor in the ring to indicate wrap */
+ txbdp--;
+ txbdp->status |= TXBD_WRAP;
}
- /* Set the last descriptor in the ring to indicate wrap */
- txbdp--;
- txbdp->status |= TXBD_WRAP;
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ rx_queue->cur_rx = rx_queue->rx_bd_base;
+ rx_queue->skb_currx = 0;
+ rxbdp = rx_queue->rx_bd_base;
- rxbdp = rx_queue->rx_bd_base;
- for (i = 0; i < rx_queue->rx_ring_size; i++) {
- struct sk_buff *skb = rx_queue->rx_skbuff[i];
+ for (j = 0; j < rx_queue->rx_ring_size; j++) {
+ struct sk_buff *skb = rx_queue->rx_skbuff[j];
- if (skb) {
- gfar_init_rxbdp(rx_queue, rxbdp, rxbdp->bufPtr);
- } else {
- skb = gfar_new_skb(ndev);
- if (!skb) {
- pr_err("%s: Can't allocate RX buffers\n",
- ndev->name);
- return -ENOMEM;
+ if (skb) {
+ gfar_init_rxbdp(rx_queue, rxbdp,
+ rxbdp->bufPtr);
+ } else {
+ skb = gfar_new_skb(ndev);
+ if (!skb) {
+ pr_err("%s: Can't allocate RX buffers\n",
+ ndev->name);
+ goto err_rxalloc_fail;
+ }
+ rx_queue->rx_skbuff[j] = skb;
+
+ gfar_new_rxbdp(rx_queue, rxbdp, skb);
}
- rx_queue->rx_skbuff[i] = skb;
- gfar_new_rxbdp(rx_queue, rxbdp, skb);
+ rxbdp++;
}
- rxbdp++;
}
return 0;
+
+err_rxalloc_fail:
+ free_skb_resources(priv);
+ return -ENOMEM;
}
static int gfar_alloc_skb_resources(struct net_device *ndev)
{
void *vaddr;
- int i;
+ dma_addr_t addr;
+ int i, j, k;
struct gfar_private *priv = netdev_priv(ndev);
struct device *dev = &priv->ofdev->dev;
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar __iomem *regs = priv->gfargrp.regs;
+ u32 *baddr;
+
+ priv->total_tx_ring_size = 0;
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->total_tx_ring_size += priv->tx_queue[i]->tx_ring_size;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
+ priv->total_rx_ring_size = 0;
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->total_rx_ring_size += priv->rx_queue[i]->rx_ring_size;
/* Allocate memory for the buffer descriptors */
vaddr = dma_alloc_coherent(dev,
- sizeof(*tx_queue->tx_bd_base) * tx_queue->tx_ring_size +
- sizeof(*rx_queue->rx_bd_base) * rx_queue->rx_ring_size,
- &tx_queue->tx_bd_dma_base, GFP_KERNEL);
+ sizeof(struct txbd8) * priv->total_tx_ring_size +
+ sizeof(struct rxbd8) * priv->total_rx_ring_size,
+ &addr, GFP_KERNEL);
if (!vaddr) {
if (netif_msg_ifup(priv))
pr_err("%s: Could not allocate buffer descriptors!\n",
@@ -243,38 +264,62 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
return -ENOMEM;
}
- tx_queue->tx_bd_base = vaddr;
- tx_queue->dev = ndev;
+ baddr = ®s->tbase0;
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ tx_queue->tx_bd_base = (struct txbd8 *) vaddr;
+ tx_queue->tx_bd_dma_base = addr;
+ tx_queue->dev = ndev;
+ /* enet DMA only understands physical addresses */
+ gfar_write(baddr, addr);
+ addr += sizeof(struct txbd8) *tx_queue->tx_ring_size;
+ vaddr += sizeof(struct txbd8) *tx_queue->tx_ring_size;
+ baddr += 2;
+ }
+ baddr = ®s->rbase0;
/* Start the rx descriptor ring where the tx ring leaves off */
- vaddr = vaddr + sizeof(*tx_queue->tx_bd_base) * tx_queue->tx_ring_size;
- rx_queue->rx_bd_base = vaddr;
- rx_queue->dev = ndev;
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ rx_queue->rx_bd_base = (struct rxbd8 *) vaddr;
+ rx_queue->dev = ndev;
+ gfar_write(baddr, addr);
+ addr += sizeof (struct rxbd8) * rx_queue->rx_ring_size;
+ vaddr += sizeof (struct rxbd8) * rx_queue->rx_ring_size;
+ baddr += 2;
+ }
/* Setup the skbuff rings */
- tx_queue->tx_skbuff = kmalloc(sizeof(*tx_queue->tx_skbuff) *
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ tx_queue->tx_skbuff = kmalloc(sizeof(*tx_queue->tx_skbuff) *
tx_queue->tx_ring_size, GFP_KERNEL);
- if (!tx_queue->tx_skbuff) {
- if (netif_msg_ifup(priv))
- pr_err("%s: Could not allocate tx_skbuff\n",
- ndev->name);
- goto cleanup;
- }
+ if (!tx_queue->tx_skbuff) {
+ if (netif_msg_ifup(priv))
+ pr_err("%s: Could not allocate tx_skbuff\n",
+ ndev->name);
+ goto cleanup;
+ }
- for (i = 0; i < tx_queue->tx_ring_size; i++)
- tx_queue->tx_skbuff[i] = NULL;
+ for (k = 0; k < tx_queue->tx_ring_size; k++)
+ tx_queue->tx_skbuff[k] = NULL;
+ }
- rx_queue->rx_skbuff = kmalloc(sizeof(*rx_queue->rx_skbuff) *
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ rx_queue->rx_skbuff = kmalloc(sizeof(*rx_queue->rx_skbuff) *
rx_queue->rx_ring_size, GFP_KERNEL);
- if (!rx_queue->rx_skbuff) {
- if (netif_msg_ifup(priv))
- pr_err("%s: Could not allocate rx_skbuff\n",
- ndev->name);
- goto cleanup;
- }
- for (i = 0; i < rx_queue->rx_ring_size; i++)
- rx_queue->rx_skbuff[i] = NULL;
+ if (!rx_queue->rx_skbuff) {
+ if (netif_msg_ifup(priv))
+ pr_err("%s: Could not allocate rx_skbuff\n",
+ ndev->name);
+ goto cleanup;
+ }
+
+ for (j = 0; j < rx_queue->rx_ring_size; j++)
+ rx_queue->rx_skbuff[j] = NULL;
+ }
if (gfar_init_bds(ndev))
goto cleanup;
@@ -289,30 +334,22 @@ cleanup:
static void gfar_init_mac(struct net_device *ndev)
{
struct gfar_private *priv = netdev_priv(ndev);
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
struct gfar __iomem *regs = priv->gfargrp.regs;
u32 rctrl = 0;
u32 tctrl = 0;
u32 attrs = 0;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
-
- /* enet DMA only understands physical addresses */
- gfar_write(®s->tbase0, tx_queue->tx_bd_dma_base);
- gfar_write(®s->rbase0, tx_queue->tx_bd_dma_base +
- sizeof(*tx_queue->tx_bd_base) *
- tx_queue->tx_ring_size);
-
/* Configure the coalescing support */
gfar_write(®s->txic, 0);
- if (tx_queue->txcoalescing)
- gfar_write(®s->txic, tx_queue->txic);
+ if (priv->tx_queue[0]->txcoalescing)
+ gfar_write(®s->txic, priv->tx_queue[0]->txic);
gfar_write(®s->rxic, 0);
- if (rx_queue->rxcoalescing)
- gfar_write(®s->rxic, rx_queue->rxic);
+ if (priv->rx_queue[0]->rxcoalescing)
+ gfar_write(®s->rxic, priv->rx_queue[0]->rxic);
+
+ if (priv->rx_filer_enable)
+ rctrl |= RCTRL_FILREN;
if (priv->rx_csum_enable)
rctrl |= RCTRL_CHECKSUMMING;
@@ -341,6 +378,8 @@ static void gfar_init_mac(struct net_device *ndev)
if (ndev->features & NETIF_F_IP_CSUM)
tctrl |= TCTRL_INIT_CSUM;
+ tctrl |= TCTRL_TXSCHED_PRIO;
+
gfar_write(®s->tctrl, tctrl);
/* Set the extraction length and index */
@@ -374,6 +413,7 @@ static const struct net_device_ops gfar_netdev_ops = {
.ndo_set_multicast_list = gfar_set_multi,
.ndo_tx_timeout = gfar_timeout,
.ndo_do_ioctl = gfar_ioctl,
+ .ndo_select_queue = gfar_select_queue,
.ndo_vlan_rx_register = gfar_vlan_rx_register,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
@@ -382,36 +422,131 @@ static const struct net_device_ops gfar_netdev_ops = {
#endif
};
+inline void lock_rx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_rx_queues; i++)
+ spin_lock(&priv->rx_queue[i]->rxlock);
+}
+
+inline void lock_tx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_tx_queues; i++)
+ spin_lock(&priv->tx_queue[i]->txlock);
+}
+
+inline void unlock_rx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_rx_queues; i++)
+ spin_unlock(&priv->rx_queue[i]->rxlock);
+}
+
+inline void unlock_tx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_tx_queues; i++)
+ spin_unlock(&priv->tx_queue[i]->txlock);
+}
+
/* Returns 1 if incoming frames use an FCB */
static inline int gfar_uses_fcb(struct gfar_private *priv)
{
return priv->vlgrp || priv->rx_csum_enable;
}
-static int gfar_of_init(struct net_device *dev)
+u16 gfar_select_queue(struct net_device *dev, struct sk_buff *skb)
+{
+ return skb_get_queue_mapping(skb);
+}
+static void free_tx_pointers(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_tx_queues; i++)
+ kfree(priv->tx_queue[i]);
+}
+
+static void free_rx_pointers(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_rx_queues; i++)
+ kfree(priv->rx_queue[i]);
+}
+
+static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
{
const char *model;
const char *ctype;
const void *mac_addr;
u64 addr, size;
- int err = 0;
- struct gfar_private *priv = netdev_priv(dev);
- struct device_node *np = priv->node;
+ int err = 0, i;
+ struct net_device *dev = NULL;
+ struct gfar_private *priv = NULL;
+ struct device_node *np = ofdev->node;
const u32 *stash;
const u32 *stash_len;
const u32 *stash_idx;
+ unsigned int num_tx_qs, num_rx_qs;
+ u32 *tx_queues, *rx_queues;
if (!np || !of_device_is_available(np))
return -ENODEV;
+ /* parse the num of tx and rx queues */
+ tx_queues = (u32 *)of_get_property(np, "fsl,num_tx_queues", NULL);
+ num_tx_qs = tx_queues ? *tx_queues : 1;
+
+ if (num_tx_qs > MAX_TX_QS) {
+ printk(KERN_ERR "num_tx_qs(=%d) greater than MAX_TX_QS(=%d)\n",
+ num_tx_qs, MAX_TX_QS);
+ printk(KERN_ERR "Cannot do alloc_etherdev, aborting\n");
+ return -EINVAL;
+ }
+
+ rx_queues = (u32 *)of_get_property(np, "fsl,num_rx_queues", NULL);
+ num_rx_qs = rx_queues ? *rx_queues : 1;
+
+ if (num_rx_qs > MAX_RX_QS) {
+ printk(KERN_ERR "num_rx_qs(=%d) greater than MAX_RX_QS(=%d)\n",
+ num_tx_qs, MAX_TX_QS);
+ printk(KERN_ERR "Cannot do alloc_etherdev, aborting\n");
+ return -EINVAL;
+ }
+
+ *pdev = alloc_etherdev_mq(sizeof(*priv), num_tx_qs);
+ dev = *pdev;
+ if (NULL == dev)
+ return -ENOMEM;
+
+ priv = netdev_priv(dev);
+ priv->node = ofdev->node;
+ priv->ndev = dev;
+
+ dev->num_tx_queues = num_tx_qs;
+ dev->real_num_tx_queues = num_tx_qs;
+ priv->num_tx_queues = num_tx_qs;
+ priv->num_rx_queues = num_rx_qs;
+
/* get a pointer to the register memory */
addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
priv->gfargrp.regs = ioremap(addr, size);
- if (priv->gfargrp.regs == NULL)
- return -ENOMEM;
+ if (priv->gfargrp.regs == NULL) {
+ err = -ENOMEM;
+ goto err_out;
+ }
priv->gfargrp.priv = priv; /* back pointer from group to priv */
+ priv->gfargrp.rx_bit_map = DEFAULT_MAPPING;
+ priv->gfargrp.tx_bit_map = DEFAULT_MAPPING;
+
priv->gfargrp.interruptTransmit = irq_of_parse_and_map(np, 0);
model = of_get_property(np, "model", NULL);
@@ -430,6 +565,38 @@ static int gfar_of_init(struct net_device *dev)
}
}
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->tx_queue[i] = NULL;
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i] = NULL;
+
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ priv->tx_queue[i] = (struct gfar_priv_tx_q *)kmalloc(
+ sizeof (struct gfar_priv_tx_q), GFP_KERNEL);
+ if (!priv->tx_queue[i]) {
+ err = -ENOMEM;
+ goto tx_alloc_failed;
+ }
+ priv->tx_queue[i]->tx_skbuff = NULL;
+ priv->tx_queue[i]->qindex = i;
+ priv->tx_queue[i]->dev = dev;
+ spin_lock_init(&(priv->tx_queue[i]->txlock));
+ }
+
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i] = (struct gfar_priv_rx_q *)kmalloc(
+ sizeof (struct gfar_priv_rx_q), GFP_KERNEL);
+ if (!priv->rx_queue[i]) {
+ err = -ENOMEM;
+ goto rx_alloc_failed;
+ }
+ priv->rx_queue[i]->rx_skbuff = NULL;
+ priv->rx_queue[i]->qindex = i;
+ priv->rx_queue[i]->dev = dev;
+ spin_lock_init(&(priv->rx_queue[i]->rxlock));
+ }
+
+
stash = of_get_property(np, "bd-stash", NULL);
if (stash) {
@@ -490,8 +657,13 @@ static int gfar_of_init(struct net_device *dev)
return 0;
+rx_alloc_failed:
+ free_rx_pointers(priv);
+tx_alloc_failed:
+ free_tx_pointers(priv);
err_out:
iounmap(priv->gfargrp.regs);
+ free_netdev(dev);
return err;
}
@@ -509,6 +681,17 @@ static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
return phy_mii_ioctl(priv->phydev, if_mii(rq), cmd);
}
+static unsigned int reverse_bitmap(unsigned int bit_map, unsigned int max_qs)
+{
+ unsigned int new_bit_map = 0x0;
+ int mask = 0x1 << (max_qs - 1), i;
+ for (i = 0; i < max_qs; i++) {
+ if (bit_map & mask)
+ new_bit_map = new_bit_map + (1 << i);
+ mask = mask >> 0x1;
+ }
+ return new_bit_map;
+}
/* Set up the ethernet device structure, private data,
* and anything else we need before we start */
static int gfar_probe(struct of_device *ofdev,
@@ -518,14 +701,14 @@ static int gfar_probe(struct of_device *ofdev,
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
struct gfar __iomem *regs = NULL;
- int err = 0;
+ int err = 0, i;
int len_devname;
+ u32 rstat = 0, tstat = 0, rqueue = 0, tqueue = 0;
- /* Create an ethernet device instance */
- dev = alloc_etherdev(sizeof (*priv));
+ err = gfar_of_init(ofdev, &dev);
- if (NULL == dev)
- return -ENOMEM;
+ if (err)
+ return err;
priv = netdev_priv(dev);
priv->ndev = dev;
@@ -533,23 +716,6 @@ static int gfar_probe(struct of_device *ofdev,
priv->node = ofdev->node;
SET_NETDEV_DEV(dev, &ofdev->dev);
- err = gfar_of_init(dev);
-
- if (err)
- goto regs_fail;
-
- priv->tx_queue = (struct gfar_priv_tx_q *)kmalloc(
- sizeof (struct gfar_priv_tx_q), GFP_KERNEL);
- if (!priv->tx_queue)
- goto regs_fail;
-
- priv->rx_queue = (struct gfar_priv_rx_q *)kmalloc(
- sizeof (struct gfar_priv_rx_q), GFP_KERNEL);
- if (!priv->rx_queue)
- goto rx_queue_fail;
-
- spin_lock_init(&priv->tx_queue->txlock);
- spin_lock_init(&priv->rx_queue->rxlock);
spin_lock_init(&priv->gfargrp.grplock);
spin_lock_init(&priv->bflock);
INIT_WORK(&priv->reset_task, gfar_reset_task);
@@ -587,8 +753,8 @@ static int gfar_probe(struct of_device *ofdev,
dev->netdev_ops = &gfar_netdev_ops;
dev->ethtool_ops = &gfar_ethtool_ops;
- /* Register for napi ...NAPI is for each rx_queue */
- netif_napi_add(dev, &priv->rx_queue->napi, gfar_poll, GFAR_DEV_WEIGHT);
+ /* Register for napi ...We are registering NAPI for each grp */
+ netif_napi_add(dev, &priv->gfargrp.napi, gfar_poll, GFAR_DEV_WEIGHT);
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
priv->rx_csum_enable = 1;
@@ -644,17 +810,44 @@ static int gfar_probe(struct of_device *ofdev,
if (dev->features & NETIF_F_IP_CSUM)
dev->hard_header_len += GMAC_FCB_LEN;
+ /* Need to reverse the bit maps as bit_map's MSB is q0
+ * but, for_each_bit parses from right to left, which
+ * basically reverses the queue numbers */
+ priv->gfargrp.tx_bit_map = reverse_bitmap(priv->gfargrp.tx_bit_map, MAX_TX_QS);
+ priv->gfargrp.rx_bit_map = reverse_bitmap(priv->gfargrp.rx_bit_map, MAX_RX_QS);
+
+ /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values */
+ for_each_bit(i, &priv->gfargrp.rx_bit_map, priv->num_rx_queues) {
+ priv->gfargrp.num_rx_queues++;
+ rstat = rstat | (RSTAT_CLEAR_RHALT >> i);
+ rqueue = rqueue | ((RQUEUE_EN0 | RQUEUE_EX0) >> i);
+ }
+ for_each_bit (i, &priv->gfargrp.tx_bit_map, priv->num_tx_queues) {
+ priv->gfargrp.num_tx_queues++;
+ tstat = tstat | (TSTAT_CLEAR_THALT >> i);
+ tqueue = tqueue | (TQUEUE_EN0 >> i);
+ }
+ priv->gfargrp.rstat = rstat;
+ priv->gfargrp.tstat = tstat;
+
+ gfar_write(®s->rqueue, rqueue);
+ gfar_write(®s->tqueue, tqueue);
+
priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
/* Initializing some of the rx/tx queue level parameters */
- priv->tx_queue->tx_ring_size = DEFAULT_TX_RING_SIZE;
- priv->tx_queue->num_txbdfree = DEFAULT_TX_RING_SIZE;
- priv->tx_queue->txcoalescing = DEFAULT_TX_COALESCE;
- priv->tx_queue->txic = DEFAULT_TXIC;
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ priv->tx_queue[i]->tx_ring_size = DEFAULT_TX_RING_SIZE;
+ priv->tx_queue[i]->num_txbdfree = DEFAULT_TX_RING_SIZE;
+ priv->tx_queue[i]->txcoalescing = DEFAULT_TX_COALESCE;
+ priv->tx_queue[i]->txic = DEFAULT_TXIC;
+ }
- priv->rx_queue->rx_ring_size = DEFAULT_RX_RING_SIZE;
- priv->rx_queue->rxcoalescing = DEFAULT_RX_COALESCE;
- priv->rx_queue->rxic = DEFAULT_RXIC;
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i]->rx_ring_size = DEFAULT_RX_RING_SIZE;
+ priv->rx_queue[i]->rxcoalescing = DEFAULT_RX_COALESCE;
+ priv->rx_queue[i]->rxic = DEFAULT_RXIC;
+ }
/* Enable most messages by default */
priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
@@ -699,17 +892,19 @@ static int gfar_probe(struct of_device *ofdev,
/* Even more device info helps when determining which kernel */
/* provided which set of benchmarks. */
printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name);
- printk(KERN_INFO "%s: %d/%d RX/TX BD ring size\n",
- dev->name, priv->rx_queue->rx_ring_size, priv->tx_queue->tx_ring_size);
+ for (i = 0; i < priv->num_rx_queues; i++)
+ printk(KERN_INFO "%s: :RX BD ring size for Q[%d]: %d\n",
+ dev->name, i, priv->rx_queue[i]->rx_ring_size);
+ for(i = 0; i < priv->num_tx_queues; i++)
+ printk(KERN_INFO "%s:TX BD ring size for Q[%d]: %d\n",
+ dev->name, i, priv->tx_queue[i]->tx_ring_size);
return 0;
register_fail:
iounmap(priv->gfargrp.regs);
- kfree(priv->rx_queue);
-rx_queue_fail:
- kfree(priv->tx_queue);
-regs_fail:
+ free_tx_pointers(priv);
+ free_rx_pointers(priv);
if (priv->phy_node)
of_node_put(priv->phy_node);
if (priv->tbi_node)
@@ -742,8 +937,6 @@ static int gfar_suspend(struct device *dev)
{
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
struct gfar __iomem *regs = NULL;
unsigned long flags;
u32 tempval;
@@ -752,13 +945,13 @@ static int gfar_suspend(struct device *dev)
(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
netif_device_detach(ndev);
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
regs = priv->gfargrp.regs;
if (netif_running(ndev)) {
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
+
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
gfar_halt_nodisable(ndev);
@@ -772,10 +965,11 @@ static int gfar_suspend(struct device *dev)
gfar_write(®s->maccfg1, tempval);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
- napi_disable(&rx_queue->napi);
+ napi_disable(&priv->gfargrp.napi);
if (magic_packet) {
/* Enable interrupt on Magic Packet */
@@ -797,8 +991,6 @@ static int gfar_resume(struct device *dev)
{
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
struct gfar __iomem *regs = NULL;
unsigned long flags;
u32 tempval;
@@ -816,12 +1008,11 @@ static int gfar_resume(struct device *dev)
/* Disable Magic Packet mode, in case something
* else woke us up.
*/
- rx_queue = priv->rx_queue;
- tx_queue = priv->tx_queue;
regs = priv->gfargrp.regs;
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
tempval = gfar_read(®s->maccfg2);
tempval &= ~MACCFG2_MPEN;
@@ -829,12 +1020,13 @@ static int gfar_resume(struct device *dev)
gfar_start(ndev);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
netif_device_attach(ndev);
- napi_enable(&rx_queue->napi);
+ napi_enable(&priv->gfargrp.napi);
return 0;
}
@@ -861,7 +1053,7 @@ static int gfar_restore(struct device *dev)
phy_start(priv->phydev);
netif_device_attach(ndev);
- napi_enable(&priv->napi);
+ napi_enable(&priv->gfargrp.napi);
return 0;
}
@@ -1115,23 +1307,21 @@ void gfar_halt(struct net_device *dev)
void stop_gfar(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
unsigned long flags;
phy_stop(priv->phydev);
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
/* Lock it down */
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
gfar_halt(dev);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
/* Free the IRQs */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
@@ -1145,24 +1335,14 @@ void stop_gfar(struct net_device *dev)
free_skb_resources(priv);
}
-/* If there are any tx skbs or rx skbs still around, free them.
- * Then free tx_skbuff and rx_skbuff */
-static void free_skb_resources(struct gfar_private *priv)
+static void free_skb_tx_queue(struct gfar_priv_tx_q *tx_queue)
{
- struct device *dev = &priv->ofdev->dev;
- struct rxbd8 *rxbdp;
struct txbd8 *txbdp;
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar_private *priv = netdev_priv(tx_queue->dev);
int i, j;
- /* Go through all the buffer descriptors and free their data buffers */
- tx_queue = priv->tx_queue;
txbdp = tx_queue->tx_bd_base;
- if (!tx_queue->tx_skbuff)
- goto skip_tx_skbuff;
-
for (i = 0; i < tx_queue->tx_ring_size; i++) {
if (!tx_queue->tx_skbuff[i])
continue;
@@ -1170,7 +1350,8 @@ static void free_skb_resources(struct gfar_private *priv)
dma_unmap_single(&priv->ofdev->dev, txbdp->bufPtr,
txbdp->length, DMA_TO_DEVICE);
txbdp->lstatus = 0;
- for (j = 0; j < skb_shinfo(tx_queue->tx_skbuff[i])->nr_frags; j++) {
+ for (j = 0; j < skb_shinfo(tx_queue->tx_skbuff[i])->nr_frags;
+ j++) {
txbdp++;
dma_unmap_page(&priv->ofdev->dev, txbdp->bufPtr,
txbdp->length, DMA_TO_DEVICE);
@@ -1179,36 +1360,58 @@ static void free_skb_resources(struct gfar_private *priv)
dev_kfree_skb_any(tx_queue->tx_skbuff[i]);
tx_queue->tx_skbuff[i] = NULL;
}
-
kfree(tx_queue->tx_skbuff);
-skip_tx_skbuff:
+}
- rx_queue = priv->rx_queue;
- rxbdp = rx_queue->rx_bd_base;
+static void free_skb_rx_queue(struct gfar_priv_rx_q *rx_queue)
+{
+ struct rxbd8 *rxbdp;
+ struct gfar_private *priv = netdev_priv(rx_queue->dev);
+ int i;
- if (!rx_queue->rx_skbuff)
- goto skip_rx_skbuff;
+ rxbdp = rx_queue->rx_bd_base;
for (i = 0; i < rx_queue->rx_ring_size; i++) {
if (rx_queue->rx_skbuff[i]) {
- dma_unmap_single(&priv->ofdev->dev, rxbdp->bufPtr,
- priv->rx_buffer_size,
+ dma_unmap_single(&priv->ofdev->dev,
+ rxbdp->bufPtr, priv->rx_buffer_size,
DMA_FROM_DEVICE);
dev_kfree_skb_any(rx_queue->rx_skbuff[i]);
rx_queue->rx_skbuff[i] = NULL;
}
-
rxbdp->lstatus = 0;
rxbdp->bufPtr = 0;
rxbdp++;
}
-
kfree(rx_queue->rx_skbuff);
-skip_rx_skbuff:
+}
+
+/* If there are any tx skbs or rx skbs still around, free them.
+ * Then free tx_skbuff and rx_skbuff */
+static void free_skb_resources(struct gfar_private *priv)
+{
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ int i;
+
+ /* Go through all the buffer descriptors and free their data buffers */
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ if(!tx_queue->tx_skbuff)
+ free_skb_tx_queue(tx_queue);
+ }
- dma_free_coherent(dev, sizeof(*txbdp) * tx_queue->tx_ring_size +
- sizeof(*rxbdp) * rx_queue->rx_ring_size,
- tx_queue->tx_bd_base, tx_queue->tx_bd_dma_base);
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ if(!rx_queue->rx_skbuff)
+ free_skb_rx_queue(rx_queue);
+ }
+
+ dma_free_coherent(&priv->ofdev->dev,
+ sizeof(struct txbd8) * priv->total_tx_ring_size +
+ sizeof(struct rxbd8) * priv->total_rx_ring_size,
+ priv->tx_queue[0]->tx_bd_base,
+ priv->tx_queue[0]->tx_bd_dma_base);
}
void gfar_start(struct net_device *dev)
@@ -1233,8 +1436,8 @@ void gfar_start(struct net_device *dev)
gfar_write(®s->dmactrl, tempval);
/* Clear THLT/RHLT, so that the DMA starts polling now */
- gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
- gfar_write(®s->rstat, RSTAT_CLEAR_RHALT);
+ gfar_write(®s->tstat, priv->gfargrp.tstat);
+ gfar_write(®s->rstat, priv->gfargrp.rstat);
/* Unmask the interrupts we look for */
gfar_write(®s->imask, IMASK_DEFAULT);
@@ -1329,7 +1532,7 @@ static int gfar_enet_open(struct net_device *dev)
struct gfar_private *priv = netdev_priv(dev);
int err;
- napi_enable(&priv->rx_queue->napi);
+ napi_enable(&priv->gfargrp.napi);
skb_queue_head_init(&priv->rx_recycle);
@@ -1341,17 +1544,17 @@ static int gfar_enet_open(struct net_device *dev)
err = init_phy(dev);
if (err) {
- napi_disable(&priv->rx_queue->napi);
+ napi_disable(&priv->gfargrp.napi);
return err;
}
err = startup_gfar(dev);
if (err) {
- napi_disable(&priv->rx_queue->napi);
+ napi_disable(&priv->gfargrp.napi);
return err;
}
- netif_start_queue(dev);
+ netif_tx_start_all_queues(dev);
device_set_wakeup_enable(&dev->dev, priv->wol_en);
@@ -1421,16 +1624,20 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar_priv_tx_q *tx_queue = NULL;
+ struct netdev_queue *txq;
struct gfar __iomem *regs = NULL;
struct txfcb *fcb = NULL;
struct txbd8 *txbdp, *txbdp_start, *base;
u32 lstatus;
- int i;
+ int i, rq = 0;
u32 bufaddr;
unsigned long flags;
unsigned int nr_frags, length;
- tx_queue = priv->tx_queue;
+
+ rq = skb->queue_mapping;
+ tx_queue = priv->tx_queue[rq];
+ txq = netdev_get_tx_queue(dev, rq);
base = tx_queue->tx_bd_base;
regs = priv->gfargrp.regs;
@@ -1458,7 +1665,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* check if there is space to queue this packet */
if ((nr_frags+1) > tx_queue->num_txbdfree) {
/* no space, stop the queue */
- netif_stop_queue(dev);
+ netif_tx_stop_queue(txq);
dev->stats.tx_fifo_errors++;
spin_unlock_irqrestore(&tx_queue->txlock, flags);
return NETDEV_TX_BUSY;
@@ -1550,13 +1757,13 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* If the next BD still needs to be cleaned up, then the bds
are full. We need to tell the kernel to stop sending us stuff. */
if (!tx_queue->num_txbdfree) {
- netif_stop_queue(dev);
+ netif_tx_stop_queue(txq);
dev->stats.tx_fifo_errors++;
}
/* Tell the DMA to go go go */
- gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
+ gfar_write(®s->tstat, TSTAT_CLEAR_THALT >> tx_queue->qindex);
/* Unlock priv */
spin_unlock_irqrestore(&tx_queue->txlock, flags);
@@ -1569,7 +1776,7 @@ static int gfar_close(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- napi_disable(&priv->rx_queue->napi);
+ napi_disable(&priv->gfargrp.napi);
skb_queue_purge(&priv->rx_recycle);
cancel_work_sync(&priv->reset_task);
@@ -1579,7 +1786,7 @@ static int gfar_close(struct net_device *dev)
phy_disconnect(priv->phydev);
priv->phydev = NULL;
- netif_stop_queue(dev);
+ netif_tx_stop_all_queues(dev);
return 0;
}
@@ -1598,14 +1805,13 @@ static void gfar_vlan_rx_register(struct net_device *dev,
struct vlan_group *grp)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_rx_q *rx_queue = NULL;
struct gfar __iomem *regs = NULL;
unsigned long flags;
u32 tempval;
- rx_queue = priv->rx_queue;
regs = priv->gfargrp.regs;
- spin_lock_irqsave(&rx_queue->rxlock, flags);
+ local_irq_save(flags);
+ lock_rx_qs(priv);
priv->vlgrp = grp;
@@ -1639,7 +1845,8 @@ static void gfar_vlan_rx_register(struct net_device *dev,
gfar_change_mtu(dev, dev->mtu);
- spin_unlock_irqrestore(&rx_queue->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
}
static int gfar_change_mtu(struct net_device *dev, int new_mtu)
@@ -1711,10 +1918,10 @@ static void gfar_reset_task(struct work_struct *work)
struct net_device *dev = priv->ndev;
if (dev->flags & IFF_UP) {
- netif_stop_queue(dev);
+ netif_tx_stop_all_queues(dev);
stop_gfar(dev);
startup_gfar(dev);
- netif_start_queue(dev);
+ netif_tx_start_all_queues(dev);
}
netif_tx_schedule_all(dev);
@@ -1745,7 +1952,7 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
int howmany = 0;
u32 lstatus;
- rx_queue = priv->rx_queue;
+ rx_queue = priv->rx_queue[tx_queue->qindex];
bdp = tx_queue->dirty_tx;
skb_dirtytx = tx_queue->skb_dirtytx;
@@ -1798,8 +2005,8 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
}
/* If we freed a buffer, we can restart transmission, if necessary */
- if (netif_queue_stopped(dev) && tx_queue->num_txbdfree)
- netif_wake_queue(dev);
+ if (__netif_subqueue_stopped(dev, tx_queue->qindex) && tx_queue->num_txbdfree)
+ netif_wake_subqueue(dev, tx_queue->qindex);
/* Update dirty indicators */
tx_queue->skb_dirtytx = skb_dirtytx;
@@ -1812,19 +2019,12 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
static void gfar_schedule_cleanup(struct gfar_priv_grp *gfargrp)
{
- struct gfar_private *priv = gfargrp->priv;
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
unsigned long flags;
- rx_queue = priv->rx_queue;
- tx_queue = priv->tx_queue;
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
-
- if (napi_schedule_prep(&rx_queue->napi)) {
+ spin_lock_irqsave(&gfargrp->grplock, flags);
+ if (napi_schedule_prep(&gfargrp->napi)) {
gfar_write(&gfargrp->regs->imask, IMASK_RTX_DISABLED);
- __napi_schedule(&rx_queue->napi);
+ __napi_schedule(&gfargrp->napi);
} else {
/*
* Clear IEVENT, so interrupts aren't called again
@@ -1832,9 +2032,8 @@ static void gfar_schedule_cleanup(struct gfar_priv_grp *gfargrp)
*/
gfar_write(&gfargrp->regs->ievent, IEVENT_RTX_MASK);
}
+ spin_unlock_irqrestore(&gfargrp->grplock, flags);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
}
/* Interrupt Handler for Transmit complete */
@@ -1952,6 +2151,7 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
fcb = (struct rxfcb *)skb->data;
/* Remove the FCB from the skb */
+ skb_set_queue_mapping(skb, fcb->rq);
/* Remove the padded bytes, if there are any */
if (amount_pull)
skb_pull(skb, amount_pull);
@@ -2072,28 +2272,54 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
static int gfar_poll(struct napi_struct *napi, int budget)
{
- struct gfar_priv_rx_q *rx_queue = container_of(napi,
- struct gfar_priv_rx_q, napi);
- struct net_device *dev = rx_queue->dev;
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_grp *gfargrp = container_of(napi,
+ struct gfar_priv_grp, napi);
+ struct gfar_private *priv = gfargrp->priv;
struct gfar __iomem *regs = priv->gfargrp.regs;
struct gfar_priv_tx_q *tx_queue = NULL;
- int tx_cleaned = 0;
- int rx_cleaned = 0;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ int rx_cleaned = 0, budget_per_queue = 0, rx_cleaned_per_queue = 0;
+ int tx_cleaned = 0, i, left_over_budget = budget, serviced_queues = 0;
+ int num_queues = 0;
unsigned long flags;
+ num_queues = gfargrp->num_rx_queues;
+ budget_per_queue = budget/num_queues;
+
/* Clear IEVENT, so interrupts aren't called again
* because of the packets that have already arrived */
gfar_write(®s->ievent, IEVENT_RTX_MASK);
- tx_queue = priv->tx_queue;
- /* If we fail to get the lock, don't bother with the TX BDs */
- if (spin_trylock_irqsave(&tx_queue->txlock, flags)) {
- tx_cleaned = gfar_clean_tx_ring(tx_queue);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
- }
+ while (num_queues && left_over_budget) {
- rx_cleaned = gfar_clean_rx_ring(rx_queue, budget);
+ budget_per_queue = left_over_budget/num_queues;
+ left_over_budget = 0;
+
+ for_each_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
+ if (test_bit(i, &serviced_queues))
+ continue;
+ rx_queue = priv->rx_queue[i];
+ tx_queue = priv->tx_queue[rx_queue->qindex];
+
+ /* If we fail to get the lock,
+ * don't bother with the TX BDs */
+ if (spin_trylock_irqsave(&tx_queue->txlock, flags)) {
+ tx_cleaned += gfar_clean_tx_ring(tx_queue);
+ spin_unlock_irqrestore(&tx_queue->txlock,
+ flags);
+ }
+
+ rx_cleaned_per_queue = gfar_clean_rx_ring(rx_queue,
+ budget_per_queue);
+ rx_cleaned += rx_cleaned_per_queue;
+ if(rx_cleaned_per_queue < budget_per_queue) {
+ left_over_budget = left_over_budget +
+ (budget_per_queue - rx_cleaned_per_queue);
+ set_bit(i, &serviced_queues);
+ num_queues--;
+ }
+ }
+ }
if (tx_cleaned)
return budget;
@@ -2102,7 +2328,7 @@ static int gfar_poll(struct napi_struct *napi, int budget)
napi_complete(napi);
/* Clear the halt bit in RSTAT */
- gfar_write(®s->rstat, RSTAT_CLEAR_RHALT);
+ gfar_write(®s->rstat, gfargrp->rstat);
gfar_write(®s->imask, IMASK_DEFAULT);
@@ -2180,14 +2406,14 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id)
static void adjust_link(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar __iomem *regs = priv->gfargrp.regs;
unsigned long flags;
struct phy_device *phydev = priv->phydev;
int new_state = 0;
- tx_queue = priv->tx_queue;
- spin_lock_irqsave(&tx_queue->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+
if (phydev->link) {
u32 tempval = gfar_read(®s->maccfg2);
u32 ecntrl = gfar_read(®s->ecntrl);
@@ -2252,8 +2478,8 @@ static void adjust_link(struct net_device *dev)
if (new_state && netif_msg_link(priv))
phy_print_status(phydev);
-
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
}
/* Update the hash table based on the current list of multicast
@@ -2457,7 +2683,7 @@ static irqreturn_t gfar_error(int irq, void *grp_id)
priv->extra_stats.tx_underrun++;
/* Reactivate the Tx Queues */
- gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
+ gfar_write(®s->tstat, gfargrp->tstat);
}
if (netif_msg_tx_err(priv))
printk(KERN_DEBUG "%s: Transmit Error\n", dev->name);
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 79e8471..fc43cfd 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -75,6 +75,10 @@
extern const char gfar_driver_name[];
extern const char gfar_driver_version[];
+/* MAXIMUM NUMBER OF QUEUES SUPPORTED */
+#define MAX_TX_QS 0x8
+#define MAX_RX_QS 0x8
+
/* These need to be powers of 2 for this driver */
#define DEFAULT_TX_RING_SIZE 256
#define DEFAULT_RX_RING_SIZE 256
@@ -172,12 +176,63 @@ extern const char gfar_driver_version[];
#define MINFLR_INIT_SETTINGS 0x00000040
+/* Tqueue control */
+#define TQUEUE_EN0 0x00008000
+#define TQUEUE_EN1 0x00004000
+#define TQUEUE_EN2 0x00002000
+#define TQUEUE_EN3 0x00001000
+#define TQUEUE_EN4 0x00000800
+#define TQUEUE_EN5 0x00000400
+#define TQUEUE_EN6 0x00000200
+#define TQUEUE_EN7 0x00000100
+#define TQUEUE_EN_ALL 0x0000FF00
+
+#define TR03WT_WT0_MASK 0xFF000000
+#define TR03WT_WT1_MASK 0x00FF0000
+#define TR03WT_WT2_MASK 0x0000FF00
+#define TR03WT_WT3_MASK 0x000000FF
+
+#define TR47WT_WT4_MASK 0xFF000000
+#define TR47WT_WT5_MASK 0x00FF0000
+#define TR47WT_WT6_MASK 0x0000FF00
+#define TR47WT_WT7_MASK 0x000000FF
+
+/* Rqueue control */
+#define RQUEUE_EX0 0x00800000
+#define RQUEUE_EX1 0x00400000
+#define RQUEUE_EX2 0x00200000
+#define RQUEUE_EX3 0x00100000
+#define RQUEUE_EX4 0x00080000
+#define RQUEUE_EX5 0x00040000
+#define RQUEUE_EX6 0x00020000
+#define RQUEUE_EX7 0x00010000
+#define RQUEUE_EX_ALL 0x00FF0000
+
+#define RQUEUE_EN0 0x00000080
+#define RQUEUE_EN1 0x00000040
+#define RQUEUE_EN2 0x00000020
+#define RQUEUE_EN3 0x00000010
+#define RQUEUE_EN4 0x00000008
+#define RQUEUE_EN5 0x00000004
+#define RQUEUE_EN6 0x00000002
+#define RQUEUE_EN7 0x00000001
+#define RQUEUE_EN_ALL 0x000000FF
+
/* Init to do tx snooping for buffers and descriptors */
#define DMACTRL_INIT_SETTINGS 0x000000c3
#define DMACTRL_GRS 0x00000010
#define DMACTRL_GTS 0x00000008
-#define TSTAT_CLEAR_THALT 0x80000000
+#define TSTAT_CLEAR_THALT_ALL 0xFF000000
+#define TSTAT_CLEAR_THALT 0x80000000
+#define TSTAT_CLEAR_THALT0 0x80000000
+#define TSTAT_CLEAR_THALT1 0x40000000
+#define TSTAT_CLEAR_THALT2 0x20000000
+#define TSTAT_CLEAR_THALT3 0x10000000
+#define TSTAT_CLEAR_THALT4 0x08000000
+#define TSTAT_CLEAR_THALT5 0x04000000
+#define TSTAT_CLEAR_THALT6 0x02000000
+#define TSTAT_CLEAR_THALT7 0x01000000
/* Interrupt coalescing macros */
#define IC_ICEN 0x80000000
@@ -228,6 +283,13 @@ extern const char gfar_driver_version[];
#define TCTRL_IPCSEN 0x00004000
#define TCTRL_TUCSEN 0x00002000
#define TCTRL_VLINS 0x00001000
+#define TCTRL_THDF 0x00000800
+#define TCTRL_RFCPAUSE 0x00000010
+#define TCTRL_TFCPAUSE 0x00000008
+#define TCTRL_TXSCHED_MASK 0x00000006
+#define TCTRL_TXSCHED_INIT 0x00000000
+#define TCTRL_TXSCHED_PRIO 0x00000002
+#define TCTRL_TXSCHED_WRRS 0x00000004
#define TCTRL_INIT_CSUM (TCTRL_TUCSEN | TCTRL_IPCSEN)
#define IEVENT_INIT_CLEAR 0xffffffff
@@ -700,6 +762,8 @@ struct gfar {
#define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200
#define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400
+#define DEFAULT_MAPPING 0xFF
+
/**
* struct gfar_priv_tx_q - per tx queue structure
* @txlock: per queue tx spin lock
@@ -743,7 +807,6 @@ struct gfar_priv_tx_q {
/**
* struct gfar_priv_rx_q - per rx queue structure
* @rxlock: per queue rx spin lock
- * @napi: the napi poll function
* @rx_skbuff: skb pointers
* @skb_currx: currently use skb pointer
* @rx_bd_base: First rx buffer descriptor
@@ -757,7 +820,6 @@ struct gfar_priv_tx_q {
struct gfar_priv_rx_q {
spinlock_t rxlock __attribute__ ((aligned (SMP_CACHE_BYTES)));
- struct napi_struct napi;
struct sk_buff ** rx_skbuff;
struct rxbd8 *rx_bd_base;
struct rxbd8 *cur_rx;
@@ -772,6 +834,7 @@ struct gfar_priv_rx_q {
/**
* struct gfar_priv_grp - per group structure
+ * @napi: the napi poll function
* @priv: back pointer to the priv structure
* @regs: the ioremapped register space for this group
* @grp_id: group id for this group
@@ -785,8 +848,17 @@ struct gfar_priv_rx_q {
struct gfar_priv_grp {
spinlock_t grplock __attribute__ ((aligned (SMP_CACHE_BYTES)));
+ struct napi_struct napi;
struct gfar_private *priv;
struct gfar __iomem *regs;
+ unsigned int rx_bit_map;
+ unsigned int tx_bit_map;
+ unsigned int num_tx_queues;
+ unsigned int num_rx_queues;
+ unsigned int rstat;
+ unsigned int tstat;
+ unsigned int imask;
+ unsigned int ievent;
unsigned int interruptTransmit;
unsigned int interruptReceive;
unsigned int interruptError;
@@ -807,13 +879,21 @@ struct gfar_priv_grp {
*/
struct gfar_private {
+ /* Indicates how many tx, rx queues are enabled */
+ unsigned int num_tx_queues;
+ unsigned int num_rx_queues;
+
+ /* The total tx and rx ring size for the enabled queues */
+ unsigned int total_tx_ring_size;
+ unsigned int total_rx_ring_size;
+
struct device_node *node;
struct net_device *ndev;
struct of_device *ofdev;
struct gfar_priv_grp gfargrp;
- struct gfar_priv_tx_q *tx_queue;
- struct gfar_priv_rx_q *rx_queue;
+ struct gfar_priv_tx_q *tx_queue[MAX_TX_QS];
+ struct gfar_priv_rx_q *rx_queue[MAX_RX_QS];
/* RX per device parameters */
unsigned int rx_buffer_size;
@@ -844,6 +924,7 @@ struct gfar_private {
unsigned char rx_csum_enable:1,
extended_hash:1,
bd_stash_en:1,
+ rx_filer_enable:1,
wol_en:1; /* Wake-on-LAN enabled */
unsigned short padding;
@@ -874,6 +955,10 @@ static inline void gfar_write(volatile unsigned __iomem *addr, u32 val)
out_be32(addr, val);
}
+extern inline void lock_rx_qs(struct gfar_private *priv);
+extern inline void lock_tx_qs(struct gfar_private *priv);
+extern inline void unlock_rx_qs(struct gfar_private *priv);
+extern inline void unlock_tx_qs(struct gfar_private *priv);
extern irqreturn_t gfar_receive(int irq, void *dev_id);
extern int startup_gfar(struct net_device *dev);
extern void stop_gfar(struct net_device *dev);
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index c681b41..d3d2623 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -204,9 +204,11 @@ static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
if (NULL == phydev)
return -ENODEV;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
+ tx_queue = priv->tx_queue[0];
+ rx_queue = priv->rx_queue[0];
+ /* etsec-1.7 and older versions have only one txic
+ * and rxic regs although they support multiple queues */
cmd->maxtxpkt = get_icft_value(tx_queue->txic);
cmd->maxrxpkt = get_icft_value(rx_queue->rxic);
@@ -298,8 +300,8 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
if (NULL == priv->phydev)
return -ENODEV;
- rx_queue = priv->rx_queue;
- tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue[0];
+ tx_queue = priv->tx_queue[0];
rxtime = get_ictt_value(rx_queue->rxic);
rxcount = get_icft_value(rx_queue->rxic);
@@ -357,8 +359,8 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
+ tx_queue = priv->tx_queue[0];
+ rx_queue = priv->rx_queue[0];
/* Set up rx coalescing */
if ((cvals->rx_coalesce_usecs == 0) ||
@@ -429,8 +431,8 @@ static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rv
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
+ tx_queue = priv->tx_queue[0];
+ rx_queue = priv->rx_queue[0];
rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
@@ -453,9 +455,7 @@ static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rv
static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
- int err = 0;
+ int err = 0, i = 0;
if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
return -EINVAL;
@@ -475,37 +475,41 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva
return -EINVAL;
}
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
if (dev->flags & IFF_UP) {
unsigned long flags;
/* Halt TX and RX, and process the frames which
* have already been received */
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
gfar_halt(dev);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
- gfar_clean_rx_ring(rx_queue, rx_queue->rx_ring_size);
+ for (i = 0; i < priv->num_rx_queues; i++)
+ gfar_clean_rx_ring(priv->rx_queue[i],
+ priv->rx_queue[i]->rx_ring_size);
/* Now we take down the rings to rebuild them */
stop_gfar(dev);
}
/* Change the size */
- rx_queue->rx_ring_size = rvals->rx_pending;
- tx_queue->tx_ring_size = rvals->tx_pending;
- tx_queue->num_txbdfree = tx_queue->tx_ring_size;
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i]->rx_ring_size = rvals->rx_pending;
+ priv->tx_queue[i]->tx_ring_size = rvals->tx_pending;
+ priv->tx_queue[i]->num_txbdfree = priv->tx_queue[i]->tx_ring_size;
+ }
/* Rebuild the rings with the new size */
if (dev->flags & IFF_UP) {
err = startup_gfar(dev);
- netif_wake_queue(dev);
+ netif_tx_wake_all_queues(dev);
}
return err;
}
@@ -513,29 +517,29 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva
static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_rx_q *rx_queue = NULL;
- struct gfar_priv_tx_q *tx_queue = NULL;
unsigned long flags;
- int err = 0;
+ int err = 0, i = 0;
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return -EOPNOTSUPP;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
if (dev->flags & IFF_UP) {
/* Halt TX and RX, and process the frames which
* have already been received */
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
gfar_halt(dev);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_tx_qs(priv);
+ unlock_rx_qs(priv);
+ local_irq_save(flags);
- gfar_clean_rx_ring(rx_queue, rx_queue->rx_ring_size);
+ for (i = 0; i < priv->num_rx_queues; i++)
+ gfar_clean_rx_ring(priv->rx_queue[i],
+ priv->rx_queue[i]->rx_ring_size);
/* Now we take down the rings to rebuild them */
stop_gfar(dev);
@@ -547,7 +551,7 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
if (dev->flags & IFF_UP) {
err = startup_gfar(dev);
- netif_wake_queue(dev);
+ netif_tx_wake_all_queues(dev);
}
return err;
}
diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c
index adea11e..4b726f6 100644
--- a/drivers/net/gianfar_sysfs.c
+++ b/drivers/net/gianfar_sysfs.c
@@ -51,7 +51,6 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_rx_q *rx_queue = NULL;
int new_setting = 0;
u32 temp;
unsigned long flags;
@@ -59,7 +58,6 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BD_STASHING))
return count;
- rx_queue = priv->rx_queue;
/* Find out the new setting */
if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
@@ -70,7 +68,9 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
else
return count;
- spin_lock_irqsave(&rx_queue->rxlock, flags);
+
+ local_irq_save(flags);
+ lock_rx_qs(priv);
/* Set the new stashing value */
priv->bd_stash_en = new_setting;
@@ -84,7 +84,8 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
gfar_write(®s->attr, temp);
- spin_unlock_irqrestore(&rx_queue->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -105,7 +106,6 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_rx_q *rx_queue = NULL;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -113,9 +113,9 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
return count;
- rx_queue = priv->rx_queue;
+ local_irq_save(flags);
+ lock_rx_qs(priv);
- spin_lock_irqsave(&rx_queue->rxlock, flags);
if (length > priv->rx_buffer_size)
goto out;
@@ -140,7 +140,8 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
gfar_write(®s->attr, temp);
out:
- spin_unlock_irqrestore(&rx_queue->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -164,7 +165,6 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_rx_q *rx_queue = NULL;
unsigned short index = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -172,9 +172,9 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
return count;
- rx_queue = priv->rx_queue;
+ local_irq_save(flags);
+ lock_rx_qs(priv);
- spin_lock_irqsave(&rx_queue->rxlock, flags);
if (index > priv->rx_stash_size)
goto out;
@@ -189,7 +189,8 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
gfar_write(®s->attreli, flags);
out:
- spin_unlock_irqrestore(&rx_queue->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -212,7 +213,6 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -220,9 +220,8 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
if (length > GFAR_MAX_FIFO_THRESHOLD)
return count;
- tx_queue = priv->tx_queue;
-
- spin_lock_irqsave(&tx_queue->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
priv->fifo_threshold = length;
@@ -231,7 +230,8 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
temp |= length;
gfar_write(®s->fifo_tx_thr, temp);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -253,7 +253,6 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -261,8 +260,8 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
if (num > GFAR_MAX_FIFO_STARVE)
return count;
- tx_queue = priv->tx_queue;
- spin_lock_irqsave(&tx_queue->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
priv->fifo_starve = num;
@@ -271,7 +270,8 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
temp |= num;
gfar_write(®s->fifo_tx_starve, temp);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -294,7 +294,6 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -302,8 +301,8 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
if (num > GFAR_MAX_FIFO_STARVE_OFF)
return count;
- tx_queue = priv->tx_queue;
- spin_lock_irqsave(&tx_queue->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
priv->fifo_starve_off = num;
@@ -312,7 +311,8 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
temp |= num;
gfar_write(®s->fifo_tx_starve_shutoff, temp);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
return count;
}
--
1.5.2.2
^ permalink raw reply related
* [PATCH v2 4/7] fsl_pq_mdio: Add Suport for etsec2.0 devices.
From: Sandeep Gopalpet @ 2009-10-27 16:55 UTC (permalink / raw)
To: netdev; +Cc: davem, Sandeep Gopalpet
This patch adds mdio support for etsec2.0 devices.
Modified the fsl_pq_mdio structure to include the new mdio
members.
Signed-off-by: Sandeep Gopalpet <sandeep.kumar@freescale.com>
---
drivers/net/fsl_pq_mdio.c | 59 ++++++++++++++++++++++++++++++++++++--------
drivers/net/fsl_pq_mdio.h | 11 +++++++-
2 files changed, 57 insertions(+), 13 deletions(-)
diff --git a/drivers/net/fsl_pq_mdio.c b/drivers/net/fsl_pq_mdio.c
index d167090..6de7b24 100644
--- a/drivers/net/fsl_pq_mdio.c
+++ b/drivers/net/fsl_pq_mdio.c
@@ -3,8 +3,9 @@
* Provides Bus interface for MIIM regs
*
* Author: Andy Fleming <afleming@freescale.com>
+ * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright (c) 2002-2004,2008 Freescale Semiconductor, Inc.
+ * Copyright 2002-2004, 2008-2009 Freescale Semiconductor, Inc.
*
* Based on gianfar_mii.c and ucc_geth_mii.c (Li Yang, Kim Phillips)
*
@@ -189,19 +190,29 @@ static int fsl_pq_mdio_find_free(struct mii_bus *new_bus)
#if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE)
-static u32 __iomem *get_gfar_tbipa(struct fsl_pq_mdio __iomem *regs)
+static u32 __iomem *get_gfar_tbipa(struct fsl_pq_mdio __iomem *regs, struct device_node *np)
{
struct gfar __iomem *enet_regs;
+ u32 __iomem *ioremap_tbipa;
+ u64 addr, size;
/*
* This is mildly evil, but so is our hardware for doing this.
* Also, we have to cast back to struct gfar because of
* definition weirdness done in gianfar.h.
*/
- enet_regs = (struct gfar __iomem *)
- ((char __iomem *)regs - offsetof(struct gfar, gfar_mii_regs));
-
- return &enet_regs->tbipa;
+ if(of_device_is_compatible(np, "fsl,gianfar-mdio") ||
+ of_device_is_compatible(np, "fsl,gianfar-tbi") ||
+ of_device_is_compatible(np, "gianfar")) {
+ enet_regs = (struct gfar __iomem *)regs;
+ return &enet_regs->tbipa;
+ } else if (of_device_is_compatible(np, "fsl,etsec2-mdio") ||
+ of_device_is_compatible(np, "fsl,etsec2-tbi")) {
+ addr = of_translate_address(np, of_get_address(np, 1, &size, NULL));
+ ioremap_tbipa = ioremap(addr, size);
+ return ioremap_tbipa;
+ } else
+ return NULL;
}
#endif
@@ -250,11 +261,11 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
{
struct device_node *np = ofdev->node;
struct device_node *tbi;
- struct fsl_pq_mdio __iomem *regs;
+ struct fsl_pq_mdio __iomem *regs = NULL;
u32 __iomem *tbipa;
struct mii_bus *new_bus;
int tbiaddr = -1;
- u64 addr, size;
+ u64 addr = 0, size = 0, ioremap_miimcfg = 0;
int err = 0;
new_bus = mdiobus_alloc();
@@ -268,8 +279,22 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
fsl_pq_mdio_bus_name(new_bus->id, np);
/* Set the PHY base address */
- addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
- regs = ioremap(addr, size);
+ if (of_device_is_compatible(np,"fsl,gianfar-mdio") ||
+ of_device_is_compatible(np, "fsl,gianfar-tbi") ||
+ of_device_is_compatible(np, "fsl,ucc-mdio") ||
+ of_device_is_compatible(np,"ucc_geth_phy" )) {
+ addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
+ ioremap_miimcfg = container_of(addr, struct fsl_pq_mdio, miimcfg);
+ regs = ioremap(ioremap_miimcfg, size +
+ offsetof(struct fsl_pq_mdio, miimcfg));
+ } else if (of_device_is_compatible(np,"fsl,etsec2-mdio") ||
+ of_device_is_compatible(np, "fsl,etsec2-tbi")) {
+ addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
+ regs = ioremap(addr, size);
+ } else {
+ err = -EINVAL;
+ goto err_free_bus;
+ }
if (NULL == regs) {
err = -ENOMEM;
@@ -290,9 +315,15 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
of_device_is_compatible(np, "fsl,gianfar-tbi") ||
+ of_device_is_compatible(np, "fsl,etsec2-mdio") ||
+ of_device_is_compatible(np, "fsl,etsec2-tbi") ||
of_device_is_compatible(np, "gianfar")) {
#if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE)
- tbipa = get_gfar_tbipa(regs);
+ tbipa = get_gfar_tbipa(regs, np);
+ if (!tbipa) {
+ err = -EINVAL;
+ goto err_free_irqs;
+ }
#else
err = -ENODEV;
goto err_free_irqs;
@@ -405,6 +436,12 @@ static struct of_device_id fsl_pq_mdio_match[] = {
{
.compatible = "fsl,gianfar-mdio",
},
+ {
+ .compatible = "fsl,etsec2-tbi",
+ },
+ {
+ .compatible = "fsl,etsec2-mdio",
+ },
{},
};
diff --git a/drivers/net/fsl_pq_mdio.h b/drivers/net/fsl_pq_mdio.h
index 36dad52..1f7d865 100644
--- a/drivers/net/fsl_pq_mdio.h
+++ b/drivers/net/fsl_pq_mdio.h
@@ -3,8 +3,9 @@
* Driver for the MDIO bus controller on Freescale PowerQUICC processors
*
* Author: Andy Fleming
+ * Modifier: Sandeep Gopalpet
*
- * Copyright (c) 2002-2004,2008 Freescale Semiconductor, Inc.
+ * Copyright 2002-2004, 2008-2009 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -23,6 +24,12 @@
#define MII_READ_COMMAND 0x00000001
struct fsl_pq_mdio {
+ u8 res1[16];
+ u32 ieventm; /* MDIO Interrupt event register (for etsec2)*/
+ u32 imaskm; /* MDIO Interrupt mask register (for etsec2)*/
+ u8 res2[4];
+ u32 emapm; /* MDIO Event mapping register (for etsec2)*/
+ u8 res3[1280];
u32 miimcfg; /* MII management configuration reg */
u32 miimcom; /* MII management command reg */
u32 miimadd; /* MII management address reg */
@@ -31,9 +38,9 @@ struct fsl_pq_mdio {
u32 miimind; /* MII management indication reg */
u8 reserved[28]; /* Space holder */
u32 utbipar; /* TBI phy address reg (only on UCC) */
+ u8 res4[2728];
} __attribute__ ((packed));
-
int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum);
int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
int fsl_pq_local_mdio_write(struct fsl_pq_mdio __iomem *regs, int mii_id,
--
1.5.2.2
^ permalink raw reply related
* [PATCH v2 5/7] gianfar: Add support etsec2.0 registers.
From: Sandeep Gopalpet @ 2009-10-27 16:55 UTC (permalink / raw)
To: netdev; +Cc: davem, Sandeep Gopalpet
This patch adds support for etsec2.0 regsiters
Signed-off-by: Sandeep Gopalpet <sandeep.kumar@freescale.com>
---
drivers/net/gianfar.h | 55 +++++++++++++++++++++++++++++++++++++++---------
1 files changed, 44 insertions(+), 11 deletions(-)
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index fc43cfd..70c25ce 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -566,25 +566,32 @@ struct gfar_stats {
struct gfar {
u32 tsec_id; /* 0x.000 - Controller ID register */
- u8 res1[12];
+ u32 tsec_id2; /* 0x.004 - Controller ID2 register */
+ u8 res1[8];
u32 ievent; /* 0x.010 - Interrupt Event Register */
u32 imask; /* 0x.014 - Interrupt Mask Register */
u32 edis; /* 0x.018 - Error Disabled Register */
- u8 res2[4];
+ u32 emapg; /* 0x.01c - Group Error mapping register */
u32 ecntrl; /* 0x.020 - Ethernet Control Register */
u32 minflr; /* 0x.024 - Minimum Frame Length Register */
u32 ptv; /* 0x.028 - Pause Time Value Register */
u32 dmactrl; /* 0x.02c - DMA Control Register */
u32 tbipa; /* 0x.030 - TBI PHY Address Register */
- u8 res3[88];
+ u8 res2[28];
+ u32 fifo_rx_pause; /* 0x.050 - FIFO receive pause start threshold
+ register */
+ u32 fifo_rx_pause_shutoff; /* x.054 - FIFO receive starve shutoff
+ register */
+ u32 fifo_rx_alarm; /* 0x.058 - FIFO receive alarm start threshold
+ register */
+ u32 fifo_rx_alarm_shutoff; /*0x.05c - FIFO receive alarm starve
+ shutoff register */
+ u8 res3[44];
u32 fifo_tx_thr; /* 0x.08c - FIFO transmit threshold register */
u8 res4[8];
u32 fifo_tx_starve; /* 0x.098 - FIFO transmit starve register */
u32 fifo_tx_starve_shutoff; /* 0x.09c - FIFO transmit starve shutoff register */
- u8 res5[4];
- u32 fifo_rx_pause; /* 0x.0a4 - FIFO receive pause threshold register */
- u32 fifo_rx_alarm; /* 0x.0a8 - FIFO receive alarm threshold register */
- u8 res6[84];
+ u8 res5[96];
u32 tctrl; /* 0x.100 - Transmit Control Register */
u32 tstat; /* 0x.104 - Transmit Status Register */
u32 dfvlan; /* 0x.108 - Default VLAN Control word */
@@ -635,7 +642,11 @@ struct gfar {
u8 res12[8];
u32 rxic; /* 0x.310 - Receive Interrupt Coalescing Configuration Register */
u32 rqueue; /* 0x.314 - Receive queue control register */
- u8 res13[24];
+ u32 rir0; /* 0x.318 - Ring mapping register 0 */
+ u32 rir1; /* 0x.31c - Ring mapping register 1 */
+ u32 rir2; /* 0x.320 - Ring mapping register 2 */
+ u32 rir3; /* 0x.324 - Ring mapping register 3 */
+ u8 res13[8];
u32 rbifx; /* 0x.330 - Receive bit field extract control register */
u32 rqfar; /* 0x.334 - Receive queue filing table address register */
u32 rqfcr; /* 0x.338 - Receive queue filing table control register */
@@ -684,7 +695,7 @@ struct gfar {
u32 maxfrm; /* 0x.510 - Maximum Frame Length Register */
u8 res18[12];
u8 gfar_mii_regs[24]; /* See gianfar_phy.h */
- u8 res19[4];
+ u32 ifctrl; /* 0x.538 - Interface control register */
u32 ifstat; /* 0x.53c - Interface Status Register */
u32 macstnaddr1; /* 0x.540 - Station Address Part 1 Register */
u32 macstnaddr2; /* 0x.544 - Station Address Part 2 Register */
@@ -745,8 +756,30 @@ struct gfar {
u8 res23c[248];
u32 attr; /* 0x.bf8 - Attributes Register */
u32 attreli; /* 0x.bfc - Attributes Extract Length and Extract Index Register */
- u8 res24[1024];
-
+ u8 res24[688];
+ u32 isrg0; /* 0x.eb0 - Interrupt steering group 0 register */
+ u32 isrg1; /* 0x.eb4 - Interrupt steering group 1 register */
+ u32 isrg2; /* 0x.eb8 - Interrupt steering group 2 register */
+ u32 isrg3; /* 0x.ebc - Interrupt steering group 3 register */
+ u8 res25[16];
+ u32 rxic0; /* 0x.ed0 - Ring 0 Rx interrupt coalescing */
+ u32 rxic1; /* 0x.ed4 - Ring 1 Rx interrupt coalescing */
+ u32 rxic2; /* 0x.ed8 - Ring 2 Rx interrupt coalescing */
+ u32 rxic3; /* 0x.edc - Ring 3 Rx interrupt coalescing */
+ u32 rxic4; /* 0x.ee0 - Ring 4 Rx interrupt coalescing */
+ u32 rxic5; /* 0x.ee4 - Ring 5 Rx interrupt coalescing */
+ u32 rxic6; /* 0x.ee8 - Ring 6 Rx interrupt coalescing */
+ u32 rxic7; /* 0x.eec - Ring 7 Rx interrupt coalescing */
+ u8 res26[32];
+ u32 txic0; /* 0x.f10 - Ring 0 Tx interrupt coalescing */
+ u32 txic1; /* 0x.f14 - Ring 1 Tx interrupt coalescing */
+ u32 txic2; /* 0x.f18 - Ring 2 Tx interrupt coalescing */
+ u32 txic3; /* 0x.f1c - Ring 3 Tx interrupt coalescing */
+ u32 txic4; /* 0x.f20 - Ring 4 Tx interrupt coalescing */
+ u32 txic5; /* 0x.f24 - Ring 5 Tx interrupt coalescing */
+ u32 txic6; /* 0x.f28 - Ring 6 Tx interrupt coalescing */
+ u32 txic7; /* 0x.f2c - Ring 7 Tx interrupt coalescing */
+ u8 res27[208];
};
/* Flags related to gianfar device features */
--
1.5.2.2
^ permalink raw reply related
* [PATCH v2 6/7] gianfar: Add Multiple group Support
From: Sandeep Gopalpet @ 2009-10-27 16:55 UTC (permalink / raw)
To: netdev; +Cc: davem, Sandeep Gopalpet
This patch introduces multiple group support for etsec2.0
devices.
Multiple group support is provided by mapping the set of enabled
queues to different groups and then programming the per group
regsiters imask, ievent, rstat, tstat.
The queues corresponding to a group are indicated by programming
isrg (interrupt steering) registers.
Signed-off-by: Sandeep Gopalpet <sandeep.kumar@freescale.com>
---
drivers/net/gianfar.c | 532 +++++++++++++++++++++++++++--------------
drivers/net/gianfar.h | 29 +++-
drivers/net/gianfar_ethtool.c | 58 +++--
drivers/net/gianfar_sysfs.c | 12 +-
4 files changed, 422 insertions(+), 209 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 6b18736..daff741 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -241,7 +241,7 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
struct device *dev = &priv->ofdev->dev;
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 *baddr;
priv->total_tx_ring_size = 0;
@@ -334,19 +334,13 @@ cleanup:
static void gfar_init_mac(struct net_device *ndev)
{
struct gfar_private *priv = netdev_priv(ndev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 rctrl = 0;
u32 tctrl = 0;
u32 attrs = 0;
/* Configure the coalescing support */
- gfar_write(®s->txic, 0);
- if (priv->tx_queue[0]->txcoalescing)
- gfar_write(®s->txic, priv->tx_queue[0]->txic);
-
- gfar_write(®s->rxic, 0);
- if (priv->rx_queue[0]->rxcoalescing)
- gfar_write(®s->rxic, priv->rx_queue[0]->rxic);
+ gfar_configure_coalescing(priv, 0xFF, 0xFF);
if (priv->rx_filer_enable)
rctrl |= RCTRL_FILREN;
@@ -480,16 +474,91 @@ static void free_rx_pointers(struct gfar_private *priv)
kfree(priv->rx_queue[i]);
}
+static void unmap_group_regs(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < MAXGROUPS; i++)
+ if (priv->gfargrp[i].regs)
+ iounmap(priv->gfargrp[i].regs);
+}
+
+static void disable_napi(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_grps; i++)
+ napi_disable(&priv->gfargrp[i].napi);
+}
+
+static void enable_napi(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_grps; i++)
+ napi_enable(&priv->gfargrp[i].napi);
+}
+
+static int gfar_parse_group(struct device_node *np,
+ struct gfar_private *priv, const char *model)
+{
+ u32 *queue_mask;
+ u64 addr, size;
+
+ addr = of_translate_address(np,
+ of_get_address(np, 0, &size, NULL));
+ priv->gfargrp[priv->num_grps].regs = ioremap(addr, size);
+
+ if (!priv->gfargrp[priv->num_grps].regs)
+ return -ENOMEM;
+
+ priv->gfargrp[priv->num_grps].interruptTransmit =
+ irq_of_parse_and_map(np, 0);
+
+ /* If we aren't the FEC we have multiple interrupts */
+ if (model && strcasecmp(model, "FEC")) {
+ priv->gfargrp[priv->num_grps].interruptReceive =
+ irq_of_parse_and_map(np, 1);
+ priv->gfargrp[priv->num_grps].interruptError =
+ irq_of_parse_and_map(np,2);
+ if (priv->gfargrp[priv->num_grps].interruptTransmit < 0 ||
+ priv->gfargrp[priv->num_grps].interruptReceive < 0 ||
+ priv->gfargrp[priv->num_grps].interruptError < 0) {
+ return -EINVAL;
+ }
+ }
+
+ priv->gfargrp[priv->num_grps].grp_id = priv->num_grps;
+ priv->gfargrp[priv->num_grps].priv = priv;
+ spin_lock_init(&priv->gfargrp[priv->num_grps].grplock);
+ if(priv->mode == MQ_MG_MODE) {
+ queue_mask = (u32 *)of_get_property(np,
+ "fsl,rx-bit-map", NULL);
+ priv->gfargrp[priv->num_grps].rx_bit_map =
+ queue_mask ? *queue_mask :(DEFAULT_MAPPING >> priv->num_grps);
+ queue_mask = (u32 *)of_get_property(np,
+ "fsl,tx-bit-map", NULL);
+ priv->gfargrp[priv->num_grps].tx_bit_map =
+ queue_mask ? *queue_mask : (DEFAULT_MAPPING >> priv->num_grps);
+ } else {
+ priv->gfargrp[priv->num_grps].rx_bit_map = 0xFF;
+ priv->gfargrp[priv->num_grps].tx_bit_map = 0xFF;
+ }
+ priv->num_grps++;
+
+ return 0;
+}
+
static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
{
const char *model;
const char *ctype;
const void *mac_addr;
- u64 addr, size;
int err = 0, i;
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
struct device_node *np = ofdev->node;
+ struct device_node *child = NULL;
const u32 *stash;
const u32 *stash_len;
const u32 *stash_idx;
@@ -533,36 +602,26 @@ static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
dev->real_num_tx_queues = num_tx_qs;
priv->num_tx_queues = num_tx_qs;
priv->num_rx_queues = num_rx_qs;
-
- /* get a pointer to the register memory */
- addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
- priv->gfargrp.regs = ioremap(addr, size);
-
- if (priv->gfargrp.regs == NULL) {
- err = -ENOMEM;
- goto err_out;
- }
-
- priv->gfargrp.priv = priv; /* back pointer from group to priv */
- priv->gfargrp.rx_bit_map = DEFAULT_MAPPING;
- priv->gfargrp.tx_bit_map = DEFAULT_MAPPING;
-
- priv->gfargrp.interruptTransmit = irq_of_parse_and_map(np, 0);
+ priv->num_grps = 0x0;
model = of_get_property(np, "model", NULL);
- /* If we aren't the FEC we have multiple interrupts */
- if (model && strcasecmp(model, "FEC")) {
- priv->gfargrp.interruptReceive = irq_of_parse_and_map(np, 1);
+ for (i = 0; i < MAXGROUPS; i++)
+ priv->gfargrp[i].regs = NULL;
- priv->gfargrp.interruptError = irq_of_parse_and_map(np, 2);
-
- if (priv->gfargrp.interruptTransmit < 0 ||
- priv->gfargrp.interruptReceive < 0 ||
- priv->gfargrp.interruptError < 0) {
- err = -EINVAL;
- goto err_out;
+ /* Parse and initialize group specific information */
+ if (of_device_is_compatible(np, "fsl,etsec2")) {
+ priv->mode = MQ_MG_MODE;
+ for_each_child_of_node(np, child) {
+ err = gfar_parse_group(child, priv, model);
+ if (err)
+ goto err_grp_init;
}
+ } else {
+ priv->mode = SQ_SG_MODE;
+ err = gfar_parse_group(np, priv, model);
+ if(err)
+ goto err_grp_init;
}
for (i = 0; i < priv->num_tx_queues; i++)
@@ -661,8 +720,8 @@ rx_alloc_failed:
free_rx_pointers(priv);
tx_alloc_failed:
free_tx_pointers(priv);
-err_out:
- iounmap(priv->gfargrp.regs);
+err_grp_init:
+ unmap_group_regs(priv);
free_netdev(dev);
return err;
}
@@ -701,9 +760,11 @@ static int gfar_probe(struct of_device *ofdev,
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
struct gfar __iomem *regs = NULL;
- int err = 0, i;
+ int err = 0, i, grp_idx = 0;
int len_devname;
u32 rstat = 0, tstat = 0, rqueue = 0, tqueue = 0;
+ u32 isrg = 0;
+ u32 *baddr;
err = gfar_of_init(ofdev, &dev);
@@ -716,12 +777,11 @@ static int gfar_probe(struct of_device *ofdev,
priv->node = ofdev->node;
SET_NETDEV_DEV(dev, &ofdev->dev);
- spin_lock_init(&priv->gfargrp.grplock);
spin_lock_init(&priv->bflock);
INIT_WORK(&priv->reset_task, gfar_reset_task);
dev_set_drvdata(&ofdev->dev, priv);
- regs = priv->gfargrp.regs;
+ regs = priv->gfargrp[0].regs;
/* Stop the DMA engine now, in case it was running before */
/* (The firmware could have used it, and left it running). */
@@ -754,7 +814,8 @@ static int gfar_probe(struct of_device *ofdev,
dev->ethtool_ops = &gfar_ethtool_ops;
/* Register for napi ...We are registering NAPI for each grp */
- netif_napi_add(dev, &priv->gfargrp.napi, gfar_poll, GFAR_DEV_WEIGHT);
+ for (i = 0; i < priv->num_grps; i++)
+ netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll, GFAR_DEV_WEIGHT);
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
priv->rx_csum_enable = 1;
@@ -810,25 +871,51 @@ static int gfar_probe(struct of_device *ofdev,
if (dev->features & NETIF_F_IP_CSUM)
dev->hard_header_len += GMAC_FCB_LEN;
+ /* Program the isrg regs only if number of grps > 1 */
+ if (priv->num_grps > 1) {
+ baddr = ®s->isrg0;
+ for (i = 0; i < priv->num_grps; i++) {
+ isrg |= (priv->gfargrp[i].rx_bit_map << ISRG_SHIFT_RX);
+ isrg |= (priv->gfargrp[i].tx_bit_map << ISRG_SHIFT_TX);
+ gfar_write(baddr, isrg);
+ baddr++;
+ isrg = 0x0;
+ }
+ }
+
/* Need to reverse the bit maps as bit_map's MSB is q0
* but, for_each_bit parses from right to left, which
* basically reverses the queue numbers */
- priv->gfargrp.tx_bit_map = reverse_bitmap(priv->gfargrp.tx_bit_map, MAX_TX_QS);
- priv->gfargrp.rx_bit_map = reverse_bitmap(priv->gfargrp.rx_bit_map, MAX_RX_QS);
-
- /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values */
- for_each_bit(i, &priv->gfargrp.rx_bit_map, priv->num_rx_queues) {
- priv->gfargrp.num_rx_queues++;
- rstat = rstat | (RSTAT_CLEAR_RHALT >> i);
- rqueue = rqueue | ((RQUEUE_EN0 | RQUEUE_EX0) >> i);
- }
- for_each_bit (i, &priv->gfargrp.tx_bit_map, priv->num_tx_queues) {
- priv->gfargrp.num_tx_queues++;
- tstat = tstat | (TSTAT_CLEAR_THALT >> i);
- tqueue = tqueue | (TQUEUE_EN0 >> i);
+ for (i = 0; i< priv->num_grps; i++) {
+ priv->gfargrp[i].tx_bit_map = reverse_bitmap(
+ priv->gfargrp[i].tx_bit_map, MAX_TX_QS);
+ priv->gfargrp[i].rx_bit_map = reverse_bitmap(
+ priv->gfargrp[i].rx_bit_map, MAX_RX_QS);
+ }
+
+ /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values,
+ * also assign queues to groups */
+ for (grp_idx = 0; grp_idx < priv->num_grps; grp_idx++) {
+ priv->gfargrp[grp_idx].num_rx_queues = 0x0;
+ for_each_bit(i, &priv->gfargrp[grp_idx].rx_bit_map,
+ priv->num_rx_queues) {
+ priv->gfargrp[grp_idx].num_rx_queues++;
+ priv->rx_queue[i]->grp = &priv->gfargrp[grp_idx];
+ rstat = rstat | (RSTAT_CLEAR_RHALT >> i);
+ rqueue = rqueue | ((RQUEUE_EN0 | RQUEUE_EX0) >> i);
+ }
+ priv->gfargrp[grp_idx].num_tx_queues = 0x0;
+ for_each_bit (i, &priv->gfargrp[grp_idx].tx_bit_map,
+ priv->num_tx_queues) {
+ priv->gfargrp[grp_idx].num_tx_queues++;
+ priv->tx_queue[i]->grp = &priv->gfargrp[grp_idx];
+ tstat = tstat | (TSTAT_CLEAR_THALT >> i);
+ tqueue = tqueue | (TQUEUE_EN0 >> i);
+ }
+ priv->gfargrp[grp_idx].rstat = rstat;
+ priv->gfargrp[grp_idx].tstat = tstat;
+ rstat = tstat =0;
}
- priv->gfargrp.rstat = rstat;
- priv->gfargrp.tstat = tstat;
gfar_write(®s->rqueue, rqueue);
gfar_write(®s->tqueue, tqueue);
@@ -868,20 +955,40 @@ static int gfar_probe(struct of_device *ofdev,
/* fill out IRQ number and name fields */
len_devname = strlen(dev->name);
- strncpy(&priv->gfargrp.int_name_tx[0], dev->name, len_devname);
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- strncpy(&priv->gfargrp.int_name_tx[len_devname],
- "_tx", sizeof("_tx") + 1);
-
- strncpy(&priv->gfargrp.int_name_rx[0], dev->name, len_devname);
- strncpy(&priv->gfargrp.int_name_rx[len_devname],
- "_rx", sizeof("_rx") + 1);
-
- strncpy(&priv->gfargrp.int_name_er[0], dev->name, len_devname);
- strncpy(&priv->gfargrp.int_name_er[len_devname],
- "_er", sizeof("_er") + 1);
- } else
- priv->gfargrp.int_name_tx[len_devname] = '\0';
+ for (i = 0; i < priv->num_grps; i++) {
+ strncpy(&priv->gfargrp[i].int_name_tx[0], dev->name,
+ len_devname);
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+ strncpy(&priv->gfargrp[i].int_name_tx[len_devname],
+ "_g", sizeof("_g"));
+ priv->gfargrp[i].int_name_tx[
+ strlen(priv->gfargrp[i].int_name_tx)] = i+48;
+ strncpy(&priv->gfargrp[i].int_name_tx[strlen(
+ priv->gfargrp[i].int_name_tx)],
+ "_tx", sizeof("_tx") + 1);
+
+ strncpy(&priv->gfargrp[i].int_name_rx[0], dev->name,
+ len_devname);
+ strncpy(&priv->gfargrp[i].int_name_rx[len_devname],
+ "_g", sizeof("_g"));
+ priv->gfargrp[i].int_name_rx[
+ strlen(priv->gfargrp[i].int_name_rx)] = i+48;
+ strncpy(&priv->gfargrp[i].int_name_rx[strlen(
+ priv->gfargrp[i].int_name_rx)],
+ "_rx", sizeof("_rx") + 1);
+
+ strncpy(&priv->gfargrp[i].int_name_er[0], dev->name,
+ len_devname);
+ strncpy(&priv->gfargrp[i].int_name_er[len_devname],
+ "_g", sizeof("_g"));
+ priv->gfargrp[i].int_name_er[strlen(
+ priv->gfargrp[i].int_name_er)] = i+48;
+ strncpy(&priv->gfargrp[i].int_name_er[strlen(\
+ priv->gfargrp[i].int_name_er)],
+ "_er", sizeof("_er") + 1);
+ } else
+ priv->gfargrp[i].int_name_tx[len_devname] = '\0';
+ }
/* Create all the sysfs files */
gfar_init_sysfs(dev);
@@ -902,7 +1009,7 @@ static int gfar_probe(struct of_device *ofdev,
return 0;
register_fail:
- iounmap(priv->gfargrp.regs);
+ unmap_group_regs(priv);
free_tx_pointers(priv);
free_rx_pointers(priv);
if (priv->phy_node)
@@ -925,7 +1032,7 @@ static int gfar_remove(struct of_device *ofdev)
dev_set_drvdata(&ofdev->dev, NULL);
unregister_netdev(priv->ndev);
- iounmap(priv->gfargrp.regs);
+ unmap_group_regs(priv);
free_netdev(priv->ndev);
return 0;
@@ -937,7 +1044,7 @@ static int gfar_suspend(struct device *dev)
{
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
- struct gfar __iomem *regs = NULL;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned long flags;
u32 tempval;
@@ -945,7 +1052,6 @@ static int gfar_suspend(struct device *dev)
(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
netif_device_detach(ndev);
- regs = priv->gfargrp.regs;
if (netif_running(ndev)) {
@@ -969,7 +1075,7 @@ static int gfar_suspend(struct device *dev)
unlock_tx_qs(priv);
local_irq_restore(flags);
- napi_disable(&priv->gfargrp.napi);
+ disable_napi(priv);
if (magic_packet) {
/* Enable interrupt on Magic Packet */
@@ -991,7 +1097,7 @@ static int gfar_resume(struct device *dev)
{
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
- struct gfar __iomem *regs = NULL;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned long flags;
u32 tempval;
int magic_packet = priv->wol_en &&
@@ -1008,8 +1114,6 @@ static int gfar_resume(struct device *dev)
/* Disable Magic Packet mode, in case something
* else woke us up.
*/
- regs = priv->gfargrp.regs;
-
local_irq_save(flags);
lock_tx_qs(priv);
lock_rx_qs(priv);
@@ -1026,7 +1130,7 @@ static int gfar_resume(struct device *dev)
netif_device_attach(ndev);
- napi_enable(&priv->gfargrp.napi);
+ enable_napi(priv);
return 0;
}
@@ -1092,10 +1196,9 @@ static int gfar_legacy_resume(struct of_device *ofdev)
static phy_interface_t gfar_get_interface(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = NULL;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 ecntrl;
- regs = priv->gfargrp.regs;
ecntrl = gfar_read(®s->ecntrl);
if (ecntrl & ECNTRL_SGMII_MODE)
@@ -1219,14 +1322,18 @@ static void init_registers(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar __iomem *regs = NULL;
+ int i = 0;
- regs = priv->gfargrp.regs;
- /* Clear IEVENT */
- gfar_write(®s->ievent, IEVENT_INIT_CLEAR);
+ for (i = 0; i < priv->num_grps; i++) {
+ regs = priv->gfargrp[i].regs;
+ /* Clear IEVENT */
+ gfar_write(®s->ievent, IEVENT_INIT_CLEAR);
- /* Initialize IMASK */
- gfar_write(®s->imask, IMASK_INIT_CLEAR);
+ /* Initialize IMASK */
+ gfar_write(®s->imask, IMASK_INIT_CLEAR);
+ }
+ regs = priv->gfargrp[0].regs;
/* Init hash registers to zero */
gfar_write(®s->igaddr0, 0);
gfar_write(®s->igaddr1, 0);
@@ -1267,15 +1374,20 @@ static void init_registers(struct net_device *dev)
static void gfar_halt_nodisable(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = NULL;
u32 tempval;
+ int i = 0;
- /* Mask all interrupts */
- gfar_write(®s->imask, IMASK_INIT_CLEAR);
+ for (i = 0; i < priv->num_grps; i++) {
+ regs = priv->gfargrp[i].regs;
+ /* Mask all interrupts */
+ gfar_write(®s->imask, IMASK_INIT_CLEAR);
- /* Clear all interrupts */
- gfar_write(®s->ievent, IEVENT_INIT_CLEAR);
+ /* Clear all interrupts */
+ gfar_write(®s->ievent, IEVENT_INIT_CLEAR);
+ }
+ regs = priv->gfargrp[0].regs;
/* Stop the DMA, and wait for it to stop */
tempval = gfar_read(®s->dmactrl);
if ((tempval & (DMACTRL_GRS | DMACTRL_GTS))
@@ -1293,7 +1405,7 @@ static void gfar_halt_nodisable(struct net_device *dev)
void gfar_halt(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
gfar_halt_nodisable(dev);
@@ -1304,10 +1416,18 @@ void gfar_halt(struct net_device *dev)
gfar_write(®s->maccfg1, tempval);
}
+static void free_grp_irqs(struct gfar_priv_grp *grp)
+{
+ free_irq(grp->interruptError, grp);
+ free_irq(grp->interruptTransmit, grp);
+ free_irq(grp->interruptReceive, grp);
+}
+
void stop_gfar(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
unsigned long flags;
+ int i;
phy_stop(priv->phydev);
@@ -1325,11 +1445,12 @@ void stop_gfar(struct net_device *dev)
/* Free the IRQs */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- free_irq(priv->gfargrp.interruptError, &priv->gfargrp);
- free_irq(priv->gfargrp.interruptTransmit, &priv->gfargrp);
- free_irq(priv->gfargrp.interruptReceive, &priv->gfargrp);
+ for (i = 0; i < priv->num_grps; i++)
+ free_grp_irqs(&priv->gfargrp[i]);
} else {
- free_irq(priv->gfargrp.interruptTransmit, &priv->gfargrp);
+ for (i = 0; i < priv->num_grps; i++)
+ free_irq(priv->gfargrp[i].interruptTransmit,
+ &priv->gfargrp[i]);
}
free_skb_resources(priv);
@@ -1417,8 +1538,9 @@ static void free_skb_resources(struct gfar_private *priv)
void gfar_start(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
+ int i = 0;
/* Enable Rx and Tx in MACCFG1 */
tempval = gfar_read(®s->maccfg1);
@@ -1435,92 +1557,149 @@ void gfar_start(struct net_device *dev)
tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
gfar_write(®s->dmactrl, tempval);
- /* Clear THLT/RHLT, so that the DMA starts polling now */
- gfar_write(®s->tstat, priv->gfargrp.tstat);
- gfar_write(®s->rstat, priv->gfargrp.rstat);
-
- /* Unmask the interrupts we look for */
- gfar_write(®s->imask, IMASK_DEFAULT);
+ for (i = 0; i < priv->num_grps; i++) {
+ regs = priv->gfargrp[i].regs;
+ /* Clear THLT/RHLT, so that the DMA starts polling now */
+ gfar_write(®s->tstat, priv->gfargrp[i].tstat);
+ gfar_write(®s->rstat, priv->gfargrp[i].rstat);
+ /* Unmask the interrupts we look for */
+ gfar_write(®s->imask, IMASK_DEFAULT);
+ }
dev->trans_start = jiffies;
}
-/* Bring the controller up and running */
-int startup_gfar(struct net_device *ndev)
+void gfar_configure_coalescing(struct gfar_private *priv,
+ unsigned int tx_mask, unsigned int rx_mask)
{
- struct gfar_private *priv = netdev_priv(ndev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
- int err;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 *baddr;
+ int i = 0;
- gfar_write(®s->imask, IMASK_INIT_CLEAR);
+ /* Backward compatible case ---- even if we enable
+ * multiple queues, there's only single reg to program
+ */
+ gfar_write(®s->txic, 0);
+ if(likely(priv->tx_queue[0]->txcoalescing))
+ gfar_write(®s->txic, priv->tx_queue[0]->txic);
- err = gfar_alloc_skb_resources(ndev);
- if (err)
- return err;
+ gfar_write(®s->rxic, 0);
+ if(unlikely(priv->rx_queue[0]->rxcoalescing))
+ gfar_write(®s->rxic, priv->rx_queue[0]->rxic);
- gfar_init_mac(ndev);
+ if (priv->mode == MQ_MG_MODE) {
+ baddr = ®s->txic0;
+ for_each_bit (i, &tx_mask, priv->num_tx_queues) {
+ if (likely(priv->tx_queue[i]->txcoalescing)) {
+ gfar_write(baddr + i, 0);
+ gfar_write(baddr + i, priv->tx_queue[i]->txic);
+ }
+ }
+
+ baddr = ®s->rxic0;
+ for_each_bit (i, &rx_mask, priv->num_rx_queues) {
+ if (likely(priv->rx_queue[i]->rxcoalescing)) {
+ gfar_write(baddr + i, 0);
+ gfar_write(baddr + i, priv->rx_queue[i]->rxic);
+ }
+ }
+ }
+}
+
+static int register_grp_irqs(struct gfar_priv_grp *grp)
+{
+ struct gfar_private *priv = grp->priv;
+ struct net_device *dev = priv->ndev;
+ int err;
/* If the device has multiple interrupts, register for
* them. Otherwise, only register for the one */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
/* Install our interrupt handlers for Error,
* Transmit, and Receive */
- err = request_irq(priv->gfargrp.interruptError, gfar_error, 0,
- priv->gfargrp.int_name_er, &priv->gfargrp);
- if (err) {
+ if ((err = request_irq(grp->interruptError, gfar_error, 0,
+ grp->int_name_er,grp)) < 0) {
if (netif_msg_intr(priv))
- pr_err("%s: Can't get IRQ %d\n", ndev->name,
- priv->gfargrp.interruptError);
- goto err_irq_fail;
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, grp->interruptError);
+
+ goto err_irq_fail;
}
- err = request_irq(priv->gfargrp.interruptTransmit,
- gfar_transmit, 0,
- priv->gfargrp.int_name_tx,
- &priv->gfargrp);
- if (err) {
+ if ((err = request_irq(grp->interruptTransmit, gfar_transmit,
+ 0, grp->int_name_tx, grp)) < 0) {
if (netif_msg_intr(priv))
- pr_err("%s: Can't get IRQ %d\n", ndev->name,
- priv->gfargrp.interruptTransmit);
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, grp->interruptTransmit);
goto tx_irq_fail;
}
- err = request_irq(priv->gfargrp.interruptReceive,
- gfar_receive, 0,
- priv->gfargrp.int_name_rx,
- &priv->gfargrp);
- if (err) {
+ if ((err = request_irq(grp->interruptReceive, gfar_receive, 0,
+ grp->int_name_rx, grp)) < 0) {
if (netif_msg_intr(priv))
- pr_err("%s: Can't get IRQ %d (receive0)\n",
- ndev->name,
- priv->gfargrp.interruptReceive);
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, grp->interruptReceive);
goto rx_irq_fail;
}
} else {
- err = request_irq(priv->gfargrp.interruptTransmit,
- gfar_interrupt, 0,
- priv->gfargrp.int_name_tx,
- &priv->gfargrp);
- if (err) {
+ if ((err = request_irq(grp->interruptTransmit, gfar_interrupt, 0,
+ grp->int_name_tx, grp)) < 0) {
if (netif_msg_intr(priv))
- pr_err("%s: Can't get IRQ %d\n", ndev->name,
- priv->gfargrp.interruptTransmit);
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, grp->interruptTransmit);
goto err_irq_fail;
}
}
+ return 0;
+
+rx_irq_fail:
+ free_irq(grp->interruptTransmit, grp);
+tx_irq_fail:
+ free_irq(grp->interruptError, grp);
+err_irq_fail:
+ return err;
+
+}
+
+/* Bring the controller up and running */
+int startup_gfar(struct net_device *ndev)
+{
+ struct gfar_private *priv = netdev_priv(ndev);
+ struct gfar __iomem *regs = NULL;
+ int err, i, j;
+
+ for (i = 0; i < priv->num_grps; i++) {
+ regs= priv->gfargrp[i].regs;
+ gfar_write(®s->imask, IMASK_INIT_CLEAR);
+ }
+
+ regs= priv->gfargrp[0].regs;
+ err = gfar_alloc_skb_resources(ndev);
+ if (err)
+ return err;
+
+ gfar_init_mac(ndev);
+
+ for (i = 0; i < priv->num_grps; i++) {
+ err = register_grp_irqs(&priv->gfargrp[i]);
+ if (err) {
+ for (j = 0; j < i; j++)
+ free_grp_irqs(&priv->gfargrp[j]);
+ goto irq_fail;
+ }
+ }
+
/* Start the controller */
gfar_start(ndev);
phy_start(priv->phydev);
+ gfar_configure_coalescing(priv, 0xFF, 0xFF);
+
return 0;
-rx_irq_fail:
- free_irq(priv->gfargrp.interruptTransmit, &priv->gfargrp);
-tx_irq_fail:
- free_irq(priv->gfargrp.interruptError, &priv->gfargrp);
-err_irq_fail:
+irq_fail:
free_skb_resources(priv);
return err;
}
@@ -1532,7 +1711,7 @@ static int gfar_enet_open(struct net_device *dev)
struct gfar_private *priv = netdev_priv(dev);
int err;
- napi_enable(&priv->gfargrp.napi);
+ enable_napi(priv);
skb_queue_head_init(&priv->rx_recycle);
@@ -1544,13 +1723,13 @@ static int gfar_enet_open(struct net_device *dev)
err = init_phy(dev);
if (err) {
- napi_disable(&priv->gfargrp.napi);
+ disable_napi(priv);
return err;
}
err = startup_gfar(dev);
if (err) {
- napi_disable(&priv->gfargrp.napi);
+ disable_napi(priv);
return err;
}
@@ -1639,7 +1818,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_queue = priv->tx_queue[rq];
txq = netdev_get_tx_queue(dev, rq);
base = tx_queue->tx_bd_base;
- regs = priv->gfargrp.regs;
+ regs = tx_queue->grp->regs;
/* make space for additional header when fcb is needed */
if (((skb->ip_summed == CHECKSUM_PARTIAL) ||
@@ -1776,7 +1955,7 @@ static int gfar_close(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- napi_disable(&priv->gfargrp.napi);
+ disable_napi(priv);
skb_queue_purge(&priv->rx_recycle);
cancel_work_sync(&priv->reset_task);
@@ -1809,7 +1988,7 @@ static void gfar_vlan_rx_register(struct net_device *dev,
unsigned long flags;
u32 tempval;
- regs = priv->gfargrp.regs;
+ regs = priv->gfargrp[0].regs;
local_irq_save(flags);
lock_rx_qs(priv);
@@ -1853,7 +2032,7 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
{
int tempsize, tempval;
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
int oldsize = priv->rx_buffer_size;
int frame_size = new_mtu + ETH_HLEN;
@@ -2275,7 +2454,7 @@ static int gfar_poll(struct napi_struct *napi, int budget)
struct gfar_priv_grp *gfargrp = container_of(napi,
struct gfar_priv_grp, napi);
struct gfar_private *priv = gfargrp->priv;
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = gfargrp->regs;
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
int rx_cleaned = 0, budget_per_queue = 0, rx_cleaned_per_queue = 0;
@@ -2334,14 +2513,8 @@ static int gfar_poll(struct napi_struct *napi, int budget)
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
- if (likely(rx_queue->rxcoalescing)) {
- gfar_write(®s->rxic, 0);
- gfar_write(®s->rxic, rx_queue->rxic);
- }
- if (likely(tx_queue->txcoalescing)) {
- gfar_write(®s->txic, 0);
- gfar_write(®s->txic, tx_queue->txic);
- }
+ gfar_configure_coalescing(priv,
+ gfargrp->rx_bit_map, gfargrp->tx_bit_map);
}
return rx_cleaned;
@@ -2356,20 +2529,26 @@ static int gfar_poll(struct napi_struct *napi, int budget)
static void gfar_netpoll(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
+ int i = 0;
/* If the device has multiple interrupts, run tx/rx */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- disable_irq(priv->gfargrp.interruptTransmit);
- disable_irq(priv->gfargrp.interruptReceive);
- disable_irq(priv->gfargrp.interruptError);
- gfar_interrupt(priv->gfargrp.interruptTransmit, &priv->gfargrp);
- enable_irq(priv->gfargrp.interruptError);
- enable_irq(priv->gfargrp.interruptReceive);
- enable_irq(priv->gfargrp.interruptTransmit);
+ for (i = 0; i < priv->num_grps; i++) {
+ disable_irq(priv->gfargrp[i].interruptTransmit);
+ disable_irq(priv->gfargrp[i].interruptReceive);
+ disable_irq(priv->gfargrp[i].interruptError);
+ gfar_interrupt(priv->gfargrp[i].interruptTransmit,
+ &priv->gfargrp[i]);
+ enable_irq(priv->gfargrp[i].interruptError);
+ enable_irq(priv->gfargrp[i].interruptReceive);
+ enable_irq(priv->gfargrp[i].interruptTransmit);
+ }
} else {
- disable_irq(priv->gfargrp.interruptTransmit);
- gfar_interrupt(priv->gfargrp.interruptTransmit, &priv->gfargrp);
- enable_irq(priv->gfargrp.interruptTransmit);
+ for (i = 0; i < priv->num_grps; i++) {
+ disable_irq(priv->gfargrp[i].interruptTransmit);
+ gfar_interrupt(priv->gfargrp[i].interruptTransmit,
+ &priv->gfargrp[i]);
+ enable_irq(priv->gfargrp[i].interruptTransmit);
}
}
#endif
@@ -2406,7 +2585,7 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id)
static void adjust_link(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned long flags;
struct phy_device *phydev = priv->phydev;
int new_state = 0;
@@ -2490,7 +2669,7 @@ static void gfar_set_multi(struct net_device *dev)
{
struct dev_mc_list *mc_ptr;
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
if (dev->flags & IFF_PROMISC) {
@@ -2623,7 +2802,7 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr)
static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
int idx;
char tmpbuf[MAC_ADDR_LEN];
u32 tempval;
@@ -2730,6 +2909,9 @@ static struct of_device_id gfar_match[] =
.type = "network",
.compatible = "gianfar",
},
+ {
+ .compatible = "fsl,etsec2",
+ },
{},
};
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 70c25ce..a43b238 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -79,6 +79,9 @@ extern const char gfar_driver_version[];
#define MAX_TX_QS 0x8
#define MAX_RX_QS 0x8
+/* MAXIMUM NUMBER OF GROUPS SUPPORTED */
+#define MAXGROUPS 0x2
+
/* These need to be powers of 2 for this driver */
#define DEFAULT_TX_RING_SIZE 256
#define DEFAULT_RX_RING_SIZE 256
@@ -795,7 +798,24 @@ struct gfar {
#define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200
#define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400
+#if (MAXGROUPS == 2)
+#define DEFAULT_MAPPING 0xAA
+#else
#define DEFAULT_MAPPING 0xFF
+#endif
+
+#define ISRG_SHIFT_TX 0x10
+#define ISRG_SHIFT_RX 0x18
+
+/* The same driver can operate in two modes */
+/* SQ_SG_MODE: Single Queue Single Group Mode
+ * (Backward compatible mode)
+ * MQ_MG_MODE: Multi Queue Multi Group mode
+ */
+enum {
+ SQ_SG_MODE = 0,
+ MQ_MG_MODE
+};
/**
* struct gfar_priv_tx_q - per tx queue structure
@@ -825,6 +845,7 @@ struct gfar_priv_tx_q {
struct txbd8 *cur_tx;
struct txbd8 *dirty_tx;
struct net_device *dev;
+ struct gfar_priv_grp *grp;
u16 skb_curtx;
u16 skb_dirtytx;
u16 qindex;
@@ -857,6 +878,7 @@ struct gfar_priv_rx_q {
struct rxbd8 *rx_bd_base;
struct rxbd8 *cur_rx;
struct net_device *dev;
+ struct gfar_priv_grp *grp;
u16 skb_currx;
u16 qindex;
unsigned int rx_ring_size;
@@ -884,6 +906,7 @@ struct gfar_priv_grp {
struct napi_struct napi;
struct gfar_private *priv;
struct gfar __iomem *regs;
+ unsigned int grp_id;
unsigned int rx_bit_map;
unsigned int tx_bit_map;
unsigned int num_tx_queues;
@@ -915,6 +938,8 @@ struct gfar_private {
/* Indicates how many tx, rx queues are enabled */
unsigned int num_tx_queues;
unsigned int num_rx_queues;
+ unsigned int num_grps;
+ unsigned int mode;
/* The total tx and rx ring size for the enabled queues */
unsigned int total_tx_ring_size;
@@ -924,7 +949,7 @@ struct gfar_private {
struct net_device *ndev;
struct of_device *ofdev;
- struct gfar_priv_grp gfargrp;
+ struct gfar_priv_grp gfargrp[MAXGROUPS];
struct gfar_priv_tx_q *tx_queue[MAX_TX_QS];
struct gfar_priv_rx_q *rx_queue[MAX_RX_QS];
@@ -998,6 +1023,8 @@ extern void stop_gfar(struct net_device *dev);
extern void gfar_halt(struct net_device *dev);
extern void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev,
int enable, u32 regnum, u32 read);
+extern void gfar_configure_coalescing(struct gfar_private *priv,
+ unsigned int tx_mask, unsigned int rx_mask);
void gfar_init_sysfs(struct net_device *dev);
extern const struct ethtool_ops gfar_ethtool_ops;
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index d3d2623..562f6c2 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -137,7 +137,7 @@ static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
{
int i;
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u64 *extra = (u64 *) & priv->extra_stats;
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
@@ -226,7 +226,7 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, voi
{
int i;
struct gfar_private *priv = netdev_priv(dev);
- u32 __iomem *theregs = (u32 __iomem *) priv->gfargrp.regs;
+ u32 __iomem *theregs = (u32 __iomem *) priv->gfargrp[0].regs;
u32 *buf = (u32 *) regbuf;
for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++)
@@ -352,22 +352,23 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
+ int i = 0;
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
- tx_queue = priv->tx_queue[0];
- rx_queue = priv->rx_queue[0];
-
/* Set up rx coalescing */
+ /* As of now, we will enable/disable coalescing for all
+ * queues together in case of eTSEC2, this will be modified
+ * along with the ethtool interface */
if ((cvals->rx_coalesce_usecs == 0) ||
- (cvals->rx_max_coalesced_frames == 0))
- rx_queue->rxcoalescing = 0;
- else
- rx_queue->rxcoalescing = 1;
+ (cvals->rx_max_coalesced_frames == 0)) {
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i]->rxcoalescing = 0;
+ } else {
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i]->rxcoalescing = 1;
+ }
if (NULL == priv->phydev)
return -ENODEV;
@@ -385,15 +386,21 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- rx_queue->rxic = mk_ic_value(cvals->rx_max_coalesced_frames,
- gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs));
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i]->rxic = mk_ic_value(
+ cvals->rx_max_coalesced_frames,
+ gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs));
+ }
/* Set up tx coalescing */
if ((cvals->tx_coalesce_usecs == 0) ||
- (cvals->tx_max_coalesced_frames == 0))
- tx_queue->txcoalescing = 0;
- else
- tx_queue->txcoalescing = 1;
+ (cvals->tx_max_coalesced_frames == 0)) {
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->tx_queue[i]->txcoalescing = 0;
+ } else {
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->tx_queue[i]->txcoalescing = 1;
+ }
/* Check the bounds of the values */
if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
@@ -408,16 +415,13 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- tx_queue->txic = mk_ic_value(cvals->tx_max_coalesced_frames,
- gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
-
- gfar_write(®s->rxic, 0);
- if (rx_queue->rxcoalescing)
- gfar_write(®s->rxic, rx_queue->rxic);
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ priv->tx_queue[i]->txic = mk_ic_value(
+ cvals->tx_max_coalesced_frames,
+ gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
+ }
- gfar_write(®s->txic, 0);
- if (tx_queue->txcoalescing)
- gfar_write(®s->txic, tx_queue->txic);
+ gfar_configure_coalescing(priv, 0xFF, 0xFF);
return 0;
}
diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c
index 4b726f6..3724835 100644
--- a/drivers/net/gianfar_sysfs.c
+++ b/drivers/net/gianfar_sysfs.c
@@ -50,7 +50,7 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
int new_setting = 0;
u32 temp;
unsigned long flags;
@@ -105,7 +105,7 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -164,7 +164,7 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned short index = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -212,7 +212,7 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -252,7 +252,7 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -293,7 +293,7 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
--
1.5.2.2
^ permalink raw reply related
* [PATCH v2 7/7] gianfar: Basic Support for programming hash rules
From: Sandeep Gopalpet @ 2009-10-27 16:55 UTC (permalink / raw)
To: netdev; +Cc: davem, Sandeep Gopalpet
This patch provides basic hash rules programming via the ethtool
interface.
Signed-off-by: Sandeep Gopalpet <sandeep.kumar@freescale.com>
---
drivers/net/gianfar.c | 73 +++++++++++++
drivers/net/gianfar.h | 93 ++++++++++++++++
drivers/net/gianfar_ethtool.c | 236 +++++++++++++++++++++++++++++++++++++++++
include/linux/ethtool.h | 2 +
4 files changed, 404 insertions(+), 0 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index daff741..1b1a8b4 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -416,6 +416,9 @@ static const struct net_device_ops gfar_netdev_ops = {
#endif
};
+unsigned int ftp_rqfpr[MAX_FILER_IDX + 1];
+unsigned int ftp_rqfcr[MAX_FILER_IDX + 1];
+
inline void lock_rx_qs(struct gfar_private *priv)
{
int i = 0x0;
@@ -751,6 +754,73 @@ static unsigned int reverse_bitmap(unsigned int bit_map, unsigned int max_qs)
}
return new_bit_map;
}
+
+u32 cluster_entry_per_class(struct gfar_private *priv, u32 rqfar, u32 class)
+{
+ u32 rqfpr = FPR_FILER_MASK;
+ u32 rqfcr = 0x0;
+
+ rqfar--;
+ rqfcr = RQFCR_CLE | RQFCR_PID_MASK | RQFCR_CMP_EXACT;
+ ftp_rqfpr[rqfar] = rqfpr;
+ ftp_rqfcr[rqfar] = rqfcr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar--;
+ rqfcr = RQFCR_CMP_NOMATCH;
+ ftp_rqfpr[rqfar] = rqfpr;
+ ftp_rqfcr[rqfar] = rqfcr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar--;
+ rqfcr = RQFCR_CMP_EXACT | RQFCR_PID_PARSE | RQFCR_CLE | RQFCR_AND;
+ rqfpr = class;
+ ftp_rqfcr[rqfar] = rqfcr;
+ ftp_rqfpr[rqfar] = rqfpr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar--;
+ rqfcr = RQFCR_CMP_EXACT | RQFCR_PID_MASK | RQFCR_AND;
+ rqfpr = class;
+ ftp_rqfcr[rqfar] = rqfcr;
+ ftp_rqfpr[rqfar] = rqfpr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ return rqfar;
+}
+
+static void gfar_init_filer_table(struct gfar_private *priv)
+{
+ int i = 0x0;
+ u32 rqfar = MAX_FILER_IDX;
+ u32 rqfcr = 0x0;
+ u32 rqfpr = FPR_FILER_MASK;
+
+ /* Default rule */
+ rqfcr = RQFCR_CMP_MATCH;
+ ftp_rqfcr[rqfar] = rqfcr;
+ ftp_rqfpr[rqfar] = rqfpr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_UDP);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_TCP);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_UDP);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_TCP);
+
+ /* cur_filer_idx indicated the fisrt non-masked rule */
+ priv->cur_filer_idx = rqfar;
+
+ /* Rest are masked rules */
+ rqfcr = RQFCR_CMP_NOMATCH;
+ for (i = 0; i < rqfar; i++) {
+ ftp_rqfcr[i] = rqfcr;
+ ftp_rqfpr[i] = rqfpr;
+ gfar_write_filer(priv, i, rqfcr, rqfpr);
+ }
+}
+
/* Set up the ethernet device structure, private data,
* and anything else we need before we start */
static int gfar_probe(struct of_device *ofdev,
@@ -990,6 +1060,9 @@ static int gfar_probe(struct of_device *ofdev,
priv->gfargrp[i].int_name_tx[len_devname] = '\0';
}
+ /* Initialize the filer table */
+ gfar_init_filer_table(priv);
+
/* Create all the sysfs files */
gfar_init_sysfs(dev);
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index a43b238..1c9fdea 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -381,6 +381,84 @@ extern const char gfar_driver_version[];
#define BD_LFLAG(flags) ((flags) << 16)
#define BD_LENGTH_MASK 0x0000ffff
+#define CLASS_CODE_UNRECOG 0x00
+#define CLASS_CODE_DUMMY1 0x01
+#define CLASS_CODE_ETHERTYPE1 0x02
+#define CLASS_CODE_ETHERTYPE2 0x03
+#define CLASS_CODE_USER_PROG1 0x04
+#define CLASS_CODE_USER_PROG2 0x05
+#define CLASS_CODE_USER_PROG3 0x06
+#define CLASS_CODE_USER_PROG4 0x07
+#define CLASS_CODE_TCP_IPV4 0x08
+#define CLASS_CODE_UDP_IPV4 0x09
+#define CLASS_CODE_AH_ESP_IPV4 0x0a
+#define CLASS_CODE_SCTP_IPV4 0x0b
+#define CLASS_CODE_TCP_IPV6 0x0c
+#define CLASS_CODE_UDP_IPV6 0x0d
+#define CLASS_CODE_AH_ESP_IPV6 0x0e
+#define CLASS_CODE_SCTP_IPV6 0x0f
+
+#define FPR_FILER_MASK 0xFFFFFFFF
+#define MAX_FILER_IDX 0xFF
+
+/* RQFCR register bits */
+#define RQFCR_GPI 0x80000000
+#define RQFCR_HASHTBL_Q 0x00000000
+#define RQFCR_HASHTBL_0 0x00020000
+#define RQFCR_HASHTBL_1 0x00040000
+#define RQFCR_HASHTBL_2 0x00060000
+#define RQFCR_HASHTBL_3 0x00080000
+#define RQFCR_HASH 0x00010000
+#define RQFCR_CLE 0x00000200
+#define RQFCR_RJE 0x00000100
+#define RQFCR_AND 0x00000080
+#define RQFCR_CMP_EXACT 0x00000000
+#define RQFCR_CMP_MATCH 0x00000020
+#define RQFCR_CMP_NOEXACT 0x00000040
+#define RQFCR_CMP_NOMATCH 0x00000060
+
+/* RQFCR PID values */
+#define RQFCR_PID_MASK 0x00000000
+#define RQFCR_PID_PARSE 0x00000001
+#define RQFCR_PID_ARB 0x00000002
+#define RQFCR_PID_DAH 0x00000003
+#define RQFCR_PID_DAL 0x00000004
+#define RQFCR_PID_SAH 0x00000005
+#define RQFCR_PID_SAL 0x00000006
+#define RQFCR_PID_ETY 0x00000007
+#define RQFCR_PID_VID 0x00000008
+#define RQFCR_PID_PRI 0x00000009
+#define RQFCR_PID_TOS 0x0000000A
+#define RQFCR_PID_L4P 0x0000000B
+#define RQFCR_PID_DIA 0x0000000C
+#define RQFCR_PID_SIA 0x0000000D
+#define RQFCR_PID_DPT 0x0000000E
+#define RQFCR_PID_SPT 0x0000000F
+
+/* RQFPR when PID is 0x0001 */
+#define RQFPR_HDR_GE_512 0x00200000
+#define RQFPR_LERR 0x00100000
+#define RQFPR_RAR 0x00080000
+#define RQFPR_RARQ 0x00040000
+#define RQFPR_AR 0x00020000
+#define RQFPR_ARQ 0x00010000
+#define RQFPR_EBC 0x00008000
+#define RQFPR_VLN 0x00004000
+#define RQFPR_CFI 0x00002000
+#define RQFPR_JUM 0x00001000
+#define RQFPR_IPF 0x00000800
+#define RQFPR_FIF 0x00000400
+#define RQFPR_IPV4 0x00000200
+#define RQFPR_IPV6 0x00000100
+#define RQFPR_ICC 0x00000080
+#define RQFPR_ICV 0x00000040
+#define RQFPR_TCP 0x00000020
+#define RQFPR_UDP 0x00000010
+#define RQFPR_TUC 0x00000008
+#define RQFPR_TUV 0x00000004
+#define RQFPR_PER 0x00000002
+#define RQFPR_EER 0x00000001
+
/* TxBD status field bits */
#define TXBD_READY 0x8000
#define TXBD_PADCRC 0x4000
@@ -958,6 +1036,8 @@ struct gfar_private {
unsigned int rx_stash_size;
unsigned int rx_stash_index;
+ u32 cur_filer_idx;
+
struct sk_buff_head rx_recycle;
struct vlan_group *vlgrp;
@@ -1001,6 +1081,9 @@ struct gfar_private {
struct gfar_extra_stats extra_stats;
};
+extern unsigned int ftp_rqfpr[MAX_FILER_IDX + 1];
+extern unsigned int ftp_rqfcr[MAX_FILER_IDX + 1];
+
static inline u32 gfar_read(volatile unsigned __iomem *addr)
{
u32 val;
@@ -1013,6 +1096,16 @@ static inline void gfar_write(volatile unsigned __iomem *addr, u32 val)
out_be32(addr, val);
}
+static inline void gfar_write_filer(struct gfar_private *priv,
+ unsigned int far, unsigned int fcr, unsigned int fpr)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+
+ gfar_write(®s->rqfar, far);
+ gfar_write(®s->rqfcr, fcr);
+ gfar_write(®s->rqfpr, fpr);
+}
+
extern inline void lock_rx_qs(struct gfar_private *priv);
extern inline void lock_tx_qs(struct gfar_private *priv);
extern inline void unlock_rx_qs(struct gfar_private *priv);
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index 562f6c2..1010367 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -645,6 +645,241 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
}
#endif
+static int gfar_ethflow_to_class(int flow_type, u64 *class)
+{
+ switch (flow_type) {
+ case TCP_V4_FLOW:
+ *class = CLASS_CODE_TCP_IPV4;
+ break;
+ case UDP_V4_FLOW:
+ *class = CLASS_CODE_UDP_IPV4;
+ break;
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ *class = CLASS_CODE_AH_ESP_IPV4;
+ break;
+ case SCTP_V4_FLOW:
+ *class = CLASS_CODE_SCTP_IPV4;
+ break;
+ case TCP_V6_FLOW:
+ *class = CLASS_CODE_TCP_IPV6;
+ break;
+ case UDP_V6_FLOW:
+ *class = CLASS_CODE_UDP_IPV6;
+ break;
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ *class = CLASS_CODE_AH_ESP_IPV6;
+ break;
+ case SCTP_V6_FLOW:
+ *class = CLASS_CODE_SCTP_IPV6;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow)
+{
+ u32 fcr = 0x0, fpr = FPR_FILER_MASK;
+
+ if (ethflow & RXH_L2DA) {
+ fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH |
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+
+ fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH |
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_VLAN) {
+ fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_IP_SRC) {
+ fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & (RXH_IP_DST)) {
+ fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L3_PROTO) {
+ fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L4_B_0_1) {
+ fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L4_B_2_3) {
+ fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+}
+
+static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class)
+{
+ unsigned int last_rule_idx = priv->cur_filer_idx;
+ unsigned int cmp_rqfpr;
+ unsigned int local_rqfpr[MAX_FILER_IDX + 1];
+ unsigned int local_rqfcr[MAX_FILER_IDX + 1];
+ int i = 0x0, k = 0x0;
+ int j = MAX_FILER_IDX, l = 0x0;
+
+ switch (class) {
+ case TCP_V4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP;
+ break;
+ case UDP_V4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP;
+ break;
+ case TCP_V6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP;
+ break;
+ case UDP_V6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP;
+ break;
+ case IPV4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4;
+ case IPV6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6;
+ break;
+ default:
+ printk(KERN_ERR "Right now this class is not supported\n");
+ return 0;
+ }
+
+ for (i = 0; i < MAX_FILER_IDX + 1; i++) {
+ local_rqfpr[j] = ftp_rqfpr[i];
+ local_rqfcr[j] = ftp_rqfcr[i];
+ j--;
+ if ((ftp_rqfcr[i] == (RQFCR_PID_PARSE |
+ RQFCR_CLE |RQFCR_AND)) &&
+ (ftp_rqfpr[i] == cmp_rqfpr))
+ break;
+ }
+
+ if (i == MAX_FILER_IDX + 1) {
+ printk(KERN_ERR "No parse rule found, ");
+ printk(KERN_ERR "can't create hash rules\n");
+ return 0;
+ }
+
+ /* If a match was found, then it begins the starting of a cluster rule
+ * if it was already programmed, we need to overwrite these rules
+ */
+ for (l = i+1; l < MAX_FILER_IDX; l++) {
+ if ((ftp_rqfcr[l] & RQFCR_CLE) &&
+ !(ftp_rqfcr[l] & RQFCR_AND)) {
+ ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT |
+ RQFCR_HASHTBL_0 | RQFCR_PID_MASK;
+ ftp_rqfpr[l] = FPR_FILER_MASK;
+ gfar_write_filer(priv, l, ftp_rqfcr[l], ftp_rqfpr[l]);
+ break;
+ }
+
+ if (!(ftp_rqfcr[l] & RQFCR_CLE) && (ftp_rqfcr[l] & RQFCR_AND))
+ continue;
+ else {
+ local_rqfpr[j] = ftp_rqfpr[l];
+ local_rqfcr[j] = ftp_rqfcr[l];
+ j--;
+ }
+ }
+
+ priv->cur_filer_idx = l - 1;
+ last_rule_idx = l;
+
+ /* hash rules */
+ ethflow_to_filer_rules(priv, ethflow);
+
+ /* Write back the popped out rules again */
+ for (k = j+1; k < MAX_FILER_IDX; k++) {
+ ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k];
+ ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k];
+ gfar_write_filer(priv, priv->cur_filer_idx,
+ local_rqfcr[k], local_rqfpr[k]);
+ if (!priv->cur_filer_idx)
+ break;
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ return 1;
+}
+
+static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd)
+{
+ u64 class;
+
+ if (!gfar_ethflow_to_class(cmd->flow_type, &class))
+ return -EINVAL;
+
+ if (class < CLASS_CODE_USER_PROG1 ||
+ class > CLASS_CODE_SCTP_IPV6)
+ return -EINVAL;
+
+ /* write the filer rules here */
+ if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type))
+ return -1;
+
+ return 0;
+}
+
+static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ int ret = 0;
+
+ switch(cmd->cmd) {
+ case ETHTOOL_SRXFH:
+ ret = gfar_set_hash_opts(priv, cmd);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
const struct ethtool_ops gfar_ethtool_ops = {
.get_settings = gfar_gsettings,
.set_settings = gfar_ssettings,
@@ -670,4 +905,5 @@ const struct ethtool_ops gfar_ethtool_ops = {
.get_wol = gfar_get_wol,
.set_wol = gfar_set_wol,
#endif
+ .set_rxnfc = gfar_set_nfc,
};
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index eb1a48d..edd03b7 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -674,6 +674,8 @@ struct ethtool_ops {
#define AH_V6_FLOW 0x0b
#define ESP_V6_FLOW 0x0c
#define IP_USER_FLOW 0x0d
+#define IPV4_FLOW 0x10
+#define IPV6_FLOW 0x11
/* L3-L4 network traffic flow hash options */
#define RXH_L2DA (1 << 1)
--
1.5.2.2
^ permalink raw reply related
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