* [PATCH] gre_in_range: 16/32 bit fix
From: Alexey Dobriyan @ 2006-05-18 13:38 UTC (permalink / raw)
To: netdev; +Cc: netfilter-devel
GRE keys are 16 bit.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
---
net/ipv4/netfilter/ip_nat_proto_gre.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
--- a/net/ipv4/netfilter/ip_nat_proto_gre.c
+++ 1/net/ipv4/netfilter/ip_nat_proto_gre.c
@@ -49,15 +49,15 @@ gre_in_range(const struct ip_conntrack_t
const union ip_conntrack_manip_proto *min,
const union ip_conntrack_manip_proto *max)
{
- u_int32_t key;
+ __be16 key;
if (maniptype == IP_NAT_MANIP_SRC)
key = tuple->src.u.gre.key;
else
key = tuple->dst.u.gre.key;
- return ntohl(key) >= ntohl(min->gre.key)
- && ntohl(key) <= ntohl(max->gre.key);
+ return ntohs(key) >= ntohs(min->gre.key)
+ && ntohs(key) <= ntohs(max->gre.key);
}
/* generate unique tuple ... */
^ permalink raw reply
* Re: [IPSEC]: Optimise be16/be32 conversions
From: Herbert Xu @ 2006-05-18 12:49 UTC (permalink / raw)
To: Lennert Buytenhek; +Cc: Alexey Dobriyan, David S. Miller, netdev
In-Reply-To: <20060518123908.GA13351@xi.wantstofly.org>
[-- Attachment #1: Type: text/plain, Size: 1368 bytes --]
On Thu, May 18, 2006 at 02:39:08PM +0200, Lennert Buytenhek wrote:
> On Thu, May 18, 2006 at 10:36:32PM +1000, Herbert Xu wrote:
>
> > +#define __be32_to_be16(x) ((__force __be16)(__be32)x)
> > +#define __be16_to_be32(x) ((__force __be32)(__be16)x)
> > [...]
> > +#define __be32_to_be16(x) ((__force __be16)((__force __u32)(__be32)x >> 16))
> > +#define __be16_to_be32(x) ((__force __be32)((__force __u32)(__be16)x << 16))
>
> Sure that that's safe without the parens?
Good point. Here is a fixed version.
[IPSEC]: Optimise be16/be32 conversions
Up until now we've used constructs such as ntohl(ntohs(x)) to convert
between be16 and be32 quantities, in particular, the CPI for IPComp.
The compiler dutifully generates code on i386 to perform both operations
rather than optimising them away.
So it's worthwhile to add new primitives to handle these operations
specifically. The following patch addes be16_to_be32 and be32_to_be16
and uses them for IPComp.
This patch was prompted by a sparse clean-up patch from Alexey Dobriyan
and incorporates parentheses from Lennert Buytenhek :)
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Thanks,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
[-- Attachment #2: ipcomp-spi-cleanup-2.patch --]
[-- Type: text/plain, Size: 3681 bytes --]
diff --git a/include/linux/byteorder/big_endian.h b/include/linux/byteorder/big_endian.h
index bef8789..9b0279a 100644
--- a/include/linux/byteorder/big_endian.h
+++ b/include/linux/byteorder/big_endian.h
@@ -101,6 +101,9 @@
#define __cpu_to_be16s(x) do {} while (0)
#define __be16_to_cpus(x) do {} while (0)
+#define __be32_to_be16(x) ((__force __be16)(__be32)(x))
+#define __be16_to_be32(x) ((__force __be32)(__be16)(x))
+
#include <linux/byteorder/generic.h>
#endif /* _LINUX_BYTEORDER_BIG_ENDIAN_H */
diff --git a/include/linux/byteorder/generic.h b/include/linux/byteorder/generic.h
index e86e4a9..0bafb90 100644
--- a/include/linux/byteorder/generic.h
+++ b/include/linux/byteorder/generic.h
@@ -124,6 +124,8 @@
#define be32_to_cpus __be32_to_cpus
#define cpu_to_be16s __cpu_to_be16s
#define be16_to_cpus __be16_to_cpus
+#define be32_to_be16 __be32_to_be16
+#define be16_to_be32 __be16_to_be32
#endif
diff --git a/include/linux/byteorder/little_endian.h b/include/linux/byteorder/little_endian.h
index 86e62b7..0633776 100644
--- a/include/linux/byteorder/little_endian.h
+++ b/include/linux/byteorder/little_endian.h
@@ -101,6 +101,9 @@
#define __cpu_to_be16s(x) __swab16s((x))
#define __be16_to_cpus(x) __swab16s((x))
+#define __be32_to_be16(x) ((__force __be16)((__force __u32)(__be32)(x) >> 16))
+#define __be16_to_be32(x) ((__force __be32)((__force __u32)(__be16)(x) << 16))
+
#include <linux/byteorder/generic.h>
#endif /* _LINUX_BYTEORDER_LITTLE_ENDIAN_H */
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index cd810f4..db04339 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -188,7 +188,7 @@
ipch = (struct ip_comp_hdr *)((char *)iph + iph->ihl * 4);
ipch->nexthdr = iph->protocol;
ipch->flags = 0;
- ipch->cpi = htons((u16 )ntohl(x->id.spi));
+ ipch->cpi = be32_to_be16(x->id.spi);
iph->protocol = IPPROTO_COMP;
ip_send_check(iph);
return 0;
@@ -210,7 +210,7 @@
skb->h.icmph->code != ICMP_FRAG_NEEDED)
return;
- spi = ntohl(ntohs(ipch->cpi));
+ spi = be16_to_be32(ipch->cpi);
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr,
spi, IPPROTO_COMP, AF_INET);
if (!x)
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index f285bbf..30b671c 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -219,9 +219,9 @@
case IPPROTO_COMP:
if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
- u16 *ipcomp_hdr = (u16 *)xprth;
+ __be16 *ipcomp_hdr = (__be16 *)xprth;
- fl->fl_ipsec_spi = ntohl(ntohs(ipcomp_hdr[1]));
+ fl->fl_ipsec_spi = be16_to_be32(ipcomp_hdr[1]);
}
break;
default:
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index 05eb67d..6ed793e 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -190,7 +190,7 @@
ipch = (struct ipv6_comp_hdr *)start;
ipch->nexthdr = *skb->nh.raw;
ipch->flags = 0;
- ipch->cpi = htons((u16 )ntohl(x->id.spi));
+ ipch->cpi = be32_to_be16(x->id.spi);
*skb->nh.raw = IPPROTO_COMP;
out_ok:
@@ -208,7 +208,7 @@
if (type != ICMPV6_DEST_UNREACH && type != ICMPV6_PKT_TOOBIG)
return;
- spi = ntohl(ntohs(ipcomph->cpi));
+ spi = be16_to_be32(ipcomph->cpi);
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, IPPROTO_COMP, AF_INET6);
if (!x)
return;
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index b549710..65f208d 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -62,7 +62,7 @@
case IPPROTO_COMP:
if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
return -EINVAL;
- *spi = ntohl(ntohs(*(u16*)(skb->h.raw + 2)));
+ *spi = be16_to_be32(*(__be16 *)(skb->h.raw + 2));
*seq = 0;
return 0;
default:
^ permalink raw reply related
* [IPSEC]: Optimise be16/be32 conversions
From: Herbert Xu @ 2006-05-18 12:36 UTC (permalink / raw)
To: Alexey Dobriyan, David S. Miller; +Cc: netdev
In-Reply-To: <20060518093733.GA15918@mipter.zuzino.mipt.ru>
[-- Attachment #1: Type: text/plain, Size: 1248 bytes --]
On Thu, May 18, 2006 at 09:37:33AM +0000, Alexey Dobriyan wrote:
>
> --- a/net/ipv4/ipcomp.c
> +++ 1/net/ipv4/ipcomp.c
> @@ -210,7 +210,7 @@ static void ipcomp4_err(struct sk_buff *
> skb->h.icmph->code != ICMP_FRAG_NEEDED)
> return;
>
> - spi = ntohl(ntohs(ipch->cpi));
> + spi = htonl(ntohs(ipch->cpi));
Unfortunately the gcc on i386 generates horrible code for that construct.
So lets bite the bullet and optimise it ourselves.
[IPSEC]: Optimise be16/be32 conversions
Up until now we've used constructs such as ntohl(ntohs(x)) to convert
between be16 and be32 quantities, in particular, the CPI for IPComp.
The compiler dutifully generates code on i386 to perform both operations
rather than optimising them away.
So it's worthwhile to add new primitives to handle these operations
specifically. The following patch addes be16_to_be32 and be32_to_be16
and uses them for IPComp.
This patch was prompted by a sparse clean-up patch from Alexey Dobriyan.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
[-- Attachment #2: ipcomp-spi-cleanup.patch --]
[-- Type: text/plain, Size: 3673 bytes --]
diff --git a/include/linux/byteorder/big_endian.h b/include/linux/byteorder/big_endian.h
index bef8789..7ea1042 100644
--- a/include/linux/byteorder/big_endian.h
+++ b/include/linux/byteorder/big_endian.h
@@ -101,6 +101,9 @@
#define __cpu_to_be16s(x) do {} while (0)
#define __be16_to_cpus(x) do {} while (0)
+#define __be32_to_be16(x) ((__force __be16)(__be32)x)
+#define __be16_to_be32(x) ((__force __be32)(__be16)x)
+
#include <linux/byteorder/generic.h>
#endif /* _LINUX_BYTEORDER_BIG_ENDIAN_H */
diff --git a/include/linux/byteorder/generic.h b/include/linux/byteorder/generic.h
index e86e4a9..0bafb90 100644
--- a/include/linux/byteorder/generic.h
+++ b/include/linux/byteorder/generic.h
@@ -124,6 +124,8 @@
#define be32_to_cpus __be32_to_cpus
#define cpu_to_be16s __cpu_to_be16s
#define be16_to_cpus __be16_to_cpus
+#define be32_to_be16 __be32_to_be16
+#define be16_to_be32 __be16_to_be32
#endif
diff --git a/include/linux/byteorder/little_endian.h b/include/linux/byteorder/little_endian.h
index 86e62b7..ab6abcd 100644
--- a/include/linux/byteorder/little_endian.h
+++ b/include/linux/byteorder/little_endian.h
@@ -101,6 +101,9 @@
#define __cpu_to_be16s(x) __swab16s((x))
#define __be16_to_cpus(x) __swab16s((x))
+#define __be32_to_be16(x) ((__force __be16)((__force __u32)(__be32)x >> 16))
+#define __be16_to_be32(x) ((__force __be32)((__force __u32)(__be16)x << 16))
+
#include <linux/byteorder/generic.h>
#endif /* _LINUX_BYTEORDER_LITTLE_ENDIAN_H */
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index cd810f4..db04339 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -188,7 +188,7 @@
ipch = (struct ip_comp_hdr *)((char *)iph + iph->ihl * 4);
ipch->nexthdr = iph->protocol;
ipch->flags = 0;
- ipch->cpi = htons((u16 )ntohl(x->id.spi));
+ ipch->cpi = be32_to_be16(x->id.spi);
iph->protocol = IPPROTO_COMP;
ip_send_check(iph);
return 0;
@@ -210,7 +210,7 @@
skb->h.icmph->code != ICMP_FRAG_NEEDED)
return;
- spi = ntohl(ntohs(ipch->cpi));
+ spi = be16_to_be32(ipch->cpi);
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr,
spi, IPPROTO_COMP, AF_INET);
if (!x)
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index f285bbf..30b671c 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -219,9 +219,9 @@
case IPPROTO_COMP:
if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
- u16 *ipcomp_hdr = (u16 *)xprth;
+ __be16 *ipcomp_hdr = (__be16 *)xprth;
- fl->fl_ipsec_spi = ntohl(ntohs(ipcomp_hdr[1]));
+ fl->fl_ipsec_spi = be16_to_be32(ipcomp_hdr[1]);
}
break;
default:
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index 05eb67d..6ed793e 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -190,7 +190,7 @@
ipch = (struct ipv6_comp_hdr *)start;
ipch->nexthdr = *skb->nh.raw;
ipch->flags = 0;
- ipch->cpi = htons((u16 )ntohl(x->id.spi));
+ ipch->cpi = be32_to_be16(x->id.spi);
*skb->nh.raw = IPPROTO_COMP;
out_ok:
@@ -208,7 +208,7 @@
if (type != ICMPV6_DEST_UNREACH && type != ICMPV6_PKT_TOOBIG)
return;
- spi = ntohl(ntohs(ipcomph->cpi));
+ spi = be16_to_be32(ipcomph->cpi);
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, IPPROTO_COMP, AF_INET6);
if (!x)
return;
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index b549710..65f208d 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -62,7 +62,7 @@
case IPPROTO_COMP:
if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
return -EINVAL;
- *spi = ntohl(ntohs(*(u16*)(skb->h.raw + 2)));
+ *spi = be16_to_be32(*(__be16 *)(skb->h.raw + 2));
*seq = 0;
return 0;
default:
^ permalink raw reply related
* Re: [PATCH] Gianfar SKB Recycling Support
From: Andi Kleen @ 2006-05-18 10:11 UTC (permalink / raw)
To: Haruki Dai-r35557; +Cc: netdev, Fleming Andy-afleming, Kumar Gala
In-Reply-To: <18AEF66AFDF06F4CAAA1D419D000FD33524B99@az33exm24.fsl.freescale.net>
>
> With grant of the description, it looks negative in the memory management,
> but actually, the amount of memory usage in the driver layer is less than
> the ordinaly gianfar (around half), especially the NAPI is enable. This
> recycling is introduced in order to chop down the critical path memory
> usage.
Explain?
> Forwarding performance goes up 60 to 100% better, and amount of memory
> usage is half.
And what happens when the box doesn't route and isn't under full network
load?
-Andi
^ permalink raw reply
* Netchannel subsystem update.
From: Evgeniy Polyakov @ 2006-05-18 10:34 UTC (permalink / raw)
To: David S. Miller; +Cc: netdev, kelly, rusty
In-Reply-To: <20060516173416.GA22552@2ka.mipt.ru>
This updates brings new features to the following supported:
* unified cache to store netchannels (IPv4 and stub for fied cache
to store netchannels (IPv4 and stub for IPv6 hashes, TCP and UDP)
* skb queueing mechanism
* netchannel creation/removing/reading commands
* netchannel's callback to allocate/free pages (for
example to get data from mapped area) not only from SLAB cache
* netchannel's callback to move/copy data to userspace
Added:
* memory limits (soft limits, since update is not protected).
* blocking reading.
* two types of data reading backends (copy_to_user(), copy to (could be
mapped) area).
Patch against previous release is attached.
Userspace application, design and implementation notes, full patchsets
can be found at project homepage [1].
1. Network channel homepage.
http://tservice.net.ru/~s0mbre/old/?section=projects&item=netchannel
I would like to rise a question about how netchannel object should be
handled by system in general, i.e. should netchannels be associated with
process or they should live by themselfs, i.e. like routes?
My implementation allows netchannels to be setup permanently, so process
can exit and then new one can bind to existing netchannel and read it's
data, but it requires some tricks to create mapping of it's pages into
process' context...
Also if netchannel is created, but no process is associated with it, who
will process protocol state machine?
Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
diff --git a/include/linux/netchannel.h b/include/linux/netchannel.h
index e87a148..7ab2fa0 100644
--- a/include/linux/netchannel.h
+++ b/include/linux/netchannel.h
@@ -32,13 +32,20 @@ enum netchannel_commands {
NETCHANNEL_DUMP,
};
+enum netchannel_type {
+ NETCHANNEL_COPY_USER = 0,
+ NETCHANNEL_MMAP,
+ NETCHANEL_VM_HACK,
+};
+
struct unetchannel
{
__u32 src, dst; /* source/destination hashes */
__u16 sport, dport; /* source/destination ports */
__u8 proto; /* IP protocol number */
- __u8 listen;
- __u8 reserved[2];
+ __u8 type; /* Netchannel type */
+ __u8 memory_limit_order; /* Memor limit order */
+ __u8 reserved;
};
struct unetchannel_control
@@ -46,6 +53,8 @@ struct unetchannel_control
struct unetchannel unc;
__u32 cmd;
__u32 len;
+ __u32 flags;
+ __u32 timeout;
};
#ifdef __KERNEL__
@@ -60,9 +69,14 @@ struct netchannel
struct page * (*nc_alloc_page)(unsigned int size);
void (*nc_free_page)(struct page *page);
- int (*nc_read_data)(struct netchannel *, unsigned int *len, void __user *arg);
+ int (*nc_read_data)(struct netchannel *, unsigned int *timeout, unsigned int *len, void *arg);
+
+ struct sk_buff_head recv_queue;
+ wait_queue_head_t wait;
+
+ unsigned int qlen;
- struct sk_buff_head list;
+ void *priv;
};
struct netchannel_cache_head
@@ -71,5 +85,15 @@ struct netchannel_cache_head
struct mutex mutex;
};
+#define NETCHANNEL_MAX_ORDER 32
+#define NETCHANNEL_MIN_ORDER PAGE_SHIFT
+
+struct netchannel_mmap
+{
+ struct page **page;
+ unsigned int pnum;
+ unsigned int poff;
+};
+
#endif /* __KERNEL__ */
#endif /* __NETCHANNEL_H */
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index accd00b..ba82aa2 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -301,7 +301,6 @@ struct sk_buff {
* Handling routines are only of interest to the kernel
*/
#include <linux/slab.h>
-#include <linux/netchannel.h>
#include <asm/system.h>
@@ -316,10 +315,11 @@ static inline struct sk_buff *alloc_skb(
}
#ifdef CONFIG_NETCHANNEL
+struct unetchannel;
extern struct sk_buff *netchannel_alloc(struct unetchannel *unc, unsigned int header_size,
unsigned int total_size, gfp_t gfp_mask);
#else
-static struct sk_buff *netchannel_alloc(struct unetchannel *unc, unsigned int header_size,
+static struct sk_buff *netchannel_alloc(void *unc, unsigned int header_size,
unsigned int total_size, gfp_t gfp_mask)
{
return NULL;
diff --git a/net/core/netchannel.c b/net/core/netchannel.c
index 169a764..96e5e5b 100644
--- a/net/core/netchannel.c
+++ b/net/core/netchannel.c
@@ -27,6 +27,8 @@
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
+#include <linux/highmem.h>
+#include <linux/netchannel.h>
#include <linux/in.h>
#include <linux/ip.h>
@@ -127,6 +129,7 @@ static void netchannel_free_rcu(struct r
{
struct netchannel *nc = container_of(rcu, struct netchannel, rcu_head);
+ netchannel_cleanup(nc);
kmem_cache_free(netchannel_cache, nc);
}
@@ -151,8 +154,10 @@ static inline void netchannel_dump_info_
dport = ntohs(unc->dport);
sport = ntohs(unc->sport);
- printk(KERN_INFO "netchannel: %s %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u, proto: %u, hit: %lu, err: %d.\n",
- prefix, NIPQUAD(src), sport, NIPQUAD(dst), dport, unc->proto, hit, err);
+ printk(KERN_NOTICE "netchannel: %s %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u, "
+ "proto: %u, type: %u, order: %u, hit: %lu, err: %d.\n",
+ prefix, NIPQUAD(src), sport, NIPQUAD(dst), dport,
+ unc->proto, unc->type, unc->memory_limit_order, hit, err);
}
static int netchannel_convert_skb_ipv6(struct sk_buff *skb, struct unetchannel *unc)
@@ -197,7 +202,7 @@ static int netchannel_convert_skb_ipv4(s
len = skb->len;
- skb->h.raw = skb->nh.iph + iph->ihl*4;
+ skb->h.raw = skb->nh.raw + iph->ihl*4;
switch (unc->proto) {
case IPPROTO_TCP:
@@ -352,35 +357,91 @@ int netchannel_recv(struct sk_buff *skb)
nc->hit++;
- skb_queue_tail(&nc->list, skb);
+ if (nc->qlen + skb->len > (1 << nc->unc.memory_limit_order)) {
+ kfree_skb(skb);
+ err = 0;
+ goto unlock;
+ }
+
+ skb_queue_tail(&nc->recv_queue, skb);
+ nc->qlen += skb->len;
unlock:
rcu_read_unlock();
return err;
}
+static int netchannel_wait_for_packet(struct netchannel *nc, long *timeo_p)
+{
+ int error = 0;
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait_exclusive(&nc->wait, &wait, TASK_INTERRUPTIBLE);
+
+ if (skb_queue_empty(&nc->recv_queue)) {
+ if (signal_pending(current))
+ goto interrupted;
+
+ *timeo_p = schedule_timeout(*timeo_p);
+ }
+out:
+ finish_wait(&nc->wait, &wait);
+ return error;
+interrupted:
+ error = (*timeo_p == MAX_SCHEDULE_TIMEOUT) ? -ERESTARTSYS : -EINTR;
+ goto out;
+}
+
+static struct sk_buff *netchannel_get_skb(struct netchannel *nc, unsigned int *timeout, int *error)
+{
+ struct sk_buff *skb = NULL;
+ long tm = *timeout;
+
+ *error = 0;
+
+ while (1) {
+ skb = skb_dequeue(&nc->recv_queue);
+ if (skb)
+ break;
+
+ if (*timeout) {
+ *error = netchannel_wait_for_packet(nc, &tm);
+ if (*error) {
+ *timeout = tm;
+ break;
+ }
+ tm = *timeout;
+ } else {
+ *error = -EAGAIN;
+ break;
+ }
+ }
+
+ return skb;
+}
+
/*
* Actually it should be something like recvmsg().
*/
-static int netchannel_copy_to_user(struct netchannel *nc, unsigned int *len, void __user *arg)
+static int netchannel_copy_to_user(struct netchannel *nc, unsigned int *timeout, unsigned int *len, void *arg)
{
unsigned int copied;
struct sk_buff *skb;
struct iovec to;
- int err = -EINVAL;
-
- to.iov_base = arg;
- to.iov_len = *len;
+ int err;
- skb = skb_dequeue(&nc->list);
+ skb = netchannel_get_skb(nc, timeout, &err);
if (!skb)
- return -EAGAIN;
+ return err;
+
+ to.iov_base = arg;
+ to.iov_len = *len;
copied = skb->len;
if (copied > *len)
copied = *len;
-
- if (skb->ip_summed==CHECKSUM_UNNECESSARY) {
+
+ if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
err = skb_copy_datagram_iovec(skb, 0, &to, copied);
} else {
err = skb_copy_and_csum_datagram_iovec(skb,0, &to);
@@ -388,56 +449,290 @@ static int netchannel_copy_to_user(struc
*len = (err == 0)?copied:0;
+ nc->qlen -= skb->len;
kfree_skb(skb);
return err;
}
-static int netchannel_create(struct unetchannel *unc)
+int netchannel_skb_copy_datagram(const struct sk_buff *skb, int offset,
+ void *to, int len)
{
- struct netchannel *nc;
- int err = -ENOMEM;
- struct netchannel_cache_head *bucket;
+ int start = skb_headlen(skb);
+ int i, copy = start - offset;
+
+ /* Copy header. */
+ if (copy > 0) {
+ if (copy > len)
+ copy = len;
+ memcpy(to, skb->data + offset, copy);
+
+ if ((len -= copy) == 0)
+ return 0;
+ offset += copy;
+ to += copy;
+ }
+
+ /* Copy paged appendix. Hmm... why does this look so complicated? */
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ int end;
+
+ BUG_TRAP(start <= offset + len);
+
+ end = start + skb_shinfo(skb)->frags[i].size;
+ if ((copy = end - offset) > 0) {
+ u8 *vaddr;
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ struct page *page = frag->page;
+
+ if (copy > len)
+ copy = len;
+ vaddr = kmap(page);
+ memcpy(to, vaddr + frag->page_offset +
+ offset - start, copy);
+ kunmap(page);
+ if (!(len -= copy))
+ return 0;
+ offset += copy;
+ to += copy;
+ }
+ start = end;
+ }
+
+ if (skb_shinfo(skb)->frag_list) {
+ struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+ for (; list; list = list->next) {
+ int end;
+
+ BUG_TRAP(start <= offset + len);
+
+ end = start + list->len;
+ if ((copy = end - offset) > 0) {
+ if (copy > len)
+ copy = len;
+ if (netchannel_skb_copy_datagram(list,
+ offset - start,
+ to, copy))
+ goto fault;
+ if ((len -= copy) == 0)
+ return 0;
+ offset += copy;
+ to += copy;
+ }
+ start = end;
+ }
+ }
+ if (!len)
+ return 0;
+
+fault:
+ return -EFAULT;
+}
+
+static int netchannel_copy_to_mem(struct netchannel *nc, unsigned int *timeout, unsigned int *len, void *arg)
+{
+ struct netchannel_mmap *m = nc->priv;
+ unsigned int copied, skb_offset = 0;
+ struct sk_buff *skb;
+ int err;
+
+ skb = netchannel_get_skb(nc, timeout, &err);
+ if (!skb)
+ return err;
+
+ copied = skb->len;
+
+ while (copied) {
+ int pnum = ((m->poff % PAGE_SIZE) % m->pnum);
+ struct page *page = m->page[pnum];
+ void *page_map, *ptr;
+ unsigned int sz, left;
+
+ left = PAGE_SIZE - (m->poff % (PAGE_SIZE - 1));
+ sz = min_t(unsigned int, left, copied);
+
+ if (!sz) {
+ err = -ENOSPC;
+ goto err_out;
+ }
+
+ page_map = kmap_atomic(page, KM_USER0);
+ if (!page_map) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ ptr = page_map + (m->poff % (PAGE_SIZE - 1));
+
+ err = netchannel_skb_copy_datagram(skb, skb_offset, ptr, sz);
+ if (err) {
+ kunmap_atomic(page_map, KM_USER0);
+ goto err_out;
+ }
+ kunmap_atomic(page_map, KM_USER0);
+
+ copied -= sz;
+ m->poff += sz;
+ skb_offset += sz;
+#if 1
+ if (m->poff >= PAGE_SIZE * m->pnum) {
+ //netchannel_dump_info_unc(&nc->unc, "rewind", nc->hit, 0);
+ m->poff = 0;
+ }
+#endif
+ }
+ *len = skb->len;
+
+ err = 0;
+
+err_out:
+ nc->qlen -= skb->len;
+ kfree_skb(skb);
+
+ return err;
+}
+
+static int netchannel_mmap_setup(struct netchannel *nc)
+{
+ struct netchannel_mmap *m;
+ unsigned int i, pnum;
+
+ pnum = (1 << (nc->unc.memory_limit_order - NETCHANNEL_MIN_ORDER));
+
+ m = kzalloc(sizeof(struct netchannel_mmap) + sizeof(struct page *) * pnum, GFP_KERNEL);
+ if (!m)
+ return -ENOMEM;
+
+ m->page = (struct page **)(m + 1);
+ m->pnum = pnum;
+
+ for (i=0; i<pnum; ++i) {
+ m->page[i] = alloc_page(GFP_KERNEL);
+ if (!m->page[i])
+ break;
+ }
+
+ if (i < pnum) {
+ pnum = i;
+ goto err_out_free;
+ }
+
+ nc->priv = m;
+ nc->nc_read_data = &netchannel_copy_to_mem;
+
+ return 0;
+
+err_out_free:
+ for (i=0; i<pnum; ++i)
+ __free_page(m->page[i]);
+
+ kfree(m);
+
+ return -ENOMEM;
- if (!netchannel_hash_table)
- return -ENODEV;
+}
- bucket = netchannel_bucket(unc);
+static void netchannel_mmap_cleanup(struct netchannel *nc)
+{
+ unsigned int i;
+ struct netchannel_mmap *m = nc->priv;
- mutex_lock(&bucket->mutex);
+ for (i=0; i<m->pnum; ++i)
+ __free_page(m->page[i]);
- if (netchannel_check_full(unc, bucket)) {
- err = -EEXIST;
- goto out_unlock;
+ kfree(m);
+}
+
+static void netchannel_cleanup(struct netchannel *nc)
+{
+ switch (nc->unc.type) {
+ case NETCHANNEL_COPY_USER:
+ break;
+ case NETCHANNEL_MMAP:
+ netchannel_mmap_cleanup(nc);
+ break;
+ default:
+ break;
}
+}
- if (unc->listen && netchannel_check_dest(unc, bucket)) {
- err = -EEXIST;
- goto out_unlock;
+static int netchannel_setup(struct netchannel *nc)
+{
+ int ret = 0;
+
+ if (nc->unc.memory_limit_order > NETCHANNEL_MAX_ORDER)
+ return -E2BIG;
+
+ if (nc->unc.memory_limit_order < NETCHANNEL_MIN_ORDER)
+ nc->unc.memory_limit_order = NETCHANNEL_MIN_ORDER;
+
+ switch (nc->unc.type) {
+ case NETCHANNEL_COPY_USER:
+ nc->nc_read_data = &netchannel_copy_to_user;
+ break;
+ case NETCHANNEL_MMAP:
+ ret = netchannel_mmap_setup(nc);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
}
+ return ret;
+}
+
+static int netchannel_create(struct unetchannel *unc)
+{
+ struct netchannel *nc;
+ int err = -ENOMEM;
+ struct netchannel_cache_head *bucket;
+
+ if (!netchannel_hash_table)
+ return -ENODEV;
+
nc = kmem_cache_alloc(netchannel_cache, GFP_KERNEL);
if (!nc)
- goto out_exit;
+ return -ENOMEM;
memset(nc, 0, sizeof(struct netchannel));
nc->hit = 0;
- skb_queue_head_init(&nc->list);
+ skb_queue_head_init(&nc->recv_queue);
+ init_waitqueue_head(&nc->wait);
atomic_set(&nc->refcnt, 1);
memcpy(&nc->unc, unc, sizeof(struct unetchannel));
- nc->nc_read_data = &netchannel_copy_to_user;
+ err = netchannel_setup(nc);
+ if (err)
+ goto err_out_free;
+
+ bucket = netchannel_bucket(unc);
+
+ mutex_lock(&bucket->mutex);
+
+ if (netchannel_check_full(unc, bucket)) {
+ err = -EEXIST;
+ goto err_out_unlock;
+ }
hlist_add_head_rcu(&nc->node, &bucket->head);
err = 0;
-out_unlock:
mutex_unlock(&bucket->mutex);
-out_exit:
+
netchannel_dump_info_unc(unc, "create", 0, err);
return err;
+
+err_out_unlock:
+ mutex_unlock(&bucket->mutex);
+
+ netchannel_cleanup(nc);
+
+err_out_free:
+ kmem_cache_free(netchannel_cache, nc);
+
+ return err;
}
static int netchannel_remove(struct unetchannel *unc)
@@ -488,11 +783,17 @@ static int netchannel_recv_data(struct u
nc = netchannel_check_dest(&ctl->unc, bucket);
if (!nc)
- goto out_unlock;
+ goto err_out_unlock;
+
+ netchannel_get(nc);
+ mutex_unlock(&bucket->mutex);
- ret = nc->nc_read_data(nc, &ctl->len, data);
+ ret = nc->nc_read_data(nc, &ctl->timeout, &ctl->len, data);
+
+ netchannel_put(nc);
+ return ret;
-out_unlock:
+err_out_unlock:
mutex_unlock(&bucket->mutex);
return ret;
}
--
Evgeniy Polyakov
^ permalink raw reply related
* [PATCH] Fix "ntohl(ntohs" bugs
From: Alexey Dobriyan @ 2006-05-18 9:37 UTC (permalink / raw)
To: netdev
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
---
net/ipv4/ipcomp.c | 2 +-
net/ipv4/xfrm4_policy.c | 2 +-
net/ipv6/ipcomp6.c | 2 +-
net/xfrm/xfrm_input.c | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
--- a/net/ipv4/ipcomp.c
+++ 1/net/ipv4/ipcomp.c
@@ -210,7 +210,7 @@ static void ipcomp4_err(struct sk_buff *
skb->h.icmph->code != ICMP_FRAG_NEEDED)
return;
- spi = ntohl(ntohs(ipch->cpi));
+ spi = htonl(ntohs(ipch->cpi));
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr,
spi, IPPROTO_COMP, AF_INET);
if (!x)
--- a/net/ipv4/xfrm4_policy.c
+++ 1/net/ipv4/xfrm4_policy.c
@@ -221,7 +221,7 @@ _decode_session4(struct sk_buff *skb, st
if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
u16 *ipcomp_hdr = (u16 *)xprth;
- fl->fl_ipsec_spi = ntohl(ntohs(ipcomp_hdr[1]));
+ fl->fl_ipsec_spi = htonl(ntohs(ipcomp_hdr[1]));
}
break;
default:
--- a/net/ipv6/ipcomp6.c
+++ 1/net/ipv6/ipcomp6.c
@@ -208,7 +208,7 @@ static void ipcomp6_err(struct sk_buff *
if (type != ICMPV6_DEST_UNREACH && type != ICMPV6_PKT_TOOBIG)
return;
- spi = ntohl(ntohs(ipcomph->cpi));
+ spi = htonl(ntohs(ipcomph->cpi));
x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, IPPROTO_COMP, AF_INET6);
if (!x)
return;
--- a/net/xfrm/xfrm_input.c
+++ 1/net/xfrm/xfrm_input.c
@@ -62,7 +62,7 @@ int xfrm_parse_spi(struct sk_buff *skb,
case IPPROTO_COMP:
if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
return -EINVAL;
- *spi = ntohl(ntohs(*(u16*)(skb->h.raw + 2)));
+ *spi = htonl(ntohs(*(u16*)(skb->h.raw + 2)));
*seq = 0;
return 0;
default:
^ permalink raw reply
* [PATCH 07/07] secmark: Add new packet controls to SELinux
From: James Morris @ 2006-05-18 9:29 UTC (permalink / raw)
To: David S. Miller, Andrew Morton
Cc: Patrick McHardy, Stephen Smalley, netdev, Karl MacMillan
In-Reply-To: <Pine.LNX.4.64.0605180458130.569@d.namei>
This patch adds new per-packet access controls to SELinux, replacing the
old packet controls.
Packets are labeled with the iptables SECMARK and CONNSECMARK targets,
then security policy for the packets is enforced with these controls.
To allow for a smooth transition to the new controls, the old code is
still present, but not active by default. To restore previous behavior,
the old controls may be activated at runtime by writing a '1' to
/selinux/compat_net, and also via the kernel boot parameter
selinux_compat_net. Switching between the network control models requires
the security load_policy permission. The old controls will probably
eventually be removed and any continued use is discouraged.
Please apply.
Signed-off-by: James Morris <jmorris@namei.org>
---
Documentation/kernel-parameters.txt | 9 +
security/selinux/Kconfig | 2
security/selinux/hooks.c | 241 +++++++++++++++++++-----------------
security/selinux/include/xfrm.h | 2
security/selinux/selinuxfs.c | 59 ++++++++
security/selinux/xfrm.c | 12 -
6 files changed, 205 insertions(+), 120 deletions(-)
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/Documentation/kernel-parameters.txt linux-2.6.17-rc4-mm1.w/Documentation/kernel-parameters.txt
--- linux-2.6.17-rc4-mm1.p/Documentation/kernel-parameters.txt 2006-05-18 03:07:47.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/Documentation/kernel-parameters.txt 2006-05-17 23:45:13.000000000 -0400
@@ -1414,6 +1414,15 @@ running once the system is up.
If enabled at boot time, /selinux/disable can be used
later to disable prior to initial policy load.
+ selinux_compat_net =
+ [SELINUX] Set initial selinux_compat_net flag value.
+ Format: { "0" | "1" }
+ 0 -- use new secmark-based packet controls
+ 1 -- use legacy packet controls
+ Default value is 0 (preferred).
+ Value can be changed at runtime via
+ /selinux/compat_net.
+
serialnumber [BUGS=IA-32]
sg_def_reserved_size= [SCSI]
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/security/selinux/hooks.c linux-2.6.17-rc4-mm1.w/security/selinux/hooks.c
--- linux-2.6.17-rc4-mm1.p/security/selinux/hooks.c 2006-05-18 03:07:47.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/security/selinux/hooks.c 2006-05-17 21:01:07.000000000 -0400
@@ -80,6 +80,7 @@
extern unsigned int policydb_loaded_version;
extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
+extern int selinux_compat_net;
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
int selinux_enforcing = 0;
@@ -3216,47 +3217,16 @@ static int selinux_socket_unix_may_send(
return 0;
}
-static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, struct avc_audit_data *ad,
+ u32 sock_sid, u16 sock_class, u16 family, char *addrp, int len)
{
- u16 family;
- char *addrp;
- int len, err = 0;
+ int err = 0;
u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0;
- u32 sock_sid = 0;
- u16 sock_class = 0;
- struct socket *sock;
- struct net_device *dev;
- struct avc_audit_data ad;
- family = sk->sk_family;
- if (family != PF_INET && family != PF_INET6)
+ if (!skb->dev)
goto out;
- /* Handle mapped IPv4 packets arriving via IPv6 sockets */
- if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
- family = PF_INET;
-
- read_lock_bh(&sk->sk_callback_lock);
- sock = sk->sk_socket;
- if (sock) {
- struct inode *inode;
- inode = SOCK_INODE(sock);
- if (inode) {
- struct inode_security_struct *isec;
- isec = inode->i_security;
- sock_sid = isec->sid;
- sock_class = isec->sclass;
- }
- }
- read_unlock_bh(&sk->sk_callback_lock);
- if (!sock_sid)
- goto out;
-
- dev = skb->dev;
- if (!dev)
- goto out;
-
- err = sel_netif_sids(dev, &if_sid, NULL);
+ err = sel_netif_sids(skb->dev, &if_sid, NULL);
if (err)
goto out;
@@ -3279,44 +3249,88 @@ static int selinux_socket_sock_rcv_skb(s
break;
}
- AVC_AUDIT_DATA_INIT(&ad, NET);
- ad.u.net.netif = dev->name;
- ad.u.net.family = family;
-
- err = selinux_parse_skb(skb, &ad, &addrp, &len, 1);
- if (err)
- goto out;
-
- err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad);
+ err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
if (err)
goto out;
- /* Fixme: this lookup is inefficient */
err = security_node_sid(family, addrp, len, &node_sid);
if (err)
goto out;
- err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, &ad);
+ err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, ad);
if (err)
goto out;
if (recv_perm) {
u32 port_sid;
- /* Fixme: make this more efficient */
err = security_port_sid(sk->sk_family, sk->sk_type,
- sk->sk_protocol, ntohs(ad.u.net.sport),
+ sk->sk_protocol, ntohs(ad->u.net.sport),
&port_sid);
if (err)
goto out;
err = avc_has_perm(sock_sid, port_sid,
- sock_class, recv_perm, &ad);
+ sock_class, recv_perm, ad);
}
- if (!err)
- err = selinux_xfrm_sock_rcv_skb(sock_sid, skb);
+out:
+ return err;
+}
+
+static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ u16 family;
+ u16 sock_class = 0;
+ char *addrp;
+ int len, err = 0;
+ u32 sock_sid = 0;
+ struct socket *sock;
+ struct avc_audit_data ad;
+
+ family = sk->sk_family;
+ if (family != PF_INET && family != PF_INET6)
+ goto out;
+
+ /* Handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP))
+ family = PF_INET;
+
+ read_lock_bh(&sk->sk_callback_lock);
+ sock = sk->sk_socket;
+ if (sock) {
+ struct inode *inode;
+ inode = SOCK_INODE(sock);
+ if (inode) {
+ struct inode_security_struct *isec;
+ isec = inode->i_security;
+ sock_sid = isec->sid;
+ sock_class = isec->sclass;
+ }
+ }
+ read_unlock_bh(&sk->sk_callback_lock);
+ if (!sock_sid)
+ goto out;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]";
+ ad.u.net.family = family;
+
+ err = selinux_parse_skb(skb, &ad, &addrp, &len, 1);
+ if (err)
+ goto out;
+
+ if (selinux_compat_net)
+ err = selinux_sock_rcv_skb_compat(sk, skb, &ad, sock_sid,
+ sock_class, family,
+ addrp, len);
+ else
+ err = avc_has_perm(sock_sid, skb->secmark, SECCLASS_PACKET,
+ PACKET__RECV, &ad);
+ if (err)
+ goto out;
+ err = selinux_xfrm_sock_rcv_skb(sock_sid, skb);
out:
return err;
}
@@ -3456,42 +3470,18 @@ out:
#ifdef CONFIG_NETFILTER
-static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
- struct sk_buff **pskb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *),
- u16 family)
+static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev,
+ struct inode_security_struct *isec,
+ struct avc_audit_data *ad,
+ u16 family, char *addrp, int len)
{
- char *addrp;
- int len, err = NF_ACCEPT;
+ int err;
u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
- struct sock *sk;
- struct socket *sock;
- struct inode *inode;
- struct sk_buff *skb = *pskb;
- struct inode_security_struct *isec;
- struct avc_audit_data ad;
- struct net_device *dev = (struct net_device *)out;
- sk = skb->sk;
- if (!sk)
- goto out;
-
- sock = sk->sk_socket;
- if (!sock)
- goto out;
-
- inode = SOCK_INODE(sock);
- if (!inode)
- goto out;
-
err = sel_netif_sids(dev, &if_sid, NULL);
if (err)
goto out;
- isec = inode->i_security;
-
switch (isec->sclass) {
case SECCLASS_UDP_SOCKET:
netif_perm = NETIF__UDP_SEND;
@@ -3511,55 +3501,88 @@ static unsigned int selinux_ip_postroute
break;
}
-
- AVC_AUDIT_DATA_INIT(&ad, NET);
- ad.u.net.netif = dev->name;
- ad.u.net.family = family;
-
- err = selinux_parse_skb(skb, &ad, &addrp,
- &len, 0) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
- goto out;
-
- err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF,
- netif_perm, &ad) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
+ err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
+ if (err)
goto out;
- /* Fixme: this lookup is inefficient */
- err = security_node_sid(family, addrp, len,
- &node_sid) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
+ err = security_node_sid(family, addrp, len, &node_sid);
+ if (err)
goto out;
- err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE,
- node_perm, &ad) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
+ err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, ad);
+ if (err)
goto out;
if (send_perm) {
u32 port_sid;
- /* Fixme: make this more efficient */
err = security_port_sid(sk->sk_family,
sk->sk_type,
sk->sk_protocol,
- ntohs(ad.u.net.dport),
- &port_sid) ? NF_DROP : NF_ACCEPT;
- if (err != NF_ACCEPT)
+ ntohs(ad->u.net.dport),
+ &port_sid);
+ if (err)
goto out;
err = avc_has_perm(isec->sid, port_sid, isec->sclass,
- send_perm, &ad) ? NF_DROP : NF_ACCEPT;
+ send_perm, ad);
}
+out:
+ return err;
+}
+
+static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *),
+ u16 family)
+{
+ char *addrp;
+ int len, err = 0;
+ struct sock *sk;
+ struct socket *sock;
+ struct inode *inode;
+ struct sk_buff *skb = *pskb;
+ struct inode_security_struct *isec;
+ struct avc_audit_data ad;
+ struct net_device *dev = (struct net_device *)out;
+
+ sk = skb->sk;
+ if (!sk)
+ goto out;
+
+ sock = sk->sk_socket;
+ if (!sock)
+ goto out;
+
+ inode = SOCK_INODE(sock);
+ if (!inode)
+ goto out;
+
+ isec = inode->i_security;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = dev->name;
+ ad.u.net.family = family;
- if (err != NF_ACCEPT)
+ err = selinux_parse_skb(skb, &ad, &addrp, &len, 0);
+ if (err)
goto out;
- err = selinux_xfrm_postroute_last(isec->sid, skb);
+ if (selinux_compat_net)
+ err = selinux_ip_postroute_last_compat(sk, dev, isec, &ad,
+ family, addrp, len);
+ else
+ err = avc_has_perm(isec->sid, skb->secmark, SECCLASS_PACKET,
+ PACKET__SEND, &ad);
+
+ if (err)
+ goto out;
+ err = selinux_xfrm_postroute_last(isec->sid, skb);
out:
- return err;
+ return err ? NF_DROP : NF_ACCEPT;
}
static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum,
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/security/selinux/include/xfrm.h linux-2.6.17-rc4-mm1.w/security/selinux/include/xfrm.h
--- linux-2.6.17-rc4-mm1.p/security/selinux/include/xfrm.h 2006-05-18 03:07:47.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/security/selinux/include/xfrm.h 2006-05-17 21:01:07.000000000 -0400
@@ -49,7 +49,7 @@ static inline int selinux_xfrm_sock_rcv_
static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
{
- return NF_ACCEPT;
+ return 0;
}
static inline int selinux_socket_getpeer_stream(struct sock *sk)
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/security/selinux/Kconfig linux-2.6.17-rc4-mm1.w/security/selinux/Kconfig
--- linux-2.6.17-rc4-mm1.p/security/selinux/Kconfig 2006-03-20 00:53:29.000000000 -0500
+++ linux-2.6.17-rc4-mm1.w/security/selinux/Kconfig 2006-05-18 03:10:02.000000000 -0400
@@ -1,6 +1,6 @@
config SECURITY_SELINUX
bool "NSA SELinux Support"
- depends on SECURITY_NETWORK && AUDIT && NET && INET
+ depends on SECURITY_NETWORK && AUDIT && NET && INET && NETWORK_SECMARK
default n
help
This selects NSA Security-Enhanced Linux (SELinux).
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/security/selinux/selinuxfs.c linux-2.6.17-rc4-mm1.w/security/selinux/selinuxfs.c
--- linux-2.6.17-rc4-mm1.p/security/selinux/selinuxfs.c 2006-05-18 03:07:47.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/security/selinux/selinuxfs.c 2006-05-17 21:01:07.000000000 -0400
@@ -37,6 +37,7 @@
#include "conditional.h"
unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
+int selinux_compat_net;
static int __init checkreqprot_setup(char *str)
{
@@ -45,6 +46,13 @@ static int __init checkreqprot_setup(cha
}
__setup("checkreqprot=", checkreqprot_setup);
+static int __init selinux_compat_net_setup(char *str)
+{
+ selinux_compat_net = simple_strtoul(str,NULL,0) ? 1 : 0;
+ return 1;
+}
+__setup("selinux_compat_net=", selinux_compat_net_setup);
+
static DEFINE_MUTEX(sel_mutex);
@@ -85,6 +93,7 @@ enum sel_inos {
SEL_AVC, /* AVC management directory */
SEL_MEMBER, /* compute polyinstantiation membership decision */
SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */
+ SEL_COMPAT_NET, /* whether to use old compat network packet controls */
};
#define TMPBUFLEN 12
@@ -364,6 +373,55 @@ static struct file_operations sel_checkr
.write = sel_write_checkreqprot,
};
+static ssize_t sel_read_compat_net(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_compat_net);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static ssize_t sel_write_compat_net(struct file * file, const char __user * buf,
+ size_t count, loff_t *ppos)
+{
+ char *page;
+ ssize_t length;
+ int new_value;
+
+ length = task_has_security(current, SECURITY__LOAD_POLICY);
+ if (length)
+ return length;
+
+ if (count >= PAGE_SIZE)
+ return -ENOMEM;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ return -EINVAL;
+ }
+ page = (char*)get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ length = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%d", &new_value) != 1)
+ goto out;
+
+ selinux_compat_net = new_value ? 1 : 0;
+ length = count;
+out:
+ free_page((unsigned long) page);
+ return length;
+}
+static struct file_operations sel_compat_net_ops = {
+ .read = sel_read_compat_net,
+ .write = sel_write_compat_net,
+};
+
/*
* Remaining nodes use transaction based IO methods like nfsd/nfsctl.c
*/
@@ -1219,6 +1277,7 @@ static int sel_fill_super(struct super_b
[SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR},
[SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO},
[SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
+ [SEL_COMPAT_NET] = {"compat_net", &sel_compat_net_ops, S_IRUGO|S_IWUSR},
/* last one */ {""}
};
ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/security/selinux/xfrm.c linux-2.6.17-rc4-mm1.w/security/selinux/xfrm.c
--- linux-2.6.17-rc4-mm1.p/security/selinux/xfrm.c 2006-05-18 03:07:47.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/security/selinux/xfrm.c 2006-05-17 21:01:07.000000000 -0400
@@ -356,18 +356,12 @@ int selinux_xfrm_postroute_last(u32 isec
struct xfrm_state *x = dst_test->xfrm;
if (x && selinux_authorizable_xfrm(x))
- goto accept;
+ goto out;
}
}
rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
ASSOCIATION__SENDTO, NULL);
- if (rc)
- goto drop;
-
-accept:
- return NF_ACCEPT;
-
-drop:
- return NF_DROP;
+out:
+ return rc;
}
^ permalink raw reply
* [PATCH 06/07] secmark: Add CONNSECMARK xtables target
From: James Morris @ 2006-05-18 9:28 UTC (permalink / raw)
To: David S. Miller, Andrew Morton
Cc: Patrick McHardy, Stephen Smalley, netdev, Karl MacMillan
In-Reply-To: <Pine.LNX.4.64.0605180458130.569@d.namei>
This patch adds a new xtables target, CONNSECMARK, which is used to
specify rules for copying security marks from packets to connections, and
for copyying security marks back from connections to packets. This is
similar to the CONNMARK target, but is more limited in scope in that it
only allows copying of security marks to and from packets, as this is all
it needs to do.
A typical scenario would be to apply a security mark to a 'new' packet
with SECMARK, then copy that to its conntrack via CONNMARK, and then
restore the security mark from the connection to established and related
packets on that connection.
Please apply.
Signed-off-by: James Morris <jmorris@namei.org>
---
include/linux/netfilter/xt_CONNSECMARK.h | 13 ++
net/netfilter/Kconfig | 11 ++
net/netfilter/Makefile | 1
net/netfilter/xt_CONNSECMARK.c | 155 +++++++++++++++++++++++++++++++
4 files changed, 180 insertions(+)
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/include/linux/netfilter/xt_CONNSECMARK.h linux-2.6.17-rc4-mm1.w/include/linux/netfilter/xt_CONNSECMARK.h
--- linux-2.6.17-rc4-mm1.p/include/linux/netfilter/xt_CONNSECMARK.h 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.17-rc4-mm1.w/include/linux/netfilter/xt_CONNSECMARK.h 2006-05-17 19:54:52.000000000 -0400
@@ -0,0 +1,13 @@
+#ifndef _XT_CONNSECMARK_H_target
+#define _XT_CONNSECMARK_H_target
+
+enum {
+ CONNSECMARK_SAVE = 1,
+ CONNSECMARK_RESTORE,
+};
+
+struct xt_connsecmark_target_info {
+ u_int8_t mode;
+};
+
+#endif /*_XT_CONNSECMARK_H_target */
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/netfilter/Kconfig linux-2.6.17-rc4-mm1.w/net/netfilter/Kconfig
--- linux-2.6.17-rc4-mm1.p/net/netfilter/Kconfig 2006-05-17 19:54:43.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/netfilter/Kconfig 2006-05-17 19:54:52.000000000 -0400
@@ -195,6 +195,17 @@ config NETFILTER_XT_TARGET_SECMARK
To compile it as a module, choose M here. If unsure, say N.
+config NETFILTER_XT_TARGET_CONNSECMARK
+ tristate '"CONNSECMARK" target support'
+ depends on NETFILTER_XTABLES && (NF_CONNTRACK_SECMARK || IP_NF_CONNTRACK_SECMARK)
+ help
+ The CONNSECMARK target copies security markings from packets
+ to connections, and restores security markings from connections
+ to packets (if the packets are not already marked). This would
+ normally be used in conjunction with the SECMARK target.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
config NETFILTER_XT_MATCH_COMMENT
tristate '"comment" match support'
depends on NETFILTER_XTABLES
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/netfilter/Makefile linux-2.6.17-rc4-mm1.w/net/netfilter/Makefile
--- linux-2.6.17-rc4-mm1.p/net/netfilter/Makefile 2006-05-17 19:53:39.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/netfilter/Makefile 2006-05-17 19:54:52.000000000 -0400
@@ -29,6 +29,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) +
obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o
obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o
obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o
+obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
# matches
obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/netfilter/xt_CONNSECMARK.c linux-2.6.17-rc4-mm1.w/net/netfilter/xt_CONNSECMARK.c
--- linux-2.6.17-rc4-mm1.p/net/netfilter/xt_CONNSECMARK.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.17-rc4-mm1.w/net/netfilter/xt_CONNSECMARK.c 2006-05-17 19:55:25.000000000 -0400
@@ -0,0 +1,155 @@
+/*
+ * This module is used to copy security markings from packets
+ * to connections, and restore security markings from connections
+ * back to packets. This would normally be performed in conjunction
+ * with the SECMARK target and state match.
+ *
+ * Based somewhat on CONNMARK:
+ * Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_CONNSECMARK.h>
+#include <net/netfilter/nf_conntrack_compat.h>
+
+#define PFX "CONNSECMARK: "
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("James Morris <jmorris@redhat.com>");
+MODULE_DESCRIPTION("ip[6]tables CONNSECMARK module");
+MODULE_ALIAS("ipt_CONNSECMARK");
+MODULE_ALIAS("ip6t_CONNSECMARK");
+
+/*
+ * If the packet has a security mark and the connection does not, copy
+ * the security mark from the packet to the connection.
+ */
+static void secmark_save(struct sk_buff *skb)
+{
+ if (skb->secmark) {
+ u32 *connsecmark;
+ enum ip_conntrack_info ctinfo;
+
+ connsecmark = nf_ct_get_secmark(skb, &ctinfo);
+ if (connsecmark && !*connsecmark)
+ if (*connsecmark != skb->secmark)
+ *connsecmark = skb->secmark;
+ }
+}
+
+/*
+ * If packet has no security mark, and the connection does, restore the
+ * security mark from the connection to the packet.
+ */
+static void secmark_restore(struct sk_buff *skb)
+{
+ if (!skb->secmark) {
+ u32 *connsecmark;
+ enum ip_conntrack_info ctinfo;
+
+ connsecmark = nf_ct_get_secmark(skb, &ctinfo);
+ if (connsecmark && *connsecmark)
+ if (skb->secmark != *connsecmark)
+ skb->secmark = *connsecmark;
+ }
+}
+
+static unsigned int target(struct sk_buff **pskb, const struct net_device *in,
+ const struct net_device *out, unsigned int hooknum,
+ const struct xt_target *target,
+ const void *targinfo, void *userinfo)
+{
+ struct sk_buff *skb = *pskb;
+ const struct xt_connsecmark_target_info *info = targinfo;
+
+ switch (info->mode) {
+ case CONNSECMARK_SAVE:
+ secmark_save(skb);
+ break;
+
+ case CONNSECMARK_RESTORE:
+ secmark_restore(skb);
+ break;
+
+ default:
+ BUG();
+ }
+
+ return XT_CONTINUE;
+}
+
+static int checkentry(const char *tablename, const void *entry,
+ const struct xt_target *target, void *targinfo,
+ unsigned int targinfosize, unsigned int hook_mask)
+{
+ struct xt_connsecmark_target_info *info = targinfo;
+
+ switch (info->mode) {
+ case CONNSECMARK_SAVE:
+ case CONNSECMARK_RESTORE:
+ break;
+
+ default:
+ printk(KERN_INFO PFX "invalid mode: %hu\n", info->mode);
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct xt_target ipt_connsecmark_reg = {
+ .name = "CONNSECMARK",
+ .target = target,
+ .targetsize = sizeof(struct xt_connsecmark_target_info),
+ .table = "mangle",
+ .checkentry = checkentry,
+ .me = THIS_MODULE,
+ .family = AF_INET,
+ .revision = 0,
+};
+
+static struct xt_target ip6t_connsecmark_reg = {
+ .name = "CONNSECMARK",
+ .target = target,
+ .targetsize = sizeof(struct xt_connsecmark_target_info),
+ .table = "mangle",
+ .checkentry = checkentry,
+ .me = THIS_MODULE,
+ .family = AF_INET6,
+ .revision = 0,
+};
+
+static int __init xt_connsecmark_init(void)
+{
+ int err;
+
+ need_conntrack();
+
+ err = xt_register_target(&ipt_connsecmark_reg);
+ if (err)
+ return err;
+
+ err = xt_register_target(&ip6t_connsecmark_reg);
+ if (err)
+ xt_unregister_target(&ipt_connsecmark_reg);
+
+ return err;
+}
+
+static void __exit xt_connsecmark_fini(void)
+{
+ xt_unregister_target(&ip6t_connsecmark_reg);
+ xt_unregister_target(&ipt_connsecmark_reg);
+}
+
+module_init(xt_connsecmark_init);
+module_exit(xt_connsecmark_fini);
^ permalink raw reply
* [PATCH 05/07] secmark: Add secmark support to conntrack
From: James Morris @ 2006-05-18 9:27 UTC (permalink / raw)
To: David S. Miller, Andrew Morton
Cc: Patrick McHardy, Stephen Smalley, netdev, Karl MacMillan
In-Reply-To: <Pine.LNX.4.64.0605180458130.569@d.namei>
This patch adds a secmark field to IP and NF conntracks, so that security
markings on packets can be copied to their associated connections, and
also copied back to packets as required. This is similar to the network
mark field currently used with conntrack, although it is intended for
enforcement of security policy rather than network policy.
Please apply.
Signed-off-by: James Morris <jmorris@namei.org>
---
include/linux/netfilter_ipv4/ip_conntrack.h | 4 ++++
include/net/netfilter/nf_conntrack.h | 4 ++++
include/net/netfilter/nf_conntrack_compat.h | 26 ++++++++++++++++++++++++++
net/ipv4/netfilter/Kconfig | 12 ++++++++++++
net/ipv4/netfilter/ip_conntrack_core.c | 3 +++
net/ipv4/netfilter/ip_conntrack_standalone.c | 5 +++++
net/netfilter/Kconfig | 12 ++++++++++++
net/netfilter/nf_conntrack_core.c | 3 +++
net/netfilter/nf_conntrack_standalone.c | 5 +++++
9 files changed, 74 insertions(+)
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.6.17-rc4-mm1.w/include/linux/netfilter_ipv4/ip_conntrack.h
--- linux-2.6.17-rc4-mm1.p/include/linux/netfilter_ipv4/ip_conntrack.h 2006-05-16 23:35:11.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/include/linux/netfilter_ipv4/ip_conntrack.h 2006-05-17 01:39:42.000000000 -0400
@@ -120,6 +120,10 @@ struct ip_conntrack
u_int32_t mark;
#endif
+#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK
+ u_int32_t secmark;
+#endif
+
/* Traversed often, so hopefully in different cacheline to top */
/* These are my tuples; original and reply */
struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/include/net/netfilter/nf_conntrack_compat.h linux-2.6.17-rc4-mm1.w/include/net/netfilter/nf_conntrack_compat.h
--- linux-2.6.17-rc4-mm1.p/include/net/netfilter/nf_conntrack_compat.h 2006-03-20 00:53:29.000000000 -0500
+++ linux-2.6.17-rc4-mm1.w/include/net/netfilter/nf_conntrack_compat.h 2006-05-17 01:39:42.000000000 -0400
@@ -20,6 +20,19 @@ static inline u_int32_t *nf_ct_get_mark(
}
#endif /* CONFIG_IP_NF_CONNTRACK_MARK */
+#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK
+static inline u_int32_t *nf_ct_get_secmark(const struct sk_buff *skb,
+ u_int32_t *ctinfo)
+{
+ struct ip_conntrack *ct = ip_conntrack_get(skb, ctinfo);
+
+ if (ct)
+ return &ct->secmark;
+ else
+ return NULL;
+}
+#endif /* CONFIG_IP_NF_CONNTRACK_SECMARK */
+
#ifdef CONFIG_IP_NF_CT_ACCT
static inline struct ip_conntrack_counter *
nf_ct_get_counters(const struct sk_buff *skb)
@@ -70,6 +83,19 @@ static inline u_int32_t *nf_ct_get_mark(
}
#endif /* CONFIG_NF_CONNTRACK_MARK */
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+static inline u_int32_t *nf_ct_get_secmark(const struct sk_buff *skb,
+ u_int32_t *ctinfo)
+{
+ struct nf_conn *ct = nf_ct_get(skb, ctinfo);
+
+ if (ct)
+ return &ct->secmark;
+ else
+ return NULL;
+}
+#endif /* CONFIG_NF_CONNTRACK_MARK */
+
#ifdef CONFIG_NF_CT_ACCT
static inline struct ip_conntrack_counter *
nf_ct_get_counters(const struct sk_buff *skb)
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/include/net/netfilter/nf_conntrack.h linux-2.6.17-rc4-mm1.w/include/net/netfilter/nf_conntrack.h
--- linux-2.6.17-rc4-mm1.p/include/net/netfilter/nf_conntrack.h 2006-05-16 23:35:11.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/include/net/netfilter/nf_conntrack.h 2006-05-17 01:39:42.000000000 -0400
@@ -113,6 +113,10 @@ struct nf_conn
u_int32_t mark;
#endif
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+ u_int32_t secmark;
+#endif
+
/* Storage reserved for other modules: */
union nf_conntrack_proto proto;
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/ipv4/netfilter/ip_conntrack_core.c linux-2.6.17-rc4-mm1.w/net/ipv4/netfilter/ip_conntrack_core.c
--- linux-2.6.17-rc4-mm1.p/net/ipv4/netfilter/ip_conntrack_core.c 2006-05-16 23:34:59.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/ipv4/netfilter/ip_conntrack_core.c 2006-05-17 01:39:42.000000000 -0400
@@ -724,6 +724,9 @@ init_conntrack(struct ip_conntrack_tuple
/* this is ugly, but there is no other place where to put it */
conntrack->nat.masq_index = exp->master->nat.masq_index;
#endif
+#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK
+ conntrack->secmark = exp->master->secmark;
+#endif
nf_conntrack_get(&conntrack->master->ct_general);
CONNTRACK_STAT_INC(expect_new);
} else {
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.6.17-rc4-mm1.w/net/ipv4/netfilter/ip_conntrack_standalone.c
--- linux-2.6.17-rc4-mm1.p/net/ipv4/netfilter/ip_conntrack_standalone.c 2006-05-16 23:34:59.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/ipv4/netfilter/ip_conntrack_standalone.c 2006-05-17 01:39:42.000000000 -0400
@@ -189,6 +189,11 @@ static int ct_seq_show(struct seq_file *
return -ENOSPC;
#endif
+#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK
+ if (seq_printf(s, "secmark=%u ", conntrack->secmark))
+ return -ENOSPC;
+#endif
+
if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
return -ENOSPC;
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/ipv4/netfilter/Kconfig linux-2.6.17-rc4-mm1.w/net/ipv4/netfilter/Kconfig
--- linux-2.6.17-rc4-mm1.p/net/ipv4/netfilter/Kconfig 2006-05-16 23:34:59.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/ipv4/netfilter/Kconfig 2006-05-17 01:48:41.000000000 -0400
@@ -55,6 +55,18 @@ config IP_NF_CONNTRACK_MARK
of packets, but this mark value is kept in the conntrack session
instead of the individual packets.
+config IP_NF_CONNTRACK_SECMARK
+ bool 'Connection tracking security mark support'
+ depends on IP_NF_CONNTRACK && NETWORK_SECMARK
+ help
+ This option enables security markings to be applied to
+ connections. Typically they are copied to connections from
+ packets using the CONNSECMARK target and copied back from
+ connections to packets with the same target, with the packets
+ being originally labeled via SECMARK.
+
+ If unsure, say 'N'.
+
config IP_NF_CONNTRACK_EVENTS
bool "Connection tracking events (EXPERIMENTAL)"
depends on EXPERIMENTAL && IP_NF_CONNTRACK
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/netfilter/Kconfig linux-2.6.17-rc4-mm1.w/net/netfilter/Kconfig
--- linux-2.6.17-rc4-mm1.p/net/netfilter/Kconfig 2006-05-17 01:35:52.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/netfilter/Kconfig 2006-05-17 01:48:42.000000000 -0400
@@ -60,6 +60,18 @@ config NF_CONNTRACK_MARK
of packets, but this mark value is kept in the conntrack session
instead of the individual packets.
+config NF_CONNTRACK_SECMARK
+ bool 'Connection tracking security mark support'
+ depends on NF_CONNTRACK && NETWORK_SECMARK
+ help
+ This option enables security markings to be applied to
+ connections. Typically they are copied to connections from
+ packets using the CONNSECMARK target and copied back from
+ connections to packets with the same target, with the packets
+ being originally labeled via SECMARK.
+
+ If unsure, say 'N'.
+
config NF_CONNTRACK_EVENTS
bool "Connection tracking events (EXPERIMENTAL)"
depends on EXPERIMENTAL && NF_CONNTRACK
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/netfilter/nf_conntrack_core.c linux-2.6.17-rc4-mm1.w/net/netfilter/nf_conntrack_core.c
--- linux-2.6.17-rc4-mm1.p/net/netfilter/nf_conntrack_core.c 2006-05-16 23:34:59.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/netfilter/nf_conntrack_core.c 2006-05-17 01:39:42.000000000 -0400
@@ -990,6 +990,9 @@ init_conntrack(const struct nf_conntrack
#ifdef CONFIG_NF_CONNTRACK_MARK
conntrack->mark = exp->master->mark;
#endif
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+ conntrack->secmark = exp->master->secmark;
+#endif
nf_conntrack_get(&conntrack->master->ct_general);
NF_CT_STAT_INC(expect_new);
} else
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/netfilter/nf_conntrack_standalone.c linux-2.6.17-rc4-mm1.w/net/netfilter/nf_conntrack_standalone.c
--- linux-2.6.17-rc4-mm1.p/net/netfilter/nf_conntrack_standalone.c 2006-05-16 23:34:59.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/netfilter/nf_conntrack_standalone.c 2006-05-17 01:39:42.000000000 -0400
@@ -213,6 +213,11 @@ static int ct_seq_show(struct seq_file *
return -ENOSPC;
#endif
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+ if (seq_printf(s, "secmark=%u ", conntrack->secmark))
+ return -ENOSPC;
+#endif
+
if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
return -ENOSPC;
^ permalink raw reply
* [PATCH 04/07] secmark: Add xtables SECMARK target
From: James Morris @ 2006-05-18 9:26 UTC (permalink / raw)
To: David S. Miller, Andrew Morton
Cc: Patrick McHardy, Stephen Smalley, netdev, Karl MacMillan
In-Reply-To: <Pine.LNX.4.64.0605180458130.569@d.namei>
This patch adds a SECMARK target to xtables, allowing the admin to apply
security marks to packets via both iptables and ip6tables.
The target currently handles SELinux security marking, but can be extended
for other purposes as needed.
Please apply.
Signed-off-by: James Morris <jmorris@namei.org>
---
include/linux/netfilter/xt_SECMARK.h | 26 +++++
net/netfilter/Kconfig | 9 ++
net/netfilter/Makefile | 1
net/netfilter/xt_SECMARK.c | 156 +++++++++++++++++++++++++++++++++++
4 files changed, 192 insertions(+)
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/include/linux/netfilter/xt_SECMARK.h linux-2.6.17-rc4-mm1.w/include/linux/netfilter/xt_SECMARK.h
--- linux-2.6.17-rc4-mm1.p/include/linux/netfilter/xt_SECMARK.h 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.17-rc4-mm1.w/include/linux/netfilter/xt_SECMARK.h 2006-05-17 01:31:26.000000000 -0400
@@ -0,0 +1,26 @@
+#ifndef _XT_SECMARK_H_target
+#define _XT_SECMARK_H_target
+
+/*
+ * This is intended for use by various security subsystems (but not
+ * at the same time).
+ *
+ * 'mode' refers to the specific security subsystem which the
+ * packets are being marked for.
+ */
+#define SECMARK_MODE_SEL 0x01 /* SELinux */
+#define SECMARK_SELCTX_MAX 256
+
+struct xt_secmark_target_selinux_info {
+ u_int32_t selsid;
+ char selctx[SECMARK_SELCTX_MAX];
+};
+
+struct xt_secmark_target_info {
+ u_int8_t mode;
+ union {
+ struct xt_secmark_target_selinux_info sel;
+ } u;
+};
+
+#endif /*_XT_SECMARK_H_target */
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/netfilter/Kconfig linux-2.6.17-rc4-mm1.w/net/netfilter/Kconfig
--- linux-2.6.17-rc4-mm1.p/net/netfilter/Kconfig 2006-05-17 01:03:27.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/netfilter/Kconfig 2006-05-17 01:31:26.000000000 -0400
@@ -174,6 +174,15 @@ config NETFILTER_XT_TARGET_NOTRACK
If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>. If unsure, say `N'.
+config NETFILTER_XT_TARGET_SECMARK
+ tristate '"SECMARK" target support'
+ depends on NETFILTER_XTABLES && NETWORK_SECMARK
+ help
+ The SECMARK target allows security marking of network
+ packets, for use with security subsystems.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
config NETFILTER_XT_MATCH_COMMENT
tristate '"comment" match support'
depends on NETFILTER_XTABLES
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/netfilter/Makefile linux-2.6.17-rc4-mm1.w/net/netfilter/Makefile
--- linux-2.6.17-rc4-mm1.p/net/netfilter/Makefile 2006-05-17 01:03:27.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/netfilter/Makefile 2006-05-17 01:31:26.000000000 -0400
@@ -28,6 +28,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMAR
obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) += xt_MARK.o
obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o
obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o
+obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o
# matches
obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/netfilter/xt_SECMARK.c linux-2.6.17-rc4-mm1.w/net/netfilter/xt_SECMARK.c
--- linux-2.6.17-rc4-mm1.p/net/netfilter/xt_SECMARK.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.17-rc4-mm1.w/net/netfilter/xt_SECMARK.c 2006-05-17 01:31:52.000000000 -0400
@@ -0,0 +1,156 @@
+/*
+ * Module for modifying the secmark field of the skb, for use by
+ * security subsystems.
+ *
+ * Based on the nfmark match by:
+ * (C) 1999-2001 Marc Boucher <marc@mbsi.ca>
+ *
+ * (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/selinux.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_SECMARK.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("James Morris <jmorris@redhat.com>");
+MODULE_DESCRIPTION("ip[6]tables SECMARK modification module");
+MODULE_ALIAS("ipt_SECMARK");
+MODULE_ALIAS("ip6t_SECMARK");
+
+#define PFX "SECMARK: "
+
+static u8 mode;
+
+static unsigned int target(struct sk_buff **pskb, const struct net_device *in,
+ const struct net_device *out, unsigned int hooknum,
+ const struct xt_target *target,
+ const void *targinfo, void *userinfo)
+{
+ u32 secmark = 0;
+ const struct xt_secmark_target_info *info = targinfo;
+
+ BUG_ON(info->mode != mode);
+
+ switch (mode) {
+ case SECMARK_MODE_SEL:
+ secmark = info->u.sel.selsid;
+ break;
+
+ default:
+ BUG();
+ }
+
+ if ((*pskb)->secmark != secmark)
+ (*pskb)->secmark = secmark;
+
+ return XT_CONTINUE;
+}
+
+static int checkentry_selinux(struct xt_secmark_target_info *info)
+{
+ int err;
+ struct xt_secmark_target_selinux_info *sel = &info->u.sel;
+
+ err = selinux_string_to_sid(sel->selctx, &sel->selsid);
+ if (err) {
+ if (err == -EINVAL)
+ printk(KERN_INFO PFX "invalid SELinux context \'%s\'\n",
+ sel->selctx);
+ return 0;
+ }
+
+ if (!sel->selsid) {
+ printk(KERN_INFO PFX "unable to map SELinux context \'%s\'\n",
+ sel->selctx);
+ return 0;
+ }
+
+ err = selinux_relabel_packet_permission(sel->selsid);
+ if (err) {
+ printk(KERN_INFO PFX "unable to obtain relabeling permission\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int checkentry(const char *tablename, const void *entry,
+ const struct xt_target *target, void *targinfo,
+ unsigned int targinfosize, unsigned int hook_mask)
+{
+ struct xt_secmark_target_info *info = targinfo;
+
+ if (mode && mode != info->mode) {
+ printk(KERN_INFO PFX "mode already set to %hu cannot mix with "
+ "rules for mode %hu\n", mode, info->mode);
+ return 0;
+ }
+
+ switch (info->mode) {
+ case SECMARK_MODE_SEL:
+ if (!checkentry_selinux(info))
+ return 0;
+ break;
+
+ default:
+ printk(KERN_INFO PFX "invalid mode: %hu\n", info->mode);
+ return 0;
+ }
+
+ if (!mode)
+ mode = info->mode;
+ return 1;
+}
+
+static struct xt_target ipt_secmark_reg = {
+ .name = "SECMARK",
+ .target = target,
+ .targetsize = sizeof(struct xt_secmark_target_info),
+ .table = "mangle",
+ .checkentry = checkentry,
+ .me = THIS_MODULE,
+ .family = AF_INET,
+ .revision = 0,
+};
+
+static struct xt_target ip6t_secmark_reg = {
+ .name = "SECMARK",
+ .target = target,
+ .targetsize = sizeof(struct xt_secmark_target_info),
+ .table = "mangle",
+ .checkentry = checkentry,
+ .me = THIS_MODULE,
+ .family = AF_INET6,
+ .revision = 0,
+};
+
+static int __init xt_secmark_init(void)
+{
+ int err;
+
+ err = xt_register_target(&ipt_secmark_reg);
+ if (err)
+ return err;
+
+ err = xt_register_target(&ip6t_secmark_reg);
+ if (err)
+ xt_unregister_target(&ipt_secmark_reg);
+
+ return err;
+}
+
+static void __exit xt_secmark_fini(void)
+{
+ xt_unregister_target(&ip6t_secmark_reg);
+ xt_unregister_target(&ipt_secmark_reg);
+}
+
+module_init(xt_secmark_init);
+module_exit(xt_secmark_fini);
^ permalink raw reply
* [PATCH 03/07] secmark: Add secmark support to core networking.
From: James Morris @ 2006-05-18 9:24 UTC (permalink / raw)
To: David S. Miller, Andrew Morton
Cc: Patrick McHardy, Stephen Smalley, netdev, Karl MacMillan
In-Reply-To: <Pine.LNX.4.64.0605180458130.569@d.namei>
This patch adds a secmark field to the skbuff structure, to allow security
subsystems to place security markings on network packets. This is similar
to the nfmark field, except is intended for implementing security policy,
rather than than networking policy.
This patch was already acked in principle by Dave Miller.
Please apply.
Signed-off-by: James Morris <jmorris@namei.org>
---
include/linux/skbuff.h | 22 ++++++++++++++++++++++
net/Kconfig | 7 +++++++
net/core/skbuff.c | 3 ++-
net/ipv4/ip_output.c | 1 +
net/ipv4/netfilter/ipt_REJECT.c | 1 +
net/ipv6/ip6_output.c | 1 +
6 files changed, 34 insertions(+), 1 deletion(-)
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/include/linux/skbuff.h linux-2.6.17-rc4-mm1.w/include/linux/skbuff.h
--- linux-2.6.17-rc4-mm1.p/include/linux/skbuff.h 2006-05-17 01:04:35.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/include/linux/skbuff.h 2006-05-17 01:29:36.000000000 -0400
@@ -208,6 +208,7 @@ enum {
* @nf_bridge: Saved data about a bridged frame - see br_netfilter.c
* @tc_index: Traffic control index
* @tc_verd: traffic control verdict
+ * @secmark: security marking
*/
struct sk_buff {
@@ -284,6 +285,9 @@ struct sk_buff {
__u16 tc_verd; /* traffic control verdict */
#endif
#endif
+#ifdef CONFIG_NETWORK_SECMARK
+ __u32 secmark;
+#endif
/* These elements must be at the end, see alloc_skb() for details. */
@@ -1395,5 +1399,23 @@ static inline void nf_reset(struct sk_bu
static inline void nf_reset(struct sk_buff *skb) {}
#endif /* CONFIG_NETFILTER */
+#ifdef CONFIG_NETWORK_SECMARK
+static inline void skb_copy_secmark(struct sk_buff *to, const struct sk_buff *from)
+{
+ to->secmark = from->secmark;
+}
+
+static inline void skb_init_secmark(struct sk_buff *skb)
+{
+ skb->secmark = 0;
+}
+#else
+static inline void skb_copy_secmark(struct sk_buff *to, const struct sk_buff *from)
+{ }
+
+static inline void skb_init_secmark(struct sk_buff *skb)
+{ }
+#endif
+
#endif /* __KERNEL__ */
#endif /* _LINUX_SKBUFF_H */
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/core/skbuff.c linux-2.6.17-rc4-mm1.w/net/core/skbuff.c
--- linux-2.6.17-rc4-mm1.p/net/core/skbuff.c 2006-05-17 01:04:35.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/core/skbuff.c 2006-05-17 01:29:36.000000000 -0400
@@ -464,7 +464,7 @@ struct sk_buff *skb_clone(struct sk_buff
n->tc_verd = CLR_TC_MUNGED(n->tc_verd);
C(input_dev);
#endif
-
+ skb_copy_secmark(n, skb);
#endif
C(truesize);
atomic_set(&n->users, 1);
@@ -526,6 +526,7 @@ static void copy_skb_header(struct sk_bu
#endif
new->tc_index = old->tc_index;
#endif
+ skb_copy_secmark(new, old);
atomic_set(&new->users, 1);
skb_shinfo(new)->tso_size = skb_shinfo(old)->tso_size;
skb_shinfo(new)->tso_segs = skb_shinfo(old)->tso_segs;
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/ipv4/ip_output.c linux-2.6.17-rc4-mm1.w/net/ipv4/ip_output.c
--- linux-2.6.17-rc4-mm1.p/net/ipv4/ip_output.c 2006-05-17 01:04:35.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/ipv4/ip_output.c 2006-05-17 01:29:36.000000000 -0400
@@ -410,6 +410,7 @@ static void ip_copy_metadata(struct sk_b
nf_bridge_get(to->nf_bridge);
#endif
#endif
+ skb_copy_secmark(to, from);
}
/*
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/ipv4/netfilter/ipt_REJECT.c linux-2.6.17-rc4-mm1.w/net/ipv4/netfilter/ipt_REJECT.c
--- linux-2.6.17-rc4-mm1.p/net/ipv4/netfilter/ipt_REJECT.c 2006-05-17 01:04:35.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/ipv4/netfilter/ipt_REJECT.c 2006-05-17 01:29:36.000000000 -0400
@@ -147,6 +147,7 @@ static void send_reset(struct sk_buff *o
/* This packet will not be the same as the other: clear nf fields */
nf_reset(nskb);
nskb->nfmark = 0;
+ skb_init_secmark(nskb);
tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/ipv6/ip6_output.c linux-2.6.17-rc4-mm1.w/net/ipv6/ip6_output.c
--- linux-2.6.17-rc4-mm1.p/net/ipv6/ip6_output.c 2006-05-17 01:04:35.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/ipv6/ip6_output.c 2006-05-17 01:29:36.000000000 -0400
@@ -458,6 +458,7 @@ static void ip6_copy_metadata(struct sk_
nf_bridge_get(to->nf_bridge);
#endif
#endif
+ skb_copy_secmark(to, from);
}
int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/net/Kconfig linux-2.6.17-rc4-mm1.w/net/Kconfig
--- linux-2.6.17-rc4-mm1.p/net/Kconfig 2006-05-17 01:04:35.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/net/Kconfig 2006-05-17 01:29:36.000000000 -0400
@@ -66,6 +66,13 @@ source "net/ipv6/Kconfig"
endif # if INET
+config NETWORK_SECMARK
+ bool "Security Marking"
+ help
+ This enables security marking of network packets, similar
+ to nfmark, but designated for security purposes.
+ If you are unsure how to answer this question, answer N.
+
menuconfig NETFILTER
bool "Network packet filtering (replaces ipchains)"
---help---
^ permalink raw reply
* [PATCH 02/07] secmark: Add SELinux exports
From: James Morris @ 2006-05-18 9:23 UTC (permalink / raw)
To: David S. Miller, Andrew Morton
Cc: Patrick McHardy, Stephen Smalley, netdev, Karl MacMillan
In-Reply-To: <Pine.LNX.4.64.0605180458130.569@d.namei>
This patch exports adds new functions to the in-kernel SELinux API in
support of the new secmark-based packet controls.
Please apply.
Signed-off-by: James Morris <jmorris@namei.org>
---
include/linux/selinux.h | 32 ++++++++++++++++++++++++++++++++
security/selinux/exports.c | 22 ++++++++++++++++++++++
2 files changed, 54 insertions(+)
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/include/linux/selinux.h linux-2.6.17-rc4-mm1.w/include/linux/selinux.h
--- linux-2.6.17-rc4-mm1.p/include/linux/selinux.h 2006-05-17 01:03:34.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/include/linux/selinux.h 2006-05-17 01:18:57.000000000 -0400
@@ -118,6 +118,27 @@ void selinux_get_ipc_sid(const struct ke
*/
void selinux_get_task_sid(struct task_struct *tsk, u32 *sid);
+/**
+ * selinux_string_to_sid - map a security context string to a security ID
+ * @str: the security context string to be mapped
+ * @sid: ID value returned via this.
+ *
+ * Returns 0 if successful, with the SID stored in sid. A value
+ * of zero for sid indicates no SID could be determined (but no error
+ * occurred).
+ */
+int selinux_string_to_sid(char *str, u32 *sid);
+
+/**
+ * selinux_relabel_packet_permission - check permission to relabel a packet
+ * @sid: ID value to be applied to network packet (via SECMARK, most likely)
+ *
+ * Returns 0 if the current task is allowed to label packets with the
+ * supplied security ID. Note that it is implicit that the packet is always
+ * being relabeled from the default unlabled value, and that the access
+ * control decision is made in the AVC.
+ */
+int selinux_relabel_packet_permission(u32 sid);
#else
@@ -172,6 +193,17 @@ static inline void selinux_get_task_sid(
*sid = 0;
}
+static inline int selinux_string_to_sid(const char *str, u32 *sid)
+{
+ *sid = 0;
+ return 0;
+}
+
+static inline int selinux_relabel_packet_permission(u32 sid)
+{
+ return 0;
+}
+
#endif /* CONFIG_SECURITY_SELINUX */
#endif /* _LINUX_SELINUX_H */
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/security/selinux/exports.c linux-2.6.17-rc4-mm1.w/security/selinux/exports.c
--- linux-2.6.17-rc4-mm1.p/security/selinux/exports.c 2006-05-17 01:03:34.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/security/selinux/exports.c 2006-05-17 01:18:57.000000000 -0400
@@ -72,3 +72,25 @@ void selinux_get_task_sid(struct task_st
*sid = 0;
}
+int selinux_string_to_sid(char *str, u32 *sid)
+{
+ if (selinux_enabled)
+ return security_context_to_sid(str, strlen(str), sid);
+ else {
+ *sid = 0;
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(selinux_string_to_sid);
+
+int selinux_relabel_packet_permission(u32 sid)
+{
+ if (selinux_enabled) {
+ struct task_security_struct *tsec = current->security;
+
+ return avc_has_perm(tsec->sid, sid, SECCLASS_PACKET,
+ PACKET__RELABELTO, NULL);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(selinux_relabel_packet_permission);
^ permalink raw reply
* [PATCH 01/07] secmark: Add new flask definitions to SELinux
From: James Morris @ 2006-05-18 9:22 UTC (permalink / raw)
To: David S. Miller, Andrew Morton
Cc: Patrick McHardy, Stephen Smalley, netdev, Karl MacMillan
In-Reply-To: <Pine.LNX.4.64.0605180458130.569@d.namei>
This patch adds support for a new object class ('packet'), and associated
permissions ('send', 'recv', 'relabelto'). These are used to enforce
security policy for network packets labeled with SECMARK, and for adding
labeling rules.
Please apply.
Signed-off-by: James Morris <jmorris@namei.org>
---
security/selinux/include/av_perm_to_string.h | 3 +++
security/selinux/include/av_permissions.h | 3 +++
security/selinux/include/class_to_string.h | 1 +
security/selinux/include/flask.h | 1 +
4 files changed, 8 insertions(+)
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/security/selinux/include/av_permissions.h linux-2.6.17-rc4-mm1.w/security/selinux/include/av_permissions.h
--- linux-2.6.17-rc4-mm1.p/security/selinux/include/av_permissions.h 2006-05-16 23:35:11.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/security/selinux/include/av_permissions.h 2006-05-17 01:13:15.000000000 -0400
@@ -956,3 +956,6 @@
#define APPLETALK_SOCKET__SEND_MSG 0x00100000UL
#define APPLETALK_SOCKET__NAME_BIND 0x00200000UL
+#define PACKET__SEND 0x00000001UL
+#define PACKET__RECV 0x00000002UL
+#define PACKET__RELABELTO 0x00000004UL
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/security/selinux/include/av_perm_to_string.h linux-2.6.17-rc4-mm1.w/security/selinux/include/av_perm_to_string.h
--- linux-2.6.17-rc4-mm1.p/security/selinux/include/av_perm_to_string.h 2006-03-20 00:53:29.000000000 -0500
+++ linux-2.6.17-rc4-mm1.w/security/selinux/include/av_perm_to_string.h 2006-05-17 01:13:33.000000000 -0400
@@ -239,3 +239,6 @@
S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, "recvfrom")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, "setcontext")
+ S_(SECCLASS_PACKET, PACKET__SEND, "send")
+ S_(SECCLASS_PACKET, PACKET__RECV, "recv")
+ S_(SECCLASS_PACKET, PACKET__RELABELTO, "relabelto")
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/security/selinux/include/class_to_string.h linux-2.6.17-rc4-mm1.w/security/selinux/include/class_to_string.h
--- linux-2.6.17-rc4-mm1.p/security/selinux/include/class_to_string.h 2006-05-16 23:35:11.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/security/selinux/include/class_to_string.h 2006-05-17 01:10:46.000000000 -0400
@@ -59,3 +59,4 @@
S_("association")
S_("netlink_kobject_uevent_socket")
S_("appletalk_socket")
+ S_("packet")
diff -purN -X dontdiff linux-2.6.17-rc4-mm1.p/security/selinux/include/flask.h linux-2.6.17-rc4-mm1.w/security/selinux/include/flask.h
--- linux-2.6.17-rc4-mm1.p/security/selinux/include/flask.h 2006-05-16 23:35:11.000000000 -0400
+++ linux-2.6.17-rc4-mm1.w/security/selinux/include/flask.h 2006-05-17 01:09:43.000000000 -0400
@@ -61,6 +61,7 @@
#define SECCLASS_ASSOCIATION 54
#define SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET 55
#define SECCLASS_APPLETALK_SOCKET 56
+#define SECCLASS_PACKET 57
/*
* Security identifier indices for initial entities
^ permalink raw reply
* [PATCHSET 0/7] SECMARK 2.0
From: James Morris @ 2006-05-18 9:20 UTC (permalink / raw)
To: David S. Miller, Andrew Morton
Cc: Patrick McHardy, Stephen Smalley, netdev, Karl MacMillan
The following patchset is an updated version of secmark, which I'd like to
propose for inclusion in either Dave or Andrew's tree for subsequent merge
into mainline during the 2.6.18 merge window.
Secmark implements a new scheme for adding security markings to packets
via iptables, as well as changes to SELinux to use these markings for
security policy enforcement. The rationale for this scheme is explained
and discussed in detail in the original threads:
http://thread.gmane.org/gmane.linux.network/34927/
http://thread.gmane.org/gmane.linux.network/35244/
Since the last posting, I've fully separated the conntrack-related
functionality of SECMARK into the CONNSECMARK target, following the
original suggestion by Patrick McHardy. This indeed keeps the code
cleaner and only requires one extra rule per service. I've also added a
boot param for SELinux to control whether the new packet controls are used
or not.
Examples of policy and rulesets, as well as a full archive of patches for
iptables and SELinux userland, may be found at:
http://people.redhat.com/jmorris/selinux/secmark/
The code has been tested with various compilation options and in several
scenarios, including with 'complicated' protocols such as FTP and also
with the new generic conntrack code with IPv6 connection tracking.
I'm not sure if this would be better in Dave or Andrew's tree. It touches
a lot of the core networking, although not very heavily, and most of the
Netfilter stuff is new targets. There's already an SELinux patch in -mm
which affects the SELinux components in this patchset (I rebased against
current -mm because of this).
I'll send the Netfilter userland patches separately to the netfilter
developers.
Cumulative diffstat:
Documentation/kernel-parameters.txt | 9 +
include/linux/netfilter/xt_CONNSECMARK.h | 13 +
include/linux/netfilter/xt_SECMARK.h | 26 ++
include/linux/netfilter_ipv4/ip_conntrack.h | 4
include/linux/selinux.h | 32 +++
include/linux/skbuff.h | 22 ++
include/net/netfilter/nf_conntrack.h | 4
include/net/netfilter/nf_conntrack_compat.h | 26 ++
net/Kconfig | 7
net/core/skbuff.c | 3
net/ipv4/ip_output.c | 1
net/ipv4/netfilter/Kconfig | 12 +
net/ipv4/netfilter/ip_conntrack_core.c | 3
net/ipv4/netfilter/ip_conntrack_standalone.c | 5
net/ipv4/netfilter/ipt_REJECT.c | 1
net/ipv6/ip6_output.c | 1
net/netfilter/Kconfig | 32 +++
net/netfilter/Makefile | 2
net/netfilter/nf_conntrack_core.c | 3
net/netfilter/nf_conntrack_standalone.c | 5
net/netfilter/xt_CONNSECMARK.c | 155 +++++++++++++++++
net/netfilter/xt_SECMARK.c | 156 +++++++++++++++++
security/selinux/Kconfig | 2
security/selinux/exports.c | 22 ++
security/selinux/hooks.c | 241 ++++++++++++++-------------
security/selinux/include/av_perm_to_string.h | 3
security/selinux/include/av_permissions.h | 3
security/selinux/include/class_to_string.h | 1
security/selinux/include/flask.h | 1
security/selinux/include/xfrm.h | 2
security/selinux/selinuxfs.c | 59 ++++++
security/selinux/xfrm.c | 12 -
32 files changed, 747 insertions(+), 121 deletions(-)
--
James Morris
<jmorris@namei.org>
^ permalink raw reply
* Re: ipsec tunnel asymmetrical mtu
From: Marco Berizzi @ 2006-05-18 8:46 UTC (permalink / raw)
To: herbert; +Cc: netdev
In-Reply-To: <BAY103-F1D9D892CDA10889FCB525B2A80@phx.gbl>
Marco Berizzi wrote:
>Herbert Xu wrote:
>
>>However, the fact that the tcpdump causes more chunky packets to
>>make it through could be an indication that there is a bug somewhere
>>in our NAT/IPsec code or at least a suboptimal memory allocation
>>strategy that's somehow avoided when AF_PACKET pins the skb down.
JFYI: same problem with 2.6.17-rc4-git5
^ permalink raw reply
* RESEND: [PATCH] Interface Stat Clearing Framework, skge support, ethtool support]
From: Phil Dibowitz @ 2006-05-18 8:23 UTC (permalink / raw)
To: jgarzik; +Cc: netdev
[-- Attachment #1.1: Type: text/plain, Size: 1340 bytes --]
Resending this - saw no response.
Hey folks,
A few months back I posted an in-progress patch for adding a clear_stats
framework similar to the get_stats framework and implimenting support
for it in the skge driver (the one NIC I have access to), as well as
adding the ethtool support for it.
While a few people said they didn't see the need for it, other people
did see the need for it, and I know it's a common request on many
sysadmin mailing lists I'm on.
Since no one seemed to have any technical issue with the patch, I've
cleaned up the patch, tested it, and fixed a few minor issues.
Unless someone has an objection, I'd think this would be useful to a lot
of people.
There are two patches attached:
interface_stats_clear.patch - the kernel patch against 2.6.17-rc3-git2
ethtool3-clearstats.patch - the ethtool patch to add the -z flag to
support it.
If the kernel patch gets accepted I'll send a more complete ethtool
patch with documentation updates, etc.
Thanks.
--
Phil Dibowitz phil@ipom.com
Freeware and Technical Pages Insanity Palace of Metallica
http://www.phildev.net/ http://www.ipom.com/
"Be who you are and say what you feel, because those who mind don't
matter and those who matter don't mind."
- Dr. Suess
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: interface_stats_clear.patch --]
[-- Type: text/x-patch; name="interface_stats_clear.patch", Size: 6419 bytes --]
This patch adds support for clearing interface statistics using the ethtool interface adding a new 0x23 command. It adds a clear_stats function pointer to the net_device struct, and then impliments local functions in the driver much the say get_stats works. The ethtool funtion pointer points to the same functions. The driver-local functions are currently only implimented in the skge driver.
Signed-off-by: Phil Dibowitz <phil@ipom.com>
---
diff -puN include/linux/netdevice.h~interface_stats_clear include/linux/netdevice.h
--- linux-2.6.17-rc3-git2/include/linux/netdevice.h~interface_stats_clear 2006-04-29 19:44:41.000000000 -0700
+++ linux-2.6.17-rc3-git2-phil/include/linux/netdevice.h 2006-04-29 19:44:41.000000000 -0700
@@ -319,6 +319,7 @@ struct net_device
struct net_device_stats* (*get_stats)(struct net_device *dev);
+ void (*clear_stats)(struct net_device *dev);
struct iw_statistics* (*get_wireless_stats)(struct net_device *dev);
/* List of functions to handle Wireless Extensions (instead of ioctl).
diff -puN drivers/net/skge.c~interface_stats_clear drivers/net/skge.c
--- linux-2.6.17-rc3-git2/drivers/net/skge.c~interface_stats_clear 2006-04-29 19:44:41.000000000 -0700
+++ linux-2.6.17-rc3-git2-phil/drivers/net/skge.c 2006-04-29 19:44:41.000000000 -0700
@@ -44,7 +44,7 @@
#include "skge.h"
#define DRV_NAME "skge"
-#define DRV_VERSION "1.5"
+#define DRV_VERSION "1.6"
#define PFX DRV_NAME " "
#define DEFAULT_TX_RING_SIZE 128
@@ -97,6 +97,8 @@ static int xm_phy_write(struct skge_hw *
static int gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);
static void genesis_get_stats(struct skge_port *skge, u64 *data);
static void yukon_get_stats(struct skge_port *skge, u64 *data);
+static void genesis_clear_stats(struct skge_port *skge);
+static void yukon_clear_stats(struct skge_port *skge);
static void yukon_init(struct skge_hw *hw, int port);
static void genesis_mac_init(struct skge_hw *hw, int port);
static void genesis_link_up(struct skge_port *skge);
@@ -366,6 +368,15 @@ static struct net_device_stats *skge_get
return &skge->net_stats;
}
+static void skge_clear_stats(struct net_device *dev)
+{
+ struct skge_port *skge = netdev_priv(dev);
+ if (skge->hw->chip_id == CHIP_ID_GENESIS)
+ genesis_clear_stats(skge);
+ else
+ yukon_clear_stats(skge);
+}
+
static void skge_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
int i;
@@ -722,6 +733,7 @@ static struct ethtool_ops skge_ethtool_o
.phys_id = skge_phys_id,
.get_stats_count = skge_get_stats_count,
.get_ethtool_stats = skge_get_ethtool_stats,
+ .clear_ethtool_stats = skge_clear_stats,
.get_perm_addr = ethtool_op_get_perm_addr,
};
@@ -1383,6 +1395,20 @@ static void genesis_get_stats(struct skg
data[i] = xm_read32(hw, port, skge_stats[i].xmac_offset);
}
+static void genesis_clear_stats(struct skge_port *skge)
+{
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
+
+ /*
+ * This is based on reading other parts of the driver
+ * and is not yet tested.
+ */
+
+ xm_write16(hw, port, XM_STAT_CMD, 0 | XM_SC_CLR_RXC
+ | XM_SC_CLR_TXC);
+}
+
static void genesis_mac_intr(struct skge_hw *hw, int port)
{
struct skge_port *skge = netdev_priv(hw->dev[port]);
@@ -1871,6 +1897,21 @@ static void yukon_get_stats(struct skge_
skge_stats[i].gma_offset);
}
+static void yukon_clear_stats(struct skge_port *skge)
+{
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
+ u16 reg;
+ int i;
+
+ reg = gma_read16(hw, port, GM_PHY_ADDR);
+ /* this read is important, or we sometimes get no effect */
+ gma_write16(hw, port, GM_PHY_ADDR, reg | GM_PAR_MIB_CLR);
+ for (i = 0; i < GM_MIB_CNT_SIZE; i++)
+ gma_read16(hw, port, GM_MIB_CNT_BASE + 8*i);
+ gma_write16(hw, port, GM_PHY_ADDR, reg);
+}
+
static void yukon_mac_intr(struct skge_hw *hw, int port)
{
struct net_device *dev = hw->dev[port];
@@ -3183,6 +3224,7 @@ static struct net_device *skge_devinit(s
dev->do_ioctl = skge_ioctl;
dev->hard_start_xmit = skge_xmit_frame;
dev->get_stats = skge_get_stats;
+ dev->clear_stats = skge_clear_stats;
if (hw->chip_id == CHIP_ID_GENESIS)
dev->set_multicast_list = genesis_set_multicast;
else
diff -puN include/linux/ethtool.h~interface_stats_clear include/linux/ethtool.h
--- linux-2.6.17-rc3-git2/include/linux/ethtool.h~interface_stats_clear 2006-04-29 19:44:41.000000000 -0700
+++ linux-2.6.17-rc3-git2-phil/include/linux/ethtool.h 2006-04-29 19:44:41.000000000 -0700
@@ -365,6 +365,7 @@ struct ethtool_ops {
int (*phys_id)(struct net_device *, u32);
int (*get_stats_count)(struct net_device *);
void (*get_ethtool_stats)(struct net_device *, struct ethtool_stats *, u64 *);
+ void (*clear_ethtool_stats)(struct net_device *);
int (*get_perm_addr)(struct net_device *, struct ethtool_perm_addr *, u8 *);
int (*begin)(struct net_device *);
void (*complete)(struct net_device *);
@@ -408,6 +409,7 @@ struct ethtool_ops {
#define ETHTOOL_GPERMADDR 0x00000020 /* Get permanent hardware address */
#define ETHTOOL_GUFO 0x00000021 /* Get UFO enable (ethtool_value) */
#define ETHTOOL_SUFO 0x00000022 /* Set UFO enable (ethtool_value) */
+#define ETHTOOL_CSTATS 0x00000023 /* Clear NIC-specific statistics */
/* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET
diff -puN net/core/ethtool.c~interface_stats_clear net/core/ethtool.c
--- linux-2.6.17-rc3-git2/net/core/ethtool.c~interface_stats_clear 2006-04-29 19:44:41.000000000 -0700
+++ linux-2.6.17-rc3-git2-phil/net/core/ethtool.c 2006-04-29 19:44:41.000000000 -0700
@@ -741,6 +741,17 @@ static int ethtool_get_stats(struct net_
return ret;
}
+static int ethtool_clear_stats(struct net_device *dev, void __user *useraddr)
+{
+ struct ethtool_ops *ops = dev->ethtool_ops;
+ if (!ops->clear_ethtool_stats)
+ return -EOPNOTSUPP;
+
+ ops->clear_ethtool_stats(dev);
+
+ return 0;
+}
+
static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
{
struct ethtool_perm_addr epaddr;
@@ -906,6 +917,9 @@ int dev_ethtool(struct ifreq *ifr)
case ETHTOOL_SUFO:
rc = ethtool_set_ufo(dev, useraddr);
break;
+ case ETHTOOL_CSTATS:
+ rc = ethtool_clear_stats(dev, useraddr);
+ break;
default:
rc = -EOPNOTSUPP;
}
_
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.3: ethtool3-clearstats.patch --]
[-- Type: text/x-patch; name="ethtool3-clearstats.patch", Size: 3685 bytes --]
--- ethtool-3/ethtool.c.b4phil 2006-04-29 19:01:18.000000000 -0700
+++ ethtool-3/ethtool.c 2006-04-29 19:01:36.000000000 -0700
@@ -66,6 +66,7 @@
static int do_goffload(int fd, struct ifreq *ifr);
static int do_soffload(int fd, struct ifreq *ifr);
static int do_gstats(int fd, struct ifreq *ifr);
+static int do_cstats(int fd, struct ifreq *ifr);
static int send_ioctl(int fd, struct ifreq *ifr);
static int check_for_pre24_kernel();
@@ -242,6 +243,7 @@
MODE_GOFFLOAD,
MODE_SOFFLOAD,
MODE_GSTATS,
+ MODE_CSTATS,
} mode = MODE_GSET;
static int goffload_changed = 0;
@@ -470,6 +472,8 @@
mode = MODE_TEST;
else if (!strcmp(argp[i], "-S"))
mode = MODE_GSTATS;
+ else if (!strcmp(argp[i], "-z"))
+ mode = MODE_CSTATS;
else if (!strcmp(argp[i], "-h"))
show_usage(0);
else
@@ -492,6 +496,7 @@
(mode == MODE_GOFFLOAD) ||
(mode == MODE_SOFFLOAD) ||
(mode == MODE_GSTATS) ||
+ (mode == MODE_CSTATS) ||
(mode == MODE_PHYS_ID)) {
devname = argp[i];
break;
@@ -1266,6 +1271,8 @@
return do_soffload(fd, &ifr);
} else if (mode == MODE_GSTATS) {
return do_gstats(fd, &ifr);
+ } else if (mode == MODE_CSTATS) {
+ return do_cstats(fd, &ifr);
}
return 69;
@@ -2010,6 +2017,79 @@
return 0;
}
+static int do_cstats(int fd, struct ifreq *ifr)
+{
+ struct ethtool_drvinfo drvinfo;
+ struct ethtool_gstrings *strings;
+ struct ethtool_stats *stats;
+ unsigned int n_stats, sz_str, sz_stats, i;
+ int err;
+
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ ifr->ifr_data = (caddr_t)&drvinfo;
+ err = send_ioctl(fd, ifr);
+ if (err < 0) {
+ perror("Cannot get driver information");
+ return 71;
+ }
+
+ n_stats = drvinfo.n_stats;
+ if (n_stats < 1) {
+ fprintf(stderr, "no stats available\n");
+ return 94;
+ }
+
+ sz_str = n_stats * ETH_GSTRING_LEN;
+ sz_stats = n_stats * sizeof(u64);
+
+ strings = calloc(1, sz_str + sizeof(struct ethtool_gstrings));
+ stats = calloc(1, sz_stats + sizeof(struct ethtool_stats));
+ if (!strings || !stats) {
+ fprintf(stderr, "no memory available\n");
+ return 95;
+ }
+
+ strings->cmd = ETHTOOL_GSTRINGS;
+ strings->string_set = ETH_SS_STATS;
+ strings->len = n_stats;
+ ifr->ifr_data = (caddr_t) strings;
+ err = send_ioctl(fd, ifr);
+ if (err < 0) {
+ perror("Cannot get stats strings information");
+ free(strings);
+ free(stats);
+ return 96;
+ }
+
+ stats->cmd = ETHTOOL_CSTATS;
+ stats->n_stats = n_stats;
+ ifr->ifr_data = (caddr_t) stats;
+ err = send_ioctl(fd, ifr);
+ if (err < 0) {
+ perror("Cannot get stats information");
+ free(strings);
+ free(stats);
+ return 97;
+ }
+
+ /* todo - pretty-print the strings per-driver */
+ /*
+ fprintf(stdout, "NIC statistics:\n");
+ for (i = 0; i < n_stats; i++) {
+ char s[ETH_GSTRING_LEN];
+
+ strncpy(s, &strings->data[i * ETH_GSTRING_LEN],
+ ETH_GSTRING_LEN);
+ fprintf(stdout, " %s: %llu\n",
+ s, stats->data[i]);
+ }
+ */
+ free(strings);
+ free(stats);
+
+ return 0;
+}
+
static int send_ioctl(int fd, struct ifreq *ifr)
{
int err;
--- ethtool-3/ethtool-copy.h.b4phil 2006-04-29 19:01:26.000000000 -0700
+++ ethtool-3/ethtool-copy.h 2006-04-29 19:01:36.000000000 -0700
@@ -283,6 +283,7 @@
#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */
#define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */
#define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */
+#define ETHTOOL_CSTATS 0x00000023 /* get NIC-specific statistics */
/* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 252 bytes --]
^ permalink raw reply
* RE: [PATCH/2.6.17-rc4 5/10] Add tsi108 Ethernet support
From: Zang Roy-r61911 @ 2006-05-18 3:58 UTC (permalink / raw)
To: jgarzik
Cc: Alexandre.Bounine, netdev, linux-kernel, linuxppc-dev list,
Paul Mackerras, Yang Xin-Xin-r48390
This patch adds Tundra tsi108 Ethernet driver support.
-----Original Message-----
From: Kumar Gala [mailto:galak@kernel.crashing.org]
Sent: 2006年5月17日 21:24
To: Zang Roy-r61911
Cc: Paul Mackerras; linuxppc-dev list; Alexandre.Bounine@tundra.com; Yang Xin-Xin-r48390
Subject: Re: [PATCH/2.6.17-rc4 5/10] Add tsi108 Ethernet support
On May 17, 2006, at 5:14 AM, Zang Roy-r61911 wrote:
> This patch adds a device driver and configuration options for
> Tundra Semiconductor Tsi108 integrated dual port Gigabit Ethernet
> controller
>
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
This patch needs to go to the netdev list and Jeff Garzik as
maintainer for review.
- kumar
> drivers/net/Kconfig | 8
> drivers/net/Makefile | 1
> drivers/net/tsi108_eth.c | 1740 +++++++++++++++++++++++++++++++++++
> +++++++++++
> drivers/net/tsi108_eth.h | 404 +++++++++++
> 4 files changed, 2153 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/tsi108_eth.c
> create mode 100644 drivers/net/tsi108_eth.h
>
> 23ed1c55ab6bc7a4c1c76f646579a1bc195bc7d2
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index bdaaad8..8a4ef29 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -2177,6 +2177,14 @@ config SPIDER_NET
> This driver supports the Gigabit Ethernet chips present on the
> Cell Processor-Based Blades from IBM.
>
> +config TSI108_ETH
> + tristate "Tundra TSI108 gigabit Ethernet support"
> + depends on TSI108_BRIDGE
> + help
> + This driver supports Tundra TSI108 gigabit Ethernet ports.
> + To compile this driver as a module, choose M here: the module
> + will be called tsi108_eth.
> +
> config GIANFAR
> tristate "Gianfar Ethernet"
> depends on 85xx || 83xx
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index b90468a..5f90b30 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -109,6 +109,7 @@ obj-$(CONFIG_B44) += b44.o
> obj-$(CONFIG_FORCEDETH) += forcedeth.o
> obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
>
> +obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
> obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
>
> obj-$(CONFIG_PPP) += ppp_generic.o slhc.o
> diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
> new file mode 100644
> index 0000000..cb67dbe
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.c
> @@ -0,0 +1,1740 @@
> +/
> **********************************************************************
> *********
> +
> + Copyright(c) 2005 Tundra Semiconductor Corporation.
> +
> + 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 Free
> + Software Foundation; either version 2 of the License, or (at
> your option)
> + any later version.
> +
> + This program is distributed in the hope that it will be useful,
> but WITHOUT
> + ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> License for
> + more details.
> +
> + You should have received a copy of the GNU General Public
> License along with
> + this program; if not, write to the Free Software Foundation,
> Inc., 59
> + Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> +
> +*********************************************************************
> **********/
> +
> +/* This driver is based on the driver code originally developed
> + * for the Intel IOC80314 (ForestLake) Gigabit Ethernet by
> + * scott.wood@timesys.com * Copyright (C) 2003 TimeSys Corporation
> + *
> + * Currently changes from original version are:
> + * - portig to Tsi108-based platform and kernel 2.6
> (kong.lai@tundra.com)
> + * - modifications to handle two ports independently and support for
> + * additional PHY devices (alexandre.bounine@tundra.com)
> + *
> + */
> +
> +#include <linux/config.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/init.h>
> +#include <linux/net.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/delay.h>
> +#include <linux/crc32.h>
> +#include <linux/mii.h>
> +#include <linux/device.h>
> +#include <asm/system.h>
> +#include <asm/io.h>
> +#include <linux/pci.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/timer.h>
> +
> +#include <asm/tsi108_irq.h>
> +#include <asm/tsi108.h>
> +#include "tsi108_eth.h"
> +
> +typedef struct net_device net_device;
> +typedef struct sk_buff sk_buff;
> +
> +#define MII_READ_DELAY 10000 /* max link wait time in msec */
> +
> +#define TSI108_RXRING_LEN 256
> +
> +/* NOTE: The driver currently does not support receiving packets
> + * larger than the buffer size, so don't decrease this (unless you
> + * want to add such support).
> + */
> +#define TSI108_RXBUF_SIZE 1536
> +
> +#define TSI108_TXRING_LEN 256
> +
> +#define TSI108_TX_INT_FREQ 64
> +
> +/* Check the phy status every half a second. */
> +#define CHECK_PHY_INTERVAL (HZ/2)
> +
> +extern hw_info hw_info_table[];
> +
> +typedef struct {
> + volatile u32 regs; /* Base of normal regs */
> + volatile u32 phyregs; /* Base of register bank used for PHY
> access */
> + int phy; /* Index of PHY for this interface */
> + int irq_num;
> +
> + struct timer_list timer;/* Timer that triggers the check phy
> function */
> + int rxtail; /* Next entry in rxring to read */
> + int rxhead; /* Next entry in rxring to give a new buffer */
> + int rxfree; /* Number of free, allocated RX buffers */
> +
> + int rxpending; /* Non-zero if there are still descriptors
> + * to be processed from a previous descriptor
> + * interrupt condition that has been cleared */
> +
> + int txtail; /* Next TX descriptor to check status on */
> + int txhead; /* Next TX descriptor to use */
> +
> + /* Number of free TX descriptors. This could be calculated from
> + * rxhead and rxtail if one descriptor were left unused to
> disambiguate
> + * full and empty conditions, but it's simpler to just keep track
> + * explicitly. */
> +
> + int txfree;
> +
> + int phy_ok; /* The PHY is currently powered on. */
> +
> + /* PHY status (duplex is 1 for half, 2 for full,
> + * so that the default 0 indicates that neither has
> + * yet been configured). */
> +
> + int link_up;
> + int speed;
> + int duplex;
> +
> + tx_desc *txring;
> + rx_desc *rxring;
> + sk_buff *txskbs[TSI108_TXRING_LEN];
> + sk_buff *rxskbs[TSI108_RXRING_LEN];
> +
> + dma_addr_t txdma, rxdma;
> +
> + /* txlock nests in misclock and phy_lock */
> +
> + spinlock_t txlock, misclock;
> +
> + /* stats is used to hold the upper bits of each hardware counter,
> + * and tmpstats is used to hold the full values for returning
> + * to the caller of get_stats(). They must be separate in case
> + * an overflow interrupt occurs before the stats are consumed.
> + */
> +
> + struct net_device_stats stats;
> + struct net_device_stats tmpstats;
> +
> + /* These stats are kept separate in hardware, thus require
> individual
> + * fields for handling carry. They are combined in get_stats.
> + */
> +
> + unsigned long rx_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_short_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_long_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_underruns; /* Add to rx_length_errors */
> + unsigned long rx_overruns; /* Add to rx_length_errors */
> +
> + unsigned long tx_coll_abort; /* Add to tx_aborted_errors/
> collisions */
> + unsigned long tx_pause_drop; /* Add to tx_aborted_errors */
> +
> + unsigned long mc_hash[16];
> +} tsi108_prv_data;
> +static void tsi108_timed_checker(unsigned long dev_ptr);
> +
> +static net_device *tsi108_devs[TSI108_ETH_PORT_NUM];
> +
> +static void dump_eth_one(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + printk("Dumping %s...\n", dev->name);
> + printk("intstat %x intmask %x phy_ok %d"
> + " link %d speed %d duplex %d\n",
> + TSI108_ETH_READ_REG(TSI108_EC_INTSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK), data->phy_ok,
> + data->link_up, data->speed, data->duplex);
> +
> + printk("TX: head %d, tail %d, free %d, stat %x, estat %x, err %x\n",
> + data->txhead, data->txtail, data->txfree,
> + TSI108_ETH_READ_REG(TSI108_EC_TXSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_TXESTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_TXERR));
> +
> + printk("RX: head %d, tail %d, free %d, stat %x,"
> + " estat %x, err %x, pending %d\n\n",
> + data->rxhead, data->rxtail, data->rxfree,
> + TSI108_ETH_READ_REG(TSI108_EC_RXSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_RXESTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_RXERR), data->rxpending);
> +}
> +
> +void tsi108_dump_eth(void)
> +{
> + dump_eth_one(tsi108_devs[0]);
> + dump_eth_one(tsi108_devs[1]);
> +}
> +
> +/* Synchronization is needed between the thread and up/down events.
> + * Note that the PHY is accessed through the same registers for both
> + * interfaces, so this can't be made interface-specific.
> + */
> +
> +static spinlock_t phy_lock = SPIN_LOCK_UNLOCKED;
> +
> +static inline u16 tsi108_read_mii(tsi108_prv_data * data, int reg,
> int *status)
> +{
> + int i;
> + u16 ret;
> +
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> + (data->phy << TSI108_MAC_MII_ADDR_PHY) |
> + (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, 0);
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD,
> TSI108_MAC_MII_CMD_READ);
> + mb();
> + for (i = 0; i < 100; i++) {
> + if (!(TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> + (TSI108_MAC_MII_IND_NOTVALID | TSI108_MAC_MII_IND_BUSY)))
> + break;
> + udelay(10);
> + }
> +
> + if (i == 100) {
> + if (status)
> + *status = -EBUSY;
> +
> + ret = 0xffff;
> + } else {
> + if (status)
> + *status = 0;
> +
> + ret = TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_DATAIN);
> + }
> +
> + return ret;
> +}
> +
> +static inline void tsi108_write_mii(tsi108_prv_data * data, int
> reg, u16 val)
> +{
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> + (data->phy << TSI108_MAC_MII_ADDR_PHY) |
> + (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_DATAOUT, val);
> + mb();
> + while (TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> + TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static inline void tsi108_write_tbi(tsi108_prv_data * data, int
> reg, u16 val)
> +{
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_ADDR,
> + (0x1e << TSI108_MAC_MII_ADDR_PHY)
> + | (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_DATAOUT, val);
> + mb();
> + while (TSI108_ETH_READ_REG(TSI108_MAC_MII_IND) &
> + TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static void tsi108_check_phy(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u16 sumstat;
> + u32 mac_cfg2_reg, portctrl_reg;
> + u32 fdx_flag = 0, reg_update = 0;
> +
> + /* Do a dummy read, as for some reason the first read
> + * after a link becomes up returns link down, even if
> + * it's been a while since the link came up.
> + */
> +
> + spin_lock(&phy_lock);
> +
> + if (!data->phy_ok)
> + goto out;
> +
> + tsi108_read_mii(data, PHY_STAT, NULL);
> +
> + if (!(tsi108_read_mii(data, PHY_STAT, NULL) & PHY_STAT_LINKUP)) {
> + if (data->link_up == 1) {
> + netif_stop_queue(dev);
> + data->link_up = 0;
> + printk(KERN_NOTICE "%s : link is down\n", dev->name);
> + netif_carrier_off(dev);
> + }
> +
> + goto out;
> + }
> +
> + {
> + mac_cfg2_reg = TSI108_ETH_READ_REG(TSI108_MAC_CFG2);
> + portctrl_reg = TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL);
> +
> + sumstat = tsi108_read_mii(data, PHY_SUM_STAT, NULL);
> +
> + switch (sumstat & PHY_SUM_STAT_SPEED_MASK) {
> + case PHY_SUM_STAT_1000T_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_1000T_HD:
> + if (data->speed != 1000) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |= TSI108_MAC_CFG2_GIG;
> + portctrl_reg &= ~TSI108_EC_PORTCTRL_NOGIG;
> + data->speed = 1000;
> + reg_update++;
> + }
> + break;
> + case PHY_SUM_STAT_100TX_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_100TX_HD:
> + if (data->speed != 100) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
> + portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
> + data->speed = 100;
> + reg_update++;
> + }
> + break;
> +
> + case PHY_SUM_STAT_10T_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_10T_HD:
> + if (data->speed != 10) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
> + portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
> + data->speed = 10;
> + reg_update++;
> + }
> + break;
> +
> + default:
> + if (net_ratelimit())
> + printk(KERN_ERR "PHY reported invalid speed,"
> + KERN_ERR " summary status %x\n",
> + sumstat);
> + goto out;
> + }
> +
> + if (fdx_flag || (sumstat & PHY_SUM_STAT_FULLDUPLEX)) {
> + if (data->duplex != 2) {
> + mac_cfg2_reg |= TSI108_MAC_CFG2_FULLDUPLEX;
> + portctrl_reg &= ~TSI108_EC_PORTCTRL_HALFDUPLEX;
> + data->duplex = 2;
> + reg_update++;
> + }
> + } else {
> + if (data->duplex != 1) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_FULLDUPLEX;
> + portctrl_reg |= TSI108_EC_PORTCTRL_HALFDUPLEX;
> + data->duplex = 1;
> + reg_update++;
> + }
> + }
> +
> + if (reg_update) {
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, mac_cfg2_reg);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, portctrl_reg);
> + mb();
> + }
> +
> + }
> +
> + if (data->link_up == 0) {
> + /* The manual says it can take 3-4 usecs for the speed change
> + * to take effect.
> + */
> + udelay(5);
> +
> + spin_lock(&data->txlock);
> + if (netif_queue_stopped(dev)
> + && is_valid_ether_addr(dev->dev_addr) && data->txfree)
> + netif_wake_queue(dev);
> +
> + data->link_up = 1;
> + spin_unlock(&data->txlock);
> + printk("%s : link is up: %dMb %s-duplex\n",
> + dev->name, data->speed,
> + (data->duplex == 2) ? "full" : "half");
> + netif_carrier_on(dev);
> + }
> +
> + out:
> + spin_unlock(&phy_lock);
> +}
> +
> +static inline void
> +tsi108_stat_carry_one(int carry, int carry_bit, int carry_shift,
> + unsigned long *upper)
> +{
> + if (carry & carry_bit)
> + *upper += carry_shift;
> +}
> +
> +static void tsi108_stat_carry(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 carry1, carry2;
> +
> + spin_lock_irq(&data->misclock);
> +
> + carry1 = TSI108_ETH_READ_REG(TSI108_STAT_CARRY1);
> + carry2 = TSI108_ETH_READ_REG(TSI108_STAT_CARRY2);
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY1, carry1);
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY2, carry2);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXBYTES,
> + TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXPKTS,
> + TSI108_STAT_RXPKTS_CARRY,
> + &data->stats.rx_packets);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFCS,
> + TSI108_STAT_RXFCS_CARRY, &data->rx_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXMCAST,
> + TSI108_STAT_RXMCAST_CARRY,
> + &data->stats.multicast);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXALIGN,
> + TSI108_STAT_RXALIGN_CARRY,
> + &data->stats.rx_frame_errors);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXLENGTH,
> + TSI108_STAT_RXLENGTH_CARRY,
> + &data->stats.rx_length_errors);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXRUNT,
> + TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJUMBO,
> + TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFRAG,
> + TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJABBER,
> + TSI108_STAT_RXJABBER_CARRY, &data->rx_long_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXDROP,
> + TSI108_STAT_RXDROP_CARRY,
> + &data->stats.rx_missed_errors);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXBYTES,
> + TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPKTS,
> + TSI108_STAT_TXPKTS_CARRY,
> + &data->stats.tx_packets);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXDEF,
> + TSI108_STAT_TXEXDEF_CARRY,
> + &data->stats.tx_aborted_errors);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXCOL,
> + TSI108_STAT_TXEXCOL_CARRY, &data->tx_coll_abort);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXTCOL,
> + TSI108_STAT_TXTCOL_CARRY,
> + &data->stats.collisions);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPAUSE,
> + TSI108_STAT_TXPAUSEDROP_CARRY,
> + &data->tx_pause_drop);
> +
> + spin_unlock_irq(&data->misclock);
> +}
> +
> +/* Read a stat counter atomically with respect to carries.
> + * data->misclock must be held.
> + */
> +static inline unsigned long
> +tsi108_read_stat(tsi108_prv_data * data, int reg, int carry_bit,
> + int carry_shift, unsigned long *upper)
> +{
> + int carryreg;
> + unsigned long val;
> +
> + if (reg < 0xb0)
> + carryreg = TSI108_STAT_CARRY1;
> + else
> + carryreg = TSI108_STAT_CARRY2;
> +
> + again:
> + val = TSI108_ETH_READ_REG(reg) | *upper;
> +
> + rmb();
> +
> + /* Check to see if it overflowed, but the interrupt hasn't
> + * been serviced yet. If so, handle the carry here, and
> + * try again.
> + */
> +
> + if (unlikely(TSI108_ETH_READ_REG(carryreg) & carry_bit)) {
> + *upper += carry_shift;
> + TSI108_ETH_WRITE_REG(carryreg, carry_bit);
> + mb();
> +
> + goto again;
> + }
> +
> + return val;
> +}
> +
> +static struct net_device_stats *tsi108_get_stats(net_device * dev)
> +{
> + unsigned long excol;
> +
> + tsi108_prv_data *data = netdev_priv(dev);
> + spin_lock_irq(&data->misclock);
> +
> + data->tmpstats.rx_packets =
> + tsi108_read_stat(data, TSI108_STAT_RXPKTS,
> + TSI108_STAT_CARRY1_RXPKTS,
> + TSI108_STAT_RXPKTS_CARRY, &data->stats.rx_packets);
> +
> + data->tmpstats.tx_packets =
> + tsi108_read_stat(data, TSI108_STAT_TXPKTS,
> + TSI108_STAT_CARRY2_TXPKTS,
> + TSI108_STAT_TXPKTS_CARRY, &data->stats.tx_packets);
> +
> + data->tmpstats.rx_bytes =
> + tsi108_read_stat(data, TSI108_STAT_RXBYTES,
> + TSI108_STAT_CARRY1_RXBYTES,
> + TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
> +
> + data->tmpstats.tx_bytes =
> + tsi108_read_stat(data, TSI108_STAT_TXBYTES,
> + TSI108_STAT_CARRY2_TXBYTES,
> + TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
> +
> + data->tmpstats.multicast =
> + tsi108_read_stat(data, TSI108_STAT_RXMCAST,
> + TSI108_STAT_CARRY1_RXMCAST,
> + TSI108_STAT_RXMCAST_CARRY, &data->stats.multicast);
> +
> + excol = tsi108_read_stat(data, TSI108_STAT_TXEXCOL,
> + TSI108_STAT_CARRY2_TXEXCOL,
> + TSI108_STAT_TXEXCOL_CARRY,
> + &data->tx_coll_abort);
> +
> + data->tmpstats.collisions =
> + tsi108_read_stat(data, TSI108_STAT_TXTCOL,
> + TSI108_STAT_CARRY2_TXTCOL,
> + TSI108_STAT_TXTCOL_CARRY, &data->stats.collisions);
> +
> + data->tmpstats.collisions += excol;
> +
> + data->tmpstats.rx_length_errors =
> + tsi108_read_stat(data, TSI108_STAT_RXLENGTH,
> + TSI108_STAT_CARRY1_RXLENGTH,
> + TSI108_STAT_RXLENGTH_CARRY,
> + &data->stats.rx_length_errors);
> +
> + data->tmpstats.rx_length_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXRUNT,
> + TSI108_STAT_CARRY1_RXRUNT,
> + TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
> +
> + data->tmpstats.rx_length_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXJUMBO,
> + TSI108_STAT_CARRY1_RXJUMBO,
> + TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
> +
> + data->tmpstats.rx_frame_errors =
> + tsi108_read_stat(data, TSI108_STAT_RXALIGN,
> + TSI108_STAT_CARRY1_RXALIGN,
> + TSI108_STAT_RXALIGN_CARRY,
> + &data->stats.rx_frame_errors);
> +
> + data->tmpstats.rx_frame_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXFCS,
> + TSI108_STAT_CARRY1_RXFCS, TSI108_STAT_RXFCS_CARRY,
> + &data->rx_fcs);
> +
> + data->tmpstats.rx_frame_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXFRAG,
> + TSI108_STAT_CARRY1_RXFRAG,
> + TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
> +
> + data->tmpstats.rx_missed_errors =
> + tsi108_read_stat(data, TSI108_STAT_RXDROP,
> + TSI108_STAT_CARRY1_RXDROP,
> + TSI108_STAT_RXDROP_CARRY,
> + &data->stats.rx_missed_errors);
> +
> + /* These three are maintained by software. */
> + data->tmpstats.rx_fifo_errors = data->stats.rx_fifo_errors;
> + data->tmpstats.rx_crc_errors = data->stats.rx_crc_errors;
> +
> + data->tmpstats.tx_aborted_errors =
> + tsi108_read_stat(data, TSI108_STAT_TXEXDEF,
> + TSI108_STAT_CARRY2_TXEXDEF,
> + TSI108_STAT_TXEXDEF_CARRY,
> + &data->stats.tx_aborted_errors);
> +
> + data->tmpstats.tx_aborted_errors +=
> + tsi108_read_stat(data, TSI108_STAT_TXPAUSEDROP,
> + TSI108_STAT_CARRY2_TXPAUSE,
> + TSI108_STAT_TXPAUSEDROP_CARRY,
> + &data->tx_pause_drop);
> +
> + data->tmpstats.tx_aborted_errors += excol;
> +
> + data->tmpstats.tx_errors = data->tmpstats.tx_aborted_errors;
> + data->tmpstats.rx_errors = data->tmpstats.rx_length_errors +
> + data->tmpstats.rx_crc_errors +
> + data->tmpstats.rx_frame_errors +
> + data->tmpstats.rx_fifo_errors + data->tmpstats.rx_missed_errors;
> +
> + spin_unlock_irq(&data->misclock);
> + return &data->tmpstats;
> +}
> +
> +static void tsi108_restart_rx(tsi108_prv_data * data, net_device *
> dev)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRHIGH,
> + TSI108_EC_RXQ_PTRHIGH_VALID);
> +
> + wmb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, TSI108_EC_RXCTRL_GO
> + | TSI108_EC_RXCTRL_QUEUE0);
> +}
> +
> +static void tsi108_restart_tx(tsi108_prv_data * data)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRHIGH,
> + TSI108_EC_TXQ_PTRHIGH_VALID);
> +
> + wmb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, TSI108_EC_TXCTRL_IDLEINT |
> + TSI108_EC_TXCTRL_GO | TSI108_EC_TXCTRL_QUEUE0);
> +}
> +
> +/* txlock must be held by caller, with IRQs disabled, and
> + * with permission to re-enable them when the lock is dropped.
> + */
> +static void tsi108_check_for_completed_tx(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int tx;
> + struct sk_buff *skb;
> + int release = 0;
> +
> + while (!data->txfree || data->txhead != data->txtail) {
> + tx = data->txtail;
> +
> + if (data->txring[tx].misc & TSI108_TX_OWN)
> + break;
> +
> + skb = data->txskbs[tx];
> +
> + if (!(data->txring[tx].misc & TSI108_TX_OK))
> + printk("%s: bad tx packet, misc %x\n",
> + dev->name, data->txring[tx].misc);
> +
> + data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
> + data->txfree++;
> +
> + if (data->txring[tx].misc & TSI108_TX_EOF) {
> + dev_kfree_skb_any(skb);
> + release++;
> + }
> + }
> +
> + if (release) {
> +
> + if (netif_queue_stopped(dev)
> + && is_valid_ether_addr(dev->dev_addr) && data->link_up)
> + netif_wake_queue(dev);
> + }
> +}
> +
> +static int tsi108_send_packet(sk_buff * skb, net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int frags = skb_shinfo(skb)->nr_frags + 1;
> + int i;
> +#ifdef FIXME_SG_CSUM_NOT_TESTED /* FIXME: Not supported now. */
> + long csstart;
> + long csum;
> +
> + csstart = skb->len - skb->data_len;
> + if (csstart > skb->len - skb->data_len)
> + BUG();
> + csum = 0;
> + if (csstart != skb->len)
> + csum = skb_checksum(skb, csstart, skb->len - csstart, 0);
> +#endif
> +
> + if (!data->phy_ok && net_ratelimit())
> + printk(KERN_ERR "%s: Transmit while PHY is down!\n", dev->name);
> +
> + if (!data->link_up) {
> + printk(KERN_ERR "%s: Transmit while link is down!\n",
> + dev->name);
> + netif_stop_queue(dev);
> + return 1;
> + }
> +
> + if (data->txfree < MAX_SKB_FRAGS + 1) {
> + netif_stop_queue(dev);
> +
> + if (net_ratelimit())
> + printk(KERN_ERR "%s: Transmit with full tx ring!\n",
> + dev->name);
> + return 1;
> + }
> +
> + if (data->txfree - frags < MAX_SKB_FRAGS + 1) {
> + netif_stop_queue(dev);
> + }
> +
> + spin_lock_irq(&data->txlock);
> +
> + for (i = 0; i < frags; i++) {
> + int misc = 0;
> + int tx = data->txhead;
> +
> + /* This is done to mark every TSI108_TX_INT_FREQ tx buffers with
> + * the interrupt bit. TX descriptor-complete interrupts are
> + * enabled when the queue fills up, and masked when there is
> + * still free space. This way, when saturating the outbound
> + * link, the tx interrupts are kept to a reasonable level.
> + * When the queue is not full, reclamation of skbs still occurs
> + * as new packets are transmitted, or on a queue-empty
> + * interrupt.
> + */
> +
> + if ((tx % TSI108_TX_INT_FREQ == 0) &&
> + ((TSI108_TXRING_LEN - data->txfree) >= TSI108_TX_INT_FREQ)
> + )
> + misc = TSI108_TX_INT;
> +
> + data->txskbs[tx] = skb;
> +
> + if (i == 0) {
> + data->txring[tx].buf0 = virt_to_phys(skb->data);
> + data->txring[tx].len = skb->len - skb->data_len;
> + misc |= TSI108_TX_SOF;
> + } else {
> + skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
> +
> + data->txring[tx].buf0 =
> + page_to_phys(frag->page) + frag->page_offset;
> + data->txring[tx].len = frag->size;
> + }
> +
> + if (i == frags - 1)
> + misc |= TSI108_TX_EOF;
> +
> +#ifdef TSI108_PRINT_TX_FRAME
> + {
> + int i;
> + printk("%s: Tx Frame contents (%d)\n", dev->name,
> + skb->len);
> + for (i = 0; i < skb->len; i++)
> + printk(" %2.2x", skb->data[i]);
> + printk(".\n");
> + }
> +#endif /* TSI108_PRINT_TX_FRAME */
> +
> + mb();
> + data->txring[tx].misc = misc | TSI108_TX_OWN;
> +
> + data->txhead = (data->txhead + 1) % TSI108_TXRING_LEN;
> + data->txfree--;
> + }
> +
> + tsi108_check_for_completed_tx(dev);
> +
> + /* This must be done after the check for completed tx descriptors,
> + * so that the tail pointer is correct.
> + */
> +
> + if (!(TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) &
> TSI108_EC_TXSTAT_QUEUE0))
> + tsi108_restart_tx(data);
> +
> + spin_unlock_irq(&data->txlock);
> + return 0;
> +}
> +
> +static int tsi108_check_for_completed_rx(net_device * dev, int
> budget)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int done = 0;
> +
> + while (data->rxfree && done != budget) {
> + int rx = data->rxtail;
> + struct sk_buff *skb;
> +
> + if (data->rxring[rx].misc & TSI108_RX_OWN)
> + break;
> +
> + skb = data->rxskbs[rx];
> + data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
> + data->rxfree--;
> + done++;
> +
> + if (data->rxring[rx].misc & TSI108_RX_BAD) {
> + spin_lock_irq(&data->misclock);
> +
> + if (data->rxring[rx].misc & TSI108_RX_CRC)
> + data->stats.rx_crc_errors++;
> + if (data->rxring[rx].misc & TSI108_RX_OVER)
> + data->stats.rx_fifo_errors++;
> +
> + spin_unlock_irq(&data->misclock);
> +
> + dev_kfree_skb_any(skb);
> + continue;
> + }
> +#ifdef TSI108_PRINT_RX_FRAME
> + {
> + int i;
> + printk("%s: Rx Frame contents (%d)\n",
> + dev->name, data->rxring[rx].len);
> + for (i = 0; i < data->rxring[rx].len; i++)
> + printk(" %2.2x", skb->data[i]);
> + printk(".\n");
> + }
> +#endif /* TSI108_PRINT_RX_FRAME */
> +
> + skb->dev = dev;
> + skb_put(skb, data->rxring[rx].len);
> + skb->protocol = eth_type_trans(skb, dev);
> + netif_receive_skb(skb);
> + dev->last_rx = jiffies;
> + }
> +
> + return done;
> +}
> +
> +static int tsi108_refill_rx(net_device * dev, int budget)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int done = 0;
> +
> + while (data->rxfree != TSI108_RXRING_LEN && done != budget) {
> + int rx = data->rxhead;
> + sk_buff *skb;
> +
> + data->rxskbs[rx] = skb = dev_alloc_skb(TSI108_RXBUF_SIZE + 2);
> + if (!skb)
> + break;
> +
> + skb_reserve(skb, 2); /* Align the data on a 4-byte boundary. */
> +
> + data->rxring[rx].buf0 = virt_to_phys(skb->data);
> +
> + /* Sometimes the hardware sets blen to zero after packet
> + * reception, even though the manual says that it's only ever
> + * modified by the driver.
> + */
> +
> + data->rxring[rx].blen = 1536;
> + mb();
> + data->rxring[rx].misc = TSI108_RX_OWN | TSI108_RX_INT;
> +
> + data->rxhead = (data->rxhead + 1) % TSI108_RXRING_LEN;
> + data->rxfree++;
> + done++;
> + }
> +
> + mb();
> +
> + if (done != 0 && !(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_QUEUE0))
> + tsi108_restart_rx(data, dev);
> +
> + return done;
> +}
> +
> +static int tsi108_poll(net_device * dev, int *budget)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 estat = TSI108_ETH_READ_REG(TSI108_EC_RXESTAT);
> + u32 intstat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> + int total_budget = min(*budget, dev->quota);
> + int num_received = 0, num_filled = 0, budget_used;
> +
> + intstat &= TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR | TSI108_INT_RXWAIT;
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXESTAT, estat);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, intstat);
> +
> + if (data->rxpending || (estat & TSI108_EC_RXESTAT_Q0_DESCINT))
> + num_received = tsi108_check_for_completed_rx(dev, total_budget);
> +
> + /* This should normally fill no more slots than the number of
> + * packets received in tsi108_check_for_completed_rx(). The
> exception
> + * is when we previously ran out of memory for RX SKBs. In that
> + * case, it's helpful to obey the budget, not only so that the
> + * CPU isn't hogged, but so that memory (which may still be low)
> + * is not hogged by one device.
> + *
> + * A work unit is considered to be two SKBs to allow us to catch
> + * up when the ring has shrunk due to out-of-memory but we're
> + * still removing the full budget's worth of packets each time.
> + */
> +
> + if (data->rxfree < TSI108_RXRING_LEN)
> + num_filled = tsi108_refill_rx(dev, total_budget * 2);
> +
> + if (intstat & TSI108_INT_RXERROR) {
> + u32 err = TSI108_ETH_READ_REG(TSI108_EC_RXERR);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXERR, err);
> +
> + if (err) {
> + if (net_ratelimit())
> + printk(KERN_DEBUG "%s: RX error %x\n",
> + dev->name, err);
> +
> + if (!(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_QUEUE0))
> + tsi108_restart_rx(data, dev);
> + }
> + }
> +
> + if (intstat & TSI108_INT_RXOVERRUN) {
> + spin_lock_irq(&data->misclock);
> + data->stats.rx_fifo_errors++;
> + spin_unlock_irq(&data->misclock);
> + }
> +
> + budget_used = max(num_received, num_filled / 2);
> +
> + *budget -= budget_used;
> + dev->quota -= budget_used;
> +
> + if (budget_used != total_budget) {
> + data->rxpending = 0;
> + netif_rx_complete(dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK)
> + & ~(TSI108_INT_RXQUEUE0
> + | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT));
> +
> + mb();
> +
> + /* IRQs are level-triggered, so no need to re-check */
> + return 0;
> + } else {
> + data->rxpending = 1;
> + }
> +
> + return 1;
> +}
> +
> +static void tsi108_rx_int(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + /* A race could cause dev to already be scheduled, so it's not an
> + * error if that happens (and interrupts shouldn't be re-masked,
> + * because that can cause harmful races, if poll has already
> + * unmasked them but not cleared LINK_STATE_SCHED).
> + *
> + * This can happen if this code races with tsi108_poll(), which
> masks
> + * the interrupts after tsi108_irq_one() read the mask, but before
> + * netif_rx_schedule is called. It could also happen due to calls
> + * from tsi108_check_rxring().
> + */
> +
> + if (netif_rx_schedule_prep(dev)) {
> + /* Mask, rather than ack, the receive interrupts. The ack
> + * will happen in tsi108_poll().
> + */
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK) |
> + TSI108_INT_RXQUEUE0
> + | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT);
> + mb();
> + __netif_rx_schedule(dev);
> + } else {
> + if (!netif_running(dev)) {
> + /* This can happen if an interrupt occurs while the
> + * interface is being brought down, as the START
> + * bit is cleared before the stop function is called.
> + *
> + * In this case, the interrupts must be masked, or
> + * they will continue indefinitely.
> + *
> + * There's a race here if the interface is brought down
> + * and then up in rapid succession, as the device could
> + * be made running after the above check and before
> + * the masking below. This will only happen if the IRQ
> + * thread has a lower priority than the task brining
> + * up the interface. Fixing this race would likely
> + * require changes in generic code.
> + */
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG
> + (TSI108_EC_INTMASK) |
> + TSI108_INT_RXQUEUE0 |
> + TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT);
> + mb();
> + }
> + }
> +}
> +
> +/* If the RX ring has run out of memory, try periodically
> + * to allocate some more, as otherwise poll would never
> + * get called (apart from the initial end-of-queue condition).
> + *
> + * This is called once per second (by default) from the thread.
> + */
> +
> +static void tsi108_check_rxring(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + /* A poll is scheduled, as opposed to caling tsi108_refill_rx
> + * directly, so as to keep the receive path single-threaded
> + * (and thus not needing a lock).
> + */
> +
> + if (netif_running(dev) && data->rxfree < TSI108_RXRING_LEN / 4)
> + tsi108_rx_int(dev);
> +}
> +
> +static void tsi108_tx_int(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 estat = TSI108_ETH_READ_REG(TSI108_EC_TXESTAT);
> +
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXESTAT, estat);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_TXQUEUE0 |
> + TSI108_INT_TXIDLE | TSI108_INT_TXERROR);
> + mb();
> + if (estat & TSI108_EC_TXESTAT_Q0_ERR) {
> + u32 err = TSI108_ETH_READ_REG(TSI108_EC_TXERR);
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXERR, err);
> +
> + if (err && net_ratelimit())
> + printk(KERN_ERR "%s: TX error %x\n", dev->name, err);
> + }
> +
> + if (estat & (TSI108_EC_TXESTAT_Q0_DESCINT |
> TSI108_EC_TXESTAT_Q0_EOQ)) {
> + spin_lock(&data->txlock);
> + tsi108_check_for_completed_tx(dev);
> + spin_unlock(&data->txlock);
> + }
> +}
> +
> +static irqreturn_t tsi108_irq_one(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 stat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> +
> + if (!(stat & TSI108_INT_ANY))
> + return IRQ_NONE; /* Not our interrupt */
> +
> + stat &= ~TSI108_ETH_READ_REG(TSI108_EC_INTMASK);
> +
> + if (stat & (TSI108_INT_TXQUEUE0 | TSI108_INT_TXIDLE |
> + TSI108_INT_TXERROR))
> + tsi108_tx_int(dev);
> + if (stat & (TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXWAIT | TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR))
> + tsi108_rx_int(dev);
> +
> + if (stat & TSI108_INT_SFN) {
> + if (net_ratelimit())
> + printk(KERN_DEBUG "%s: SFN error\n", dev->name);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_SFN);
> + }
> +
> + if (stat & TSI108_INT_STATCARRY) {
> + tsi108_stat_carry(dev);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_STATCARRY);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t tsi108_irq(int irq, void *dev_id, struct
> pt_regs *regs)
> +{
> + if ((IRQ_TSI108_GIGE0 != irq) && (IRQ_TSI108_GIGE1 != irq))
> + return IRQ_NONE; /* Not our interrupt */
> +
> + return tsi108_irq_one((struct net_device *)dev_id);
> +}
> +
> +static void tsi108_stop_ethernet(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + /* Disable all TX and RX queues ... */
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, 0);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, 0);
> +
> + /* ...and wait for them to become idle */
> + mb();
> + while (TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) &
> + TSI108_EC_TXSTAT_ACTIVE) ;
> + while (TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_ACTIVE) ;
> +}
> +
> +static void tsi108_reset_ether(tsi108_prv_data * data)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, TSI108_MAC_CFG1_SOFTRST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL,
> TSI108_EC_PORTCTRL_STATRST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL,
> + TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL) &
> + ~TSI108_EC_PORTCTRL_STATRST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG, TSI108_EC_TXCFG_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG,
> + TSI108_ETH_READ_REG(TSI108_EC_TXCFG) &
> + ~TSI108_EC_TXCFG_RST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, TSI108_EC_RXCFG_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> + TSI108_ETH_READ_REG(TSI108_EC_RXCFG) &
> + ~TSI108_EC_RXCFG_RST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> + TSI108_MAC_MII_MGMT_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) &
> + ~(TSI108_MAC_MII_MGMT_RST |
> + TSI108_MAC_MII_MGMT_CLK));
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> + TSI108_MAC_MII_MGMT_CLK);
> +}
> +
> +static int tsi108_get_mac(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + u32 word1 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR1);
> + u32 word2 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR2);
> +
> + /* Note that the octets are reversed from what the manual says,
> + * producing an even weirder ordering...
> + */
> + if (word2 == 0 && word1 == 0) {
> + dev->dev_addr[0] = 0x00;
> + dev->dev_addr[1] = 0x06;
> + dev->dev_addr[2] = 0xd2;
> + dev->dev_addr[3] = 0x00;
> + dev->dev_addr[4] = 0x00;
> + if (0x8 == data->phy)
> + dev->dev_addr[5] = 0x01;
> + else
> + dev->dev_addr[5] = 0x02;
> +
> + word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
> +
> + word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
> + (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> + } else {
> + dev->dev_addr[0] = (word2 >> 16) & 0xff;
> + dev->dev_addr[1] = (word2 >> 24) & 0xff;
> + dev->dev_addr[2] = (word1 >> 0) & 0xff;
> + dev->dev_addr[3] = (word1 >> 8) & 0xff;
> + dev->dev_addr[4] = (word1 >> 16) & 0xff;
> + dev->dev_addr[5] = (word1 >> 24) & 0xff;
> + }
> +
> + if (!is_valid_ether_addr(dev->dev_addr)) {
> + printk("KERN_ERR: word1: %08x, word2: %08x\n", word1, word2);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int tsi108_set_mac(net_device * dev, void *addr)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 word1, word2;
> + int i;
> +
> + if (!is_valid_ether_addr(addr))
> + return -EINVAL;
> +
> + for (i = 0; i < 6; i++)
> + /* +2 is for the offset of the HW addr type */
> + dev->dev_addr[i] = ((unsigned char *)addr)[i + 2];
> +
> + word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
> +
> + word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
> + (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> + spin_lock_irq(&data->misclock);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> + spin_lock(&data->txlock);
> +
> + if (netif_queue_stopped(dev) && data->txfree && data->link_up)
> + netif_wake_queue(dev);
> +
> + spin_unlock(&data->txlock);
> + spin_unlock_irq(&data->misclock);
> + return 0;
> +}
> +
> +/* Protected by dev->xmit_lock. */
> +static void tsi108_set_rx_mode(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 rxcfg = TSI108_ETH_READ_REG(TSI108_EC_RXCFG);
> +
> + if (dev->flags & IFF_PROMISC) {
> + rxcfg &= ~(TSI108_EC_RXCFG_UC_HASH | TSI108_EC_RXCFG_MC_HASH);
> + rxcfg |= TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE;
> + goto out;
> + }
> +
> + rxcfg &= ~(TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE);
> +
> + if (dev->mc_count) {
> + int i;
> + struct dev_mc_list *mc = dev->mc_list;
> + rxcfg |= TSI108_EC_RXCFG_MFE | TSI108_EC_RXCFG_MC_HASH;
> +
> + memset(data->mc_hash, 0, sizeof(data->mc_hash));
> +
> + while (mc) {
> + u32 hash, crc;
> +
> + if (mc->dmi_addrlen == 6) {
> + crc = ether_crc(6, mc->dmi_addr);
> + hash = crc >> 23;
> +
> + __set_bit(hash, &data->mc_hash[0]);
> + } else {
> + printk(KERN_ERR
> + "%s: got multicast address of length %d "
> + "instead of 6.\n", dev->name,
> + mc->dmi_addrlen);
> + }
> +
> + mc = mc->next;
> + }
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_HASHADDR,
> + TSI108_EC_HASHADDR_AUTOINC |
> + TSI108_EC_HASHADDR_MCAST);
> +
> + for (i = 0; i < 16; i++) {
> + /* The manual says that the hardware may drop
> + * back-to-back writes to the data register.
> + */
> + udelay(1);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_HASHDATA,
> + data->mc_hash[i]);
> + mb();
> + }
> + }
> +
> + out:
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, rxcfg);
> +}
> +
> +static void tsi108_init_phy(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 i = 0;
> + u16 phyVal = 0;
> +
> + spin_lock_irq(&phy_lock);
> +
> + tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_RESET);
> + mb();
> + while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_RESET) ;
> +
> +#if (TSI108_PHY_TYPE == PHY_BCM54XX) /* Broadcom BCM54xx PHY */
> + tsi108_write_mii(data, 0x09, 0x0300);
> + tsi108_write_mii(data, 0x10, 0x1020);
> + tsi108_write_mii(data, 0x1c, 0x8c00);
> + mb();
> +#endif
> +
> + tsi108_write_mii(data,
> + PHY_CTRL,
> + PHY_CTRL_AUTONEG_EN | PHY_CTRL_AUTONEG_START);
> + mb();
> + while (tsi108_read_mii(data, PHY_CTRL, NULL) &
> PHY_CTRL_AUTONEG_START) ;
> +
> + /* Set G/MII mode and receive clock select in TBI control #2. The
> + * second port won't work if this isn't done, even though we don't
> + * use TBI mode.
> + */
> +
> + tsi108_write_tbi(data, 0x11, 0x30);
> +
> + /* FIXME: It seems to take more than 2 back-to-back reads to the
> + * PHY_STAT register before the link up status bit is set.
> + */
> +
> + data->link_up = 1;
> +
> + while (!((phyVal = tsi108_read_mii(data, PHY_STAT, NULL)) &
> + PHY_STAT_LINKUP)) {
> + if (i++ > (MII_READ_DELAY / 10)) {
> + data->link_up = 0;
> + break;
> + }
> + mdelay(10);
> + }
> +
> + printk(KERN_DEBUG "PHY_STAT reg contains %08x\n", phyVal);
> + data->phy_ok = 1;
> + spin_unlock_irq(&phy_lock);
> +}
> +
> +static void tsi108_kill_phy(struct net_device *dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + spin_lock_irq(&phy_lock);
> + tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_POWERDOWN);
> + data->phy_ok = 0;
> + spin_unlock_irq(&phy_lock);
> +}
> +
> +static int tsi108_open(struct net_device *dev)
> +{
> + int i;
> + tsi108_prv_data *data = netdev_priv(dev);
> + unsigned int rxring_size = TSI108_RXRING_LEN * sizeof(rx_desc);
> + unsigned int txring_size = TSI108_TXRING_LEN * sizeof(tx_desc);
> +
> + printk(KERN_DEBUG "Inside tsi108_open()!\n");
> +
> + i = request_irq(data->irq_num, tsi108_irq, 0, dev->name, dev);
> + if (i != 0) {
> + printk(KERN_ERR "tsi108_eth%d: Could not allocate IRQ%d.\n",
> + data->irq_num % IRQ_TSI108_GIGE0, data->irq_num);
> + return i;
> + } else {
> + dev->irq = data->irq_num;
> + printk(KERN_NOTICE
> + "tsi108_open : Port %d Assigned IRQ %d to %s\n",
> + data->irq_num % IRQ_TSI108_GIGE0, dev->irq, dev->name);
> + }
> +
> + data->rxring = pci_alloc_consistent(NULL, rxring_size, &data-
> >rxdma);
> +
> + if (!data->rxring) {
> + printk(KERN_DEBUG
> + "TSI108_ETH: failed to allocate memory for rxring!\n");
> + return -ENOMEM;
> + } else {
> + memset(data->rxring, 0, rxring_size);
> + }
> +
> + data->txring = pci_alloc_consistent(NULL, txring_size, &data-
> >txdma);
> +
> + if (!data->txring) {
> + printk(KERN_DEBUG
> + "TSI108_ETH: failed to allocate memory for txring!\n");
> + pci_free_consistent(0, rxring_size, data->rxring, data->rxdma);
> + return -ENOMEM;
> + } else {
> + memset(data->txring, 0, txring_size);
> + }
> +
> + for (i = 0; i < TSI108_RXRING_LEN; i++) {
> + data->rxring[i].next0 = data->rxdma + (i + 1) * sizeof(rx_desc);
> + data->rxring[i].blen = TSI108_RXBUF_SIZE;
> + data->rxring[i].vlan = 0;
> + }
> +
> + data->rxring[TSI108_RXRING_LEN - 1].next0 = data->rxdma;
> +
> + data->rxtail = 0;
> + data->rxhead = 0;
> +
> + for (i = 0; i < TSI108_RXRING_LEN; i++) {
> + sk_buff *skb = dev_alloc_skb(TSI108_RXBUF_SIZE + NET_IP_ALIGN);
> +
> + if (!skb) {
> + /* Bah. No memory for now, but maybe we'll get
> + * some more later.
> + * For now, we'll live with the smaller ring.
> + */
> + printk(KERN_WARNING
> + "%s: Could only allocate %d receive skb(s).\n",
> + dev->name, i);
> + data->rxhead = i;
> + break;
> + }
> +
> + data->rxskbs[i] = skb;
> + /* Align the payload on a 4-byte boundary */
> + skb_reserve(skb, 2);
> + data->rxskbs[i] = skb;
> + data->rxring[i].buf0 = virt_to_phys(data->rxskbs[i]->data);
> + data->rxring[i].misc = TSI108_RX_OWN | TSI108_RX_INT;
> + }
> +
> + data->rxfree = i;
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRLOW, data->rxdma);
> +
> + for (i = 0; i < TSI108_TXRING_LEN; i++) {
> + data->txring[i].next0 = data->txdma + (i + 1) * sizeof(tx_desc);
> + data->txring[i].misc = 0;
> + }
> +
> + data->txring[TSI108_TXRING_LEN - 1].next0 = data->txdma;
> + data->txtail = 0;
> + data->txhead = 0;
> + data->txfree = TSI108_TXRING_LEN;
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRLOW, data->txdma);
> + tsi108_init_phy(dev);
> +
> + init_timer(&data->timer);
> + data->timer.expires = jiffies + 1;
> + data->timer.data = (unsigned long)dev;
> + data->timer.function = &tsi108_timed_checker; /* timer handler */
> + add_timer(&data->timer);
> +
> + tsi108_restart_rx(data, dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, ~0);
> + mb();
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + ~(TSI108_INT_TXQUEUE0 | TSI108_INT_RXERROR |
> + TSI108_INT_RXTHRESH | TSI108_INT_RXQUEUE0 |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXWAIT |
> + TSI108_INT_SFN | TSI108_INT_STATCARRY));
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1,
> + TSI108_MAC_CFG1_RXEN | TSI108_MAC_CFG1_TXEN);
> + netif_start_queue(dev);
> + return 0;
> +}
> +
> +static int tsi108_close(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + netif_stop_queue(dev);
> +
> + del_timer_sync(&data->timer);
> +
> + printk(KERN_DEBUG "Inside tsi108_ifdown!\n");
> +
> + tsi108_stop_ethernet(dev);
> + tsi108_kill_phy(dev);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + /* Check for any pending TX packets, and drop them. */
> +
> + while (!data->txfree || data->txhead != data->txtail) {
> + int tx = data->txtail;
> + struct sk_buff *skb;
> + skb = data->txskbs[tx];
> + data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
> + data->txfree++;
> + dev_kfree_skb(skb);
> + }
> +
> + synchronize_irq(data->irq_num);
> + free_irq(data->irq_num, dev);
> +
> + /* Discard the RX ring. */
> +
> + while (data->rxfree) {
> + int rx = data->rxtail;
> + struct sk_buff *skb;
> +
> + skb = data->rxskbs[rx];
> + data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
> + data->rxfree--;
> + dev_kfree_skb(skb);
> + }
> +
> + pci_free_consistent(0,
> + TSI108_RXRING_LEN * sizeof(rx_desc),
> + data->rxring, data->rxdma);
> + pci_free_consistent(0,
> + TSI108_TXRING_LEN * sizeof(tx_desc),
> + data->txring, data->txdma);
> +
> + return 0;
> +}
> +
> +static void tsi108_init_mac(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2,
> TSI108_MAC_CFG2_DFLT_PREAMBLE |
> + TSI108_MAC_CFG2_PADCRC);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXTHRESH,
> + (192 << TSI108_EC_TXTHRESH_STARTFILL) |
> + (192 << TSI108_EC_TXTHRESH_STOPFILL));
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK1,
> + ~(TSI108_STAT_CARRY1_RXBYTES |
> + TSI108_STAT_CARRY1_RXPKTS |
> + TSI108_STAT_CARRY1_RXFCS |
> + TSI108_STAT_CARRY1_RXMCAST |
> + TSI108_STAT_CARRY1_RXALIGN |
> + TSI108_STAT_CARRY1_RXLENGTH |
> + TSI108_STAT_CARRY1_RXRUNT |
> + TSI108_STAT_CARRY1_RXJUMBO |
> + TSI108_STAT_CARRY1_RXFRAG |
> + TSI108_STAT_CARRY1_RXJABBER |
> + TSI108_STAT_CARRY1_RXDROP));
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK2,
> + ~(TSI108_STAT_CARRY2_TXBYTES |
> + TSI108_STAT_CARRY2_TXPKTS |
> + TSI108_STAT_CARRY2_TXEXDEF |
> + TSI108_STAT_CARRY2_TXEXCOL |
> + TSI108_STAT_CARRY2_TXTCOL |
> + TSI108_STAT_CARRY2_TXPAUSE));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, TSI108_EC_PORTCTRL_STATEN);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> + TSI108_EC_RXCFG_SE | TSI108_EC_RXCFG_BFE);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_CFG, TSI108_EC_TXQ_CFG_DESC_INT |
> + TSI108_EC_TXQ_CFG_EOQ_OWN_INT |
> + TSI108_EC_TXQ_CFG_WSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_TXQ_CFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_CFG, TSI108_EC_RXQ_CFG_DESC_INT |
> + TSI108_EC_RXQ_CFG_EOQ_OWN_INT |
> + TSI108_EC_RXQ_CFG_WSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_RXQ_CFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_BUFCFG,
> + TSI108_EC_TXQ_BUFCFG_BURST256 |
> + TSI108_EC_TXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_TXQ_BUFCFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_BUFCFG,
> + TSI108_EC_RXQ_BUFCFG_BURST256 |
> + TSI108_EC_RXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_RXQ_BUFCFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> +}
> +
> +static int tsi108_ioctl(net_device * dev, struct ifreq *rq, int cmd)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + struct mii_ioctl_data *mii_data =
> + (struct mii_ioctl_data *)&rq->ifr_data;
> + int ret;
> +
> + switch (cmd) {
> + case SIOCGMIIPHY:
> + mii_data->phy_id = data->phy;
> + ret = 0;
> + break;
> +
> + case SIOCGMIIREG:
> + spin_lock_irq(&phy_lock);
> + mii_data->val_out =
> + tsi108_read_mii(data, mii_data->reg_num, &ret);
> + spin_unlock_irq(&phy_lock);
> + break;
> +
> + default:
> + ret = -EOPNOTSUPP;
> + }
> +
> + return ret;
> +}
> +
> +static int
> +tsi108_init_one(unsigned long regs, unsigned long phyregs, u16
> phy, u16 irq_num)
> +{
> + net_device *dev = alloc_etherdev(sizeof(tsi108_prv_data));
> + tsi108_prv_data *data;
> + int ret;
> +
> + if (!dev) {
> + printk("tsi108_eth: Could not allocate a device structure\n");
> + return -ENOMEM;
> + }
> +
> + data = netdev_priv(dev);
> + memset(data, 0, sizeof(tsi108_prv_data));
> +
> + data->regs = (volatile u32)regs;
> + data->phyregs = (volatile u32)phyregs;
> + data->phy = phy;
> + data->irq_num = irq_num;
> +
> + dev->open = tsi108_open;
> + dev->stop = tsi108_close;
> + dev->hard_start_xmit = tsi108_send_packet;
> + dev->set_mac_address = tsi108_set_mac;
> + dev->set_multicast_list = tsi108_set_rx_mode;
> + dev->get_stats = tsi108_get_stats;
> + dev->poll = tsi108_poll;
> + dev->do_ioctl = tsi108_ioctl;
> + dev->weight = 64; /* 64 is more suitable for GigE interface -
> klai */
> +
> + /* Apparently, the Linux networking code won't use scatter-gather
> + * if the hardware doesn't do checksums. However, it's faster
> + * to checksum in place and use SG, as (among other reasons)
> + * the cache won't be dirtied (which then has to be flushed
> + * before DMA). The checksumming is done by the driver (via
> + * a new function skb_csum_dev() in net/core/skbuff.c).
> + */
> +
> +#ifdef FIXME_SG_CSUM_NOT_TESTED /* FIXME: Not supported now. */
> + dev->features = NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_HW_CSUM;
> +#else
> + dev->features = NETIF_F_HIGHDMA;
> +#endif
> + SET_MODULE_OWNER(dev);
> +
> + spin_lock_init(&data->txlock);
> + spin_lock_init(&data->misclock);
> +
> + tsi108_reset_ether(data);
> + tsi108_kill_phy(dev);
> +
> + if (tsi108_get_mac(dev) != 0)
> + printk(KERN_ERR "%s: Invalid MAC address. Please correct.\n",
> + dev->name);
> +
> + tsi108_init_mac(dev);
> +
> + tsi108_devs[irq_num % IRQ_TSI108_GIGE0] = dev;
> +
> + ret = register_netdev(dev);
> + if (ret < 0) {
> + kfree(dev);
> + return ret;
> + }
> +
> + printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: "
> + "%02x:%02x:%02x:%02x:%02x:%02x\n", dev->name,
> + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
> + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
> +
> + return 0;
> +}
> +
> +/* There's no way to either get interrupts from the PHY when
> + * something changes, or to have the Tsi108 automatically communicate
> + * with the PHY to reconfigure itself.
> + *
> + * Thus, we have to do it using a timer.
> + */
> +
> +static void tsi108_timed_checker(unsigned long dev_ptr)
> +{
> + struct net_device *dev = (struct net_device *)dev_ptr;
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + tsi108_check_phy(dev);
> + tsi108_check_rxring(dev);
> + data->timer.expires = jiffies + CHECK_PHY_INTERVAL;
> + add_timer(&data->timer);
> +}
> +
> +static int tsi108_ether_init(void)
> +{
> + int ret;
> + int dev_count = 0;
> + int i;
> +
> + hw_info_table[0].regs = (u32) ioremap(hw_info_table[0].regs, 0x400);
> + hw_info_table[0].phyregs = hw_info_table[0].regs;
> +
> + hw_info_table[1].regs = (u32) ioremap(hw_info_table[1].regs, 0x400);
> + hw_info_table[1].phyregs = hw_info_table[0].regs;
> +
> + for (i = 0; hw_info_table[i].regs != TBL_END; i++) {
> + ret = tsi108_init_one(hw_info_table[i].regs,
> + hw_info_table[i].phyregs,
> + hw_info_table[i].phy,
> + hw_info_table[i].irq_num);
> + if (ret < 0)
> + printk("tsi108_ether_init: error initializing ethernet "
> + "device%d\n", i);
> + else
> + dev_count++;
> + }
> +
> + printk("tsi108_ether_init: found %d device(s)\n", dev_count);
> +
> + return 0;
> +}
> +
> +static void tsi108_ether_exit(void)
> +{
> + int i;
> + net_device *dev;
> +
> + for (i = 0; hw_info_table[i].regs != TBL_END; i++) {
> + if ((dev = tsi108_devs[i]) != NULL) {
> + unregister_netdev(dev);
> + tsi108_stop_ethernet(dev);
> + kfree(dev);
> + tsi108_devs[i] = NULL;
> + }
> + }
> +}
> +
> +module_init(tsi108_ether_init);
> +module_exit(tsi108_ether_exit);
> +
> +MODULE_AUTHOR("Tundra Semiconductor Corporation");
> +MODULE_DESCRIPTION("Tsi108 Gigabit Ethernet driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/tsi108_eth.h b/drivers/net/tsi108_eth.h
> new file mode 100644
> index 0000000..cb54f92
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.h
> @@ -0,0 +1,404 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Kong Lai, <kong.lai@tundra.com).
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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 Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * net/tsi108_eth.h - definitions for Tsi108 GIGE network controller.
> + */
> +
> +#ifndef __TSI108_ETH_H
> +#define __TSI108_ETH_H
> +
> +#include <linux/config.h>
> +#include <linux/types.h>
> +
> +#define TSI108_ETH_WRITE_REG(offset, val) \
> + out_be32((volatile u32 *)(data->regs + offset), val)
> +
> +#define TSI108_ETH_READ_REG(offset) \
> + in_be32((volatile u32 *)(data->regs + offset))
> +
> +#define TSI108_ETH_WRITE_PHYREG(offset, val) \
> + out_be32((volatile u32 *)(data->phyregs + offset), val)
> +
> +#define TSI108_ETH_READ_PHYREG(offset) \
> + in_be32((volatile u32 *)(data->phyregs + offset))
> +
> +/*
> + * PHY Configuration Options
> + *
> + * NOTE: Enable set of definitions corresponding to your board type
> + */
> +#define PHY_MV88E 1 /* Marvel 88Exxxx PHY */
> +#define PHY_BCM54XX 2 /* Broardcom BCM54xx PHY */
> +#define TSI108_PHY_TYPE PHY_MV88E
> +
> +/*
> + * TSI108 GIGE port registers
> + */
> +
> +#define TSI108_ETH_PORT_NUM 2
> +#define TSI108_PBM_PORT 2
> +#define TSI108_SDRAM_PORT 4
> +
> +#define TSI108_MAC_CFG1 (0x000)
> +#define TSI108_MAC_CFG1_SOFTRST (1 << 31)
> +#define TSI108_MAC_CFG1_LOOPBACK (1 << 8)
> +#define TSI108_MAC_CFG1_RXEN (1 << 2)
> +#define TSI108_MAC_CFG1_TXEN (1 << 0)
> +
> +#define TSI108_MAC_CFG2 (0x004)
> +#define TSI108_MAC_CFG2_DFLT_PREAMBLE (7 << 12)
> +#define TSI108_MAC_CFG2_IFACE_MASK (3 << 8)
> +#define TSI108_MAC_CFG2_NOGIG (1 << 8)
> +#define TSI108_MAC_CFG2_GIG (2 << 8)
> +#define TSI108_MAC_CFG2_PADCRC (1 << 2)
> +#define TSI108_MAC_CFG2_FULLDUPLEX (1 << 0)
> +
> +#define TSI108_MAC_MII_MGMT_CFG (0x020)
> +#define TSI108_MAC_MII_MGMT_CLK (7 << 0)
> +#define TSI108_MAC_MII_MGMT_RST (1 << 31)
> +
> +#define TSI108_MAC_MII_CMD (0x024)
> +#define TSI108_MAC_MII_CMD_READ (1 << 0)
> +
> +#define TSI108_MAC_MII_ADDR (0x028)
> +#define TSI108_MAC_MII_ADDR_REG 0
> +#define TSI108_MAC_MII_ADDR_PHY 8
> +
> +#define TSI108_MAC_MII_DATAOUT (0x02c)
> +#define TSI108_MAC_MII_DATAIN (0x030)
> +
> +#define TSI108_MAC_MII_IND (0x034)
> +#define TSI108_MAC_MII_IND_NOTVALID (1 << 2)
> +#define TSI108_MAC_MII_IND_SCANNING (1 << 1)
> +#define TSI108_MAC_MII_IND_BUSY (1 << 0)
> +
> +#define TSI108_MAC_IFCTRL (0x038)
> +#define TSI108_MAC_IFCTRL_PHYMODE (1 << 24)
> +
> +#define TSI108_MAC_ADDR1 (0x040)
> +#define TSI108_MAC_ADDR2 (0x044)
> +
> +#define TSI108_STAT_RXBYTES (0x06c)
> +#define TSI108_STAT_RXBYTES_CARRY (1 << 24)
> +
> +#define TSI108_STAT_RXPKTS (0x070)
> +#define TSI108_STAT_RXPKTS_CARRY (1 << 18)
> +
> +#define TSI108_STAT_RXFCS (0x074)
> +#define TSI108_STAT_RXFCS_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXMCAST (0x078)
> +#define TSI108_STAT_RXMCAST_CARRY (1 << 18)
> +
> +#define TSI108_STAT_RXALIGN (0x08c)
> +#define TSI108_STAT_RXALIGN_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXLENGTH (0x090)
> +#define TSI108_STAT_RXLENGTH_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXRUNT (0x09c)
> +#define TSI108_STAT_RXRUNT_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXJUMBO (0x0a0)
> +#define TSI108_STAT_RXJUMBO_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXFRAG (0x0a4)
> +#define TSI108_STAT_RXFRAG_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXJABBER (0x0a8)
> +#define TSI108_STAT_RXJABBER_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXDROP (0x0ac)
> +#define TSI108_STAT_RXDROP_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXBYTES (0x0b0)
> +#define TSI108_STAT_TXBYTES_CARRY (1 << 24)
> +
> +#define TSI108_STAT_TXPKTS (0x0b4)
> +#define TSI108_STAT_TXPKTS_CARRY (1 << 18)
> +
> +#define TSI108_STAT_TXEXDEF (0x0c8)
> +#define TSI108_STAT_TXEXDEF_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXEXCOL (0x0d8)
> +#define TSI108_STAT_TXEXCOL_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXTCOL (0x0dc)
> +#define TSI108_STAT_TXTCOL_CARRY (1 << 13)
> +
> +#define TSI108_STAT_TXPAUSEDROP (0x0e4)
> +#define TSI108_STAT_TXPAUSEDROP_CARRY (1 << 12)
> +
> +#define TSI108_STAT_CARRY1 (0x100)
> +#define TSI108_STAT_CARRY1_RXBYTES (1 << 16)
> +#define TSI108_STAT_CARRY1_RXPKTS (1 << 15)
> +#define TSI108_STAT_CARRY1_RXFCS (1 << 14)
> +#define TSI108_STAT_CARRY1_RXMCAST (1 << 13)
> +#define TSI108_STAT_CARRY1_RXALIGN (1 << 8)
> +#define TSI108_STAT_CARRY1_RXLENGTH (1 << 7)
> +#define TSI108_STAT_CARRY1_RXRUNT (1 << 4)
> +#define TSI108_STAT_CARRY1_RXJUMBO (1 << 3)
> +#define TSI108_STAT_CARRY1_RXFRAG (1 << 2)
> +#define TSI108_STAT_CARRY1_RXJABBER (1 << 1)
> +#define TSI108_STAT_CARRY1_RXDROP (1 << 0)
> +
> +#define TSI108_STAT_CARRY2 (0x104)
> +#define TSI108_STAT_CARRY2_TXBYTES (1 << 13)
> +#define TSI108_STAT_CARRY2_TXPKTS (1 << 12)
> +#define TSI108_STAT_CARRY2_TXEXDEF (1 << 7)
> +#define TSI108_STAT_CARRY2_TXEXCOL (1 << 3)
> +#define TSI108_STAT_CARRY2_TXTCOL (1 << 2)
> +#define TSI108_STAT_CARRY2_TXPAUSE (1 << 0)
> +
> +#define TSI108_STAT_CARRYMASK1 (0x108)
> +#define TSI108_STAT_CARRYMASK2 (0x10c)
> +
> +#define TSI108_EC_PORTCTRL (0x200)
> +#define TSI108_EC_PORTCTRL_STATRST (1 << 31)
> +#define TSI108_EC_PORTCTRL_STATEN (1 << 28)
> +#define TSI108_EC_PORTCTRL_NOGIG (1 << 18)
> +#define TSI108_EC_PORTCTRL_HALFDUPLEX (1 << 16)
> +
> +#define TSI108_EC_INTSTAT (0x204)
> +#define TSI108_EC_INTMASK (0x208)
> +
> +#define TSI108_INT_ANY (1 << 31)
> +#define TSI108_INT_SFN (1 << 30)
> +#define TSI108_INT_RXIDLE (1 << 29)
> +#define TSI108_INT_RXABORT (1 << 28)
> +#define TSI108_INT_RXERROR (1 << 27)
> +#define TSI108_INT_RXOVERRUN (1 << 26)
> +#define TSI108_INT_RXTHRESH (1 << 25)
> +#define TSI108_INT_RXWAIT (1 << 24)
> +#define TSI108_INT_RXQUEUE0 (1 << 16)
> +#define TSI108_INT_STATCARRY (1 << 15)
> +#define TSI108_INT_TXIDLE (1 << 13)
> +#define TSI108_INT_TXABORT (1 << 12)
> +#define TSI108_INT_TXERROR (1 << 11)
> +#define TSI108_INT_TXUNDERRUN (1 << 10)
> +#define TSI108_INT_TXTHRESH (1 << 9)
> +#define TSI108_INT_TXWAIT (1 << 8)
> +#define TSI108_INT_TXQUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXCFG (0x220)
> +#define TSI108_EC_TXCFG_RST (1 << 31)
> +
> +#define TSI108_EC_TXCTRL (0x224)
> +#define TSI108_EC_TXCTRL_IDLEINT (1 << 31)
> +#define TSI108_EC_TXCTRL_ABORT (1 << 30)
> +#define TSI108_EC_TXCTRL_GO (1 << 15)
> +#define TSI108_EC_TXCTRL_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXSTAT (0x228)
> +#define TSI108_EC_TXSTAT_ACTIVE (1 << 15)
> +#define TSI108_EC_TXSTAT_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXESTAT (0x22c)
> +#define TSI108_EC_TXESTAT_Q0_ERR (1 << 24)
> +#define TSI108_EC_TXESTAT_Q0_DESCINT (1 << 16)
> +#define TSI108_EC_TXESTAT_Q0_EOF (1 << 8)
> +#define TSI108_EC_TXESTAT_Q0_EOQ (1 << 0)
> +
> +#define TSI108_EC_TXERR (0x278)
> +
> +#define TSI108_EC_TXQ_CFG (0x280)
> +#define TSI108_EC_TXQ_CFG_DESC_INT (1 << 20)
> +#define TSI108_EC_TXQ_CFG_EOQ_OWN_INT (1 << 19)
> +#define TSI108_EC_TXQ_CFG_WSWP (1 << 11)
> +#define TSI108_EC_TXQ_CFG_BSWP (1 << 10)
> +#define TSI108_EC_TXQ_CFG_SFNPORT 0
> +
> +#define TSI108_EC_TXQ_BUFCFG (0x284)
> +#define TSI108_EC_TXQ_BUFCFG_BURST8 (0 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST32 (1 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST128 (2 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST256 (3 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_WSWP (1 << 11)
> +#define TSI108_EC_TXQ_BUFCFG_BSWP (1 << 10)
> +#define TSI108_EC_TXQ_BUFCFG_SFNPORT 0
> +
> +#define TSI108_EC_TXQ_PTRLOW (0x288)
> +
> +#define TSI108_EC_TXQ_PTRHIGH (0x28c)
> +#define TSI108_EC_TXQ_PTRHIGH_VALID (1 << 31)
> +
> +#define TSI108_EC_TXTHRESH (0x230)
> +#define TSI108_EC_TXTHRESH_STARTFILL 0
> +#define TSI108_EC_TXTHRESH_STOPFILL 16
> +
> +#define TSI108_EC_RXCFG (0x320)
> +#define TSI108_EC_RXCFG_RST (1 << 31)
> +
> +#define TSI108_EC_RXSTAT (0x328)
> +#define TSI108_EC_RXSTAT_ACTIVE (1 << 15)
> +#define TSI108_EC_RXSTAT_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_RXESTAT (0x32c)
> +#define TSI108_EC_RXESTAT_Q0_ERR (1 << 24)
> +#define TSI108_EC_RXESTAT_Q0_DESCINT (1 << 16)
> +#define TSI108_EC_RXESTAT_Q0_EOF (1 << 8)
> +#define TSI108_EC_RXESTAT_Q0_EOQ (1 << 0)
> +
> +#define TSI108_EC_HASHADDR (0x360)
> +#define TSI108_EC_HASHADDR_AUTOINC (1 << 31)
> +#define TSI108_EC_HASHADDR_DO1STREAD (1 << 30)
> +#define TSI108_EC_HASHADDR_UNICAST (0 << 4)
> +#define TSI108_EC_HASHADDR_MCAST (1 << 4)
> +
> +#define TSI108_EC_HASHDATA (0x364)
> +
> +#define TSI108_EC_RXQ_PTRLOW (0x388)
> +
> +#define TSI108_EC_RXQ_PTRHIGH (0x38c)
> +#define TSI108_EC_RXQ_PTRHIGH_VALID (1 << 31)
> +
> +/* Station Enable -- accept packets destined for us */
> +#define TSI108_EC_RXCFG_SE (1 << 13)
> +/* Unicast Frame Enable -- for packets not destined for us */
> +#define TSI108_EC_RXCFG_UFE (1 << 12)
> +/* Multicast Frame Enable */
> +#define TSI108_EC_RXCFG_MFE (1 << 11)
> +/* Broadcast Frame Enable */
> +#define TSI108_EC_RXCFG_BFE (1 << 10)
> +#define TSI108_EC_RXCFG_UC_HASH (1 << 9)
> +#define TSI108_EC_RXCFG_MC_HASH (1 << 8)
> +
> +#define TSI108_EC_RXQ_CFG (0x380)
> +#define TSI108_EC_RXQ_CFG_DESC_INT (1 << 20)
> +#define TSI108_EC_RXQ_CFG_EOQ_OWN_INT (1 << 19)
> +#define TSI108_EC_RXQ_CFG_WSWP (1 << 11)
> +#define TSI108_EC_RXQ_CFG_BSWP (1 << 10)
> +#define TSI108_EC_RXQ_CFG_SFNPORT 0
> +
> +#define TSI108_EC_RXQ_BUFCFG (0x384)
> +#define TSI108_EC_RXQ_BUFCFG_BURST8 (0 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST32 (1 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST128 (2 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST256 (3 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_WSWP (1 << 11)
> +#define TSI108_EC_RXQ_BUFCFG_BSWP (1 << 10)
> +#define TSI108_EC_RXQ_BUFCFG_SFNPORT 0
> +
> +#define TSI108_EC_RXCTRL (0x324)
> +#define TSI108_EC_RXCTRL_ABORT (1 << 30)
> +#define TSI108_EC_RXCTRL_GO (1 << 15)
> +#define TSI108_EC_RXCTRL_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_RXERR (0x378)
> +
> +#define PHY_CTRL 0
> +#define PHY_CTRL_RESET (1 << 15)
> +#define PHY_CTRL_AUTONEG_EN (1 << 12)
> +#define PHY_CTRL_POWERDOWN (1 << 11)
> +#define PHY_CTRL_AUTONEG_START (1 << 9)
> +
> +#define PHY_STAT 1
> +#define PHY_STAT_LINKUP (1 << 2)
> +
> +#if (TSI108_PHY_TYPE == PHY_MV88E)
> +/* Marvel 88E1xxx-specific */
> +#define PHY_SUM_STAT 0x11
> +#define PHY_SUM_STAT_SPEED_MASK (3 << 14)
> +#define PHY_SUM_STAT_SPEED_10 (0 << 14)
> +#define PHY_SUM_STAT_SPEED_100 (1 << 14)
> +#define PHY_SUM_STAT_SPEED_1000 (2 << 14)
> +#define PHY_SUM_STAT_FULLDUPLEX (1 << 13)
> +
> +#define PHY_SUM_STAT_1000T_FD (PHY_SUM_STAT_SPEED_1000 |
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_1000T_HD (PHY_SUM_STAT_SPEED_1000)
> +#define PHY_SUM_STAT_100TX_FD (PHY_SUM_STAT_SPEED_100 |
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_100TX_HD (PHY_SUM_STAT_SPEED_100)
> +#define PHY_SUM_STAT_10T_FD (PHY_SUM_STAT_SPEED_10 |
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_10T_HD (PHY_SUM_STAT_SPEED_10)
> +#elif (TSI108_PHY_TYPE == PHY_BCM54XX)
> +/*Broadcom BCM54xx */
> +#define PHY_SUM_STAT 0x19
> +#define PHY_SUM_STAT_SPEED_MASK (7 << 8) /* Auto Negotiation HCD */
> +#define PHY_SUM_STAT_1000T_FD (7 << 8) /* 1000BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_1000T_HD (6 << 8) /* 1000BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_100TX_FD (5 << 8) /* 100BASE-TX Full-Duplex */
> +#define PHY_SUM_STAT_100T4 (4 << 8) /* 100BASE-T4 */
> +#define PHY_SUM_STAT_100TX_HD (3 << 8) /* 100BASE-TX Half-Duplex */
> +#define PHY_SUM_STAT_10T_FD (2 << 8) /* 10BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_10T_HD (1 << 8) /* 10BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_AN_FAIL (0 << 8) /* 1000BASE-T Half-Duplex */
> +#else
> +#error "PHY Device not specified"
> +#endif /* MV88_PHY */
> +
> +#define TSI108_TX_EOF (1 << 0) /* End of frame; last fragment of
> packet */
> +#define TSI108_TX_SOF (1 << 1) /* Start of frame; first frag. of
> packet */
> +#define TSI108_TX_VLAN (1 << 2) /* Per-frame VLAN: enables VLAN
> override */
> +#define TSI108_TX_HUGE (1 << 3) /* Huge frame enable */
> +#define TSI108_TX_PAD (1 << 4) /* Pad the packet if too short */
> +#define TSI108_TX_CRC (1 << 5) /* Generate CRC for this packet */
> +#define TSI108_TX_INT (1 << 14) /* Generate an IRQ after frag.
> processed */
> +#define TSI108_TX_RETRY 16 /* 4 bit field indicating num. of
> retries */
> +#define TSI108_TX_COL (1 << 20) /* Set if a collision occured */
> +#define TSI108_TX_LCOL (1 << 24) /* Set if a late collision
> occured */
> +#define TSI108_TX_UNDER (1 << 25) /* Set if a FIFO underrun
> occured */
> +#define TSI108_TX_RLIM (1 << 26) /* Set if the retry limit was
> reached */
> +#define TSI108_TX_OK (1 << 30) /* Set if the frame TX was
> successful */
> +#define TSI108_TX_OWN (1 << 31) /* Set if the device owns the
> descriptor */
> +
> +/* Note: the descriptor layouts assume big-endian byte order. */
> +typedef struct {
> + u32 buf0;
> + u32 buf1; /* Base address of buffer */
> + u32 next0; /* Address of next descriptor, if any */
> + u32 next1;
> + u16 vlan; /* VLAN, if override enabled for this packet */
> + u16 len; /* Length of buffer in bytes */
> + u32 misc; /* See TSI108_TX_* above */
> + u32 reserved0; /*reserved0 and reserved1 are added to make the
> desc */
> + u32 reserved1; /* 32-byte aligned */
> +} __attribute__ ((aligned(32))) tx_desc;
> +
> +#define TSI108_RX_EOF (1 << 0) /* End of frame; last fragment of
> packet */
> +#define TSI108_RX_SOF (1 << 1) /* Start of frame; first frag. of
> packet */
> +#define TSI108_RX_VLAN (1 << 2) /* Set on SOF if packet has a VLAN */
> +#define TSI108_RX_FTYPE (1 << 3) /* Length/Type field is type, not
> length */
> +#define TSI108_RX_RUNT (1 << 4)/* Packet is less than minimum size */
> +#define TSI108_RX_HASH (1 << 7)/* Hash table match */
> +#define TSI108_RX_BAD (1 << 8) /* Bad frame */
> +#define TSI108_RX_OVER (1 << 9) /* FIFO overrun occured */
> +#define TSI108_RX_TRUNC (1 << 11) /* Packet truncated due to
> excess length */
> +#define TSI108_RX_CRC (1 << 12) /* Packet had a CRC error */
> +#define TSI108_RX_INT (1 << 13) /* Generate an IRQ after frag.
> processed */
> +#define TSI108_RX_OWN (1 << 15) /* Set if the device owns the
> descriptor */
> +
> +typedef struct {
> + u32 buf0; /* Base address of buffer */
> + u32 buf1; /* Base address of buffer */
> + u32 next0; /* Address of next descriptor, if any */
> + u32 next1; /* Address of next descriptor, if any */
> + u16 vlan; /* VLAN of received packet, first frag only */
> + u16 len; /* Length of received fragment in bytes */
> + u16 blen; /* Length of buffer in bytes */
> + u16 misc; /* See TSI108_RX_* above */
> + u32 reserved0; /* reserved0 and reserved1 are added to make the
> desc */
> + u32 reserved1; /* 32-byte aligned */
> +} __attribute__ ((aligned(32))) rx_desc;
> +
> +#endif /* __TSI108_ETH_H */
> --
> 1.3.0
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply
* RE: [PATCH/2.6.17-rc4 5/10] Add tsi108 Ethernet support
From: Zang Roy-r61911 @ 2006-05-18 3:55 UTC (permalink / raw)
To: jgarzik
Cc: Alexandre.Bounine, netdev, linux-kernel, linuxppc-dev list,
Paul Mackerras, Yang Xin-Xin-r48390
This patch adds Tundra tsi108 Ethernet driver support.
-----Original Message-----
From: Kumar Gala [mailto:galak@kernel.crashing.org]
Sent: 2006年5月17日 21:24
To: Zang Roy-r61911
Cc: Paul Mackerras; linuxppc-dev list; Alexandre.Bounine@tundra.com; Yang Xin-Xin-r48390
Subject: Re: [PATCH/2.6.17-rc4 5/10] Add tsi108 Ethernet support
On May 17, 2006, at 5:14 AM, Zang Roy-r61911 wrote:
> This patch adds a device driver and configuration options for
> Tundra Semiconductor Tsi108 integrated dual port Gigabit Ethernet
> controller
>
> Signed-off-by: Alexandre Bounine <alexandreb@tundra.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
This patch needs to go to the netdev list and Jeff Garzik as
maintainer for review.
- kumar
> drivers/net/Kconfig | 8
> drivers/net/Makefile | 1
> drivers/net/tsi108_eth.c | 1740 +++++++++++++++++++++++++++++++++++
> +++++++++++
> drivers/net/tsi108_eth.h | 404 +++++++++++
> 4 files changed, 2153 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/tsi108_eth.c
> create mode 100644 drivers/net/tsi108_eth.h
>
> 23ed1c55ab6bc7a4c1c76f646579a1bc195bc7d2
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index bdaaad8..8a4ef29 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -2177,6 +2177,14 @@ config SPIDER_NET
> This driver supports the Gigabit Ethernet chips present on the
> Cell Processor-Based Blades from IBM.
>
> +config TSI108_ETH
> + tristate "Tundra TSI108 gigabit Ethernet support"
> + depends on TSI108_BRIDGE
> + help
> + This driver supports Tundra TSI108 gigabit Ethernet ports.
> + To compile this driver as a module, choose M here: the module
> + will be called tsi108_eth.
> +
> config GIANFAR
> tristate "Gianfar Ethernet"
> depends on 85xx || 83xx
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index b90468a..5f90b30 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -109,6 +109,7 @@ obj-$(CONFIG_B44) += b44.o
> obj-$(CONFIG_FORCEDETH) += forcedeth.o
> obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
>
> +obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
> obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
>
> obj-$(CONFIG_PPP) += ppp_generic.o slhc.o
> diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
> new file mode 100644
> index 0000000..cb67dbe
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.c
> @@ -0,0 +1,1740 @@
> +/
> **********************************************************************
> *********
> +
> + Copyright(c) 2005 Tundra Semiconductor Corporation.
> +
> + 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 Free
> + Software Foundation; either version 2 of the License, or (at
> your option)
> + any later version.
> +
> + This program is distributed in the hope that it will be useful,
> but WITHOUT
> + ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> License for
> + more details.
> +
> + You should have received a copy of the GNU General Public
> License along with
> + this program; if not, write to the Free Software Foundation,
> Inc., 59
> + Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> +
> +*********************************************************************
> **********/
> +
> +/* This driver is based on the driver code originally developed
> + * for the Intel IOC80314 (ForestLake) Gigabit Ethernet by
> + * scott.wood@timesys.com * Copyright (C) 2003 TimeSys Corporation
> + *
> + * Currently changes from original version are:
> + * - portig to Tsi108-based platform and kernel 2.6
> (kong.lai@tundra.com)
> + * - modifications to handle two ports independently and support for
> + * additional PHY devices (alexandre.bounine@tundra.com)
> + *
> + */
> +
> +#include <linux/config.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/init.h>
> +#include <linux/net.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/delay.h>
> +#include <linux/crc32.h>
> +#include <linux/mii.h>
> +#include <linux/device.h>
> +#include <asm/system.h>
> +#include <asm/io.h>
> +#include <linux/pci.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/timer.h>
> +
> +#include <asm/tsi108_irq.h>
> +#include <asm/tsi108.h>
> +#include "tsi108_eth.h"
> +
> +typedef struct net_device net_device;
> +typedef struct sk_buff sk_buff;
> +
> +#define MII_READ_DELAY 10000 /* max link wait time in msec */
> +
> +#define TSI108_RXRING_LEN 256
> +
> +/* NOTE: The driver currently does not support receiving packets
> + * larger than the buffer size, so don't decrease this (unless you
> + * want to add such support).
> + */
> +#define TSI108_RXBUF_SIZE 1536
> +
> +#define TSI108_TXRING_LEN 256
> +
> +#define TSI108_TX_INT_FREQ 64
> +
> +/* Check the phy status every half a second. */
> +#define CHECK_PHY_INTERVAL (HZ/2)
> +
> +extern hw_info hw_info_table[];
> +
> +typedef struct {
> + volatile u32 regs; /* Base of normal regs */
> + volatile u32 phyregs; /* Base of register bank used for PHY
> access */
> + int phy; /* Index of PHY for this interface */
> + int irq_num;
> +
> + struct timer_list timer;/* Timer that triggers the check phy
> function */
> + int rxtail; /* Next entry in rxring to read */
> + int rxhead; /* Next entry in rxring to give a new buffer */
> + int rxfree; /* Number of free, allocated RX buffers */
> +
> + int rxpending; /* Non-zero if there are still descriptors
> + * to be processed from a previous descriptor
> + * interrupt condition that has been cleared */
> +
> + int txtail; /* Next TX descriptor to check status on */
> + int txhead; /* Next TX descriptor to use */
> +
> + /* Number of free TX descriptors. This could be calculated from
> + * rxhead and rxtail if one descriptor were left unused to
> disambiguate
> + * full and empty conditions, but it's simpler to just keep track
> + * explicitly. */
> +
> + int txfree;
> +
> + int phy_ok; /* The PHY is currently powered on. */
> +
> + /* PHY status (duplex is 1 for half, 2 for full,
> + * so that the default 0 indicates that neither has
> + * yet been configured). */
> +
> + int link_up;
> + int speed;
> + int duplex;
> +
> + tx_desc *txring;
> + rx_desc *rxring;
> + sk_buff *txskbs[TSI108_TXRING_LEN];
> + sk_buff *rxskbs[TSI108_RXRING_LEN];
> +
> + dma_addr_t txdma, rxdma;
> +
> + /* txlock nests in misclock and phy_lock */
> +
> + spinlock_t txlock, misclock;
> +
> + /* stats is used to hold the upper bits of each hardware counter,
> + * and tmpstats is used to hold the full values for returning
> + * to the caller of get_stats(). They must be separate in case
> + * an overflow interrupt occurs before the stats are consumed.
> + */
> +
> + struct net_device_stats stats;
> + struct net_device_stats tmpstats;
> +
> + /* These stats are kept separate in hardware, thus require
> individual
> + * fields for handling carry. They are combined in get_stats.
> + */
> +
> + unsigned long rx_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_short_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_long_fcs; /* Add to rx_frame_errors */
> + unsigned long rx_underruns; /* Add to rx_length_errors */
> + unsigned long rx_overruns; /* Add to rx_length_errors */
> +
> + unsigned long tx_coll_abort; /* Add to tx_aborted_errors/
> collisions */
> + unsigned long tx_pause_drop; /* Add to tx_aborted_errors */
> +
> + unsigned long mc_hash[16];
> +} tsi108_prv_data;
> +static void tsi108_timed_checker(unsigned long dev_ptr);
> +
> +static net_device *tsi108_devs[TSI108_ETH_PORT_NUM];
> +
> +static void dump_eth_one(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + printk("Dumping %s...\n", dev->name);
> + printk("intstat %x intmask %x phy_ok %d"
> + " link %d speed %d duplex %d\n",
> + TSI108_ETH_READ_REG(TSI108_EC_INTSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK), data->phy_ok,
> + data->link_up, data->speed, data->duplex);
> +
> + printk("TX: head %d, tail %d, free %d, stat %x, estat %x, err %x\n",
> + data->txhead, data->txtail, data->txfree,
> + TSI108_ETH_READ_REG(TSI108_EC_TXSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_TXESTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_TXERR));
> +
> + printk("RX: head %d, tail %d, free %d, stat %x,"
> + " estat %x, err %x, pending %d\n\n",
> + data->rxhead, data->rxtail, data->rxfree,
> + TSI108_ETH_READ_REG(TSI108_EC_RXSTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_RXESTAT),
> + TSI108_ETH_READ_REG(TSI108_EC_RXERR), data->rxpending);
> +}
> +
> +void tsi108_dump_eth(void)
> +{
> + dump_eth_one(tsi108_devs[0]);
> + dump_eth_one(tsi108_devs[1]);
> +}
> +
> +/* Synchronization is needed between the thread and up/down events.
> + * Note that the PHY is accessed through the same registers for both
> + * interfaces, so this can't be made interface-specific.
> + */
> +
> +static spinlock_t phy_lock = SPIN_LOCK_UNLOCKED;
> +
> +static inline u16 tsi108_read_mii(tsi108_prv_data * data, int reg,
> int *status)
> +{
> + int i;
> + u16 ret;
> +
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> + (data->phy << TSI108_MAC_MII_ADDR_PHY) |
> + (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD, 0);
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_CMD,
> TSI108_MAC_MII_CMD_READ);
> + mb();
> + for (i = 0; i < 100; i++) {
> + if (!(TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> + (TSI108_MAC_MII_IND_NOTVALID | TSI108_MAC_MII_IND_BUSY)))
> + break;
> + udelay(10);
> + }
> +
> + if (i == 100) {
> + if (status)
> + *status = -EBUSY;
> +
> + ret = 0xffff;
> + } else {
> + if (status)
> + *status = 0;
> +
> + ret = TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_DATAIN);
> + }
> +
> + return ret;
> +}
> +
> +static inline void tsi108_write_mii(tsi108_prv_data * data, int
> reg, u16 val)
> +{
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_ADDR,
> + (data->phy << TSI108_MAC_MII_ADDR_PHY) |
> + (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> + TSI108_ETH_WRITE_PHYREG(TSI108_MAC_MII_DATAOUT, val);
> + mb();
> + while (TSI108_ETH_READ_PHYREG(TSI108_MAC_MII_IND) &
> + TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static inline void tsi108_write_tbi(tsi108_prv_data * data, int
> reg, u16 val)
> +{
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_ADDR,
> + (0x1e << TSI108_MAC_MII_ADDR_PHY)
> + | (reg << TSI108_MAC_MII_ADDR_REG));
> + mb();
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_DATAOUT, val);
> + mb();
> + while (TSI108_ETH_READ_REG(TSI108_MAC_MII_IND) &
> + TSI108_MAC_MII_IND_BUSY) ;
> +}
> +
> +static void tsi108_check_phy(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u16 sumstat;
> + u32 mac_cfg2_reg, portctrl_reg;
> + u32 fdx_flag = 0, reg_update = 0;
> +
> + /* Do a dummy read, as for some reason the first read
> + * after a link becomes up returns link down, even if
> + * it's been a while since the link came up.
> + */
> +
> + spin_lock(&phy_lock);
> +
> + if (!data->phy_ok)
> + goto out;
> +
> + tsi108_read_mii(data, PHY_STAT, NULL);
> +
> + if (!(tsi108_read_mii(data, PHY_STAT, NULL) & PHY_STAT_LINKUP)) {
> + if (data->link_up == 1) {
> + netif_stop_queue(dev);
> + data->link_up = 0;
> + printk(KERN_NOTICE "%s : link is down\n", dev->name);
> + netif_carrier_off(dev);
> + }
> +
> + goto out;
> + }
> +
> + {
> + mac_cfg2_reg = TSI108_ETH_READ_REG(TSI108_MAC_CFG2);
> + portctrl_reg = TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL);
> +
> + sumstat = tsi108_read_mii(data, PHY_SUM_STAT, NULL);
> +
> + switch (sumstat & PHY_SUM_STAT_SPEED_MASK) {
> + case PHY_SUM_STAT_1000T_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_1000T_HD:
> + if (data->speed != 1000) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |= TSI108_MAC_CFG2_GIG;
> + portctrl_reg &= ~TSI108_EC_PORTCTRL_NOGIG;
> + data->speed = 1000;
> + reg_update++;
> + }
> + break;
> + case PHY_SUM_STAT_100TX_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_100TX_HD:
> + if (data->speed != 100) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
> + portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
> + data->speed = 100;
> + reg_update++;
> + }
> + break;
> +
> + case PHY_SUM_STAT_10T_FD:
> + fdx_flag++;
> + case PHY_SUM_STAT_10T_HD:
> + if (data->speed != 10) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_IFACE_MASK;
> + mac_cfg2_reg |= TSI108_MAC_CFG2_NOGIG;
> + portctrl_reg |= TSI108_EC_PORTCTRL_NOGIG;
> + data->speed = 10;
> + reg_update++;
> + }
> + break;
> +
> + default:
> + if (net_ratelimit())
> + printk(KERN_ERR "PHY reported invalid speed,"
> + KERN_ERR " summary status %x\n",
> + sumstat);
> + goto out;
> + }
> +
> + if (fdx_flag || (sumstat & PHY_SUM_STAT_FULLDUPLEX)) {
> + if (data->duplex != 2) {
> + mac_cfg2_reg |= TSI108_MAC_CFG2_FULLDUPLEX;
> + portctrl_reg &= ~TSI108_EC_PORTCTRL_HALFDUPLEX;
> + data->duplex = 2;
> + reg_update++;
> + }
> + } else {
> + if (data->duplex != 1) {
> + mac_cfg2_reg &= ~TSI108_MAC_CFG2_FULLDUPLEX;
> + portctrl_reg |= TSI108_EC_PORTCTRL_HALFDUPLEX;
> + data->duplex = 1;
> + reg_update++;
> + }
> + }
> +
> + if (reg_update) {
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2, mac_cfg2_reg);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, portctrl_reg);
> + mb();
> + }
> +
> + }
> +
> + if (data->link_up == 0) {
> + /* The manual says it can take 3-4 usecs for the speed change
> + * to take effect.
> + */
> + udelay(5);
> +
> + spin_lock(&data->txlock);
> + if (netif_queue_stopped(dev)
> + && is_valid_ether_addr(dev->dev_addr) && data->txfree)
> + netif_wake_queue(dev);
> +
> + data->link_up = 1;
> + spin_unlock(&data->txlock);
> + printk("%s : link is up: %dMb %s-duplex\n",
> + dev->name, data->speed,
> + (data->duplex == 2) ? "full" : "half");
> + netif_carrier_on(dev);
> + }
> +
> + out:
> + spin_unlock(&phy_lock);
> +}
> +
> +static inline void
> +tsi108_stat_carry_one(int carry, int carry_bit, int carry_shift,
> + unsigned long *upper)
> +{
> + if (carry & carry_bit)
> + *upper += carry_shift;
> +}
> +
> +static void tsi108_stat_carry(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 carry1, carry2;
> +
> + spin_lock_irq(&data->misclock);
> +
> + carry1 = TSI108_ETH_READ_REG(TSI108_STAT_CARRY1);
> + carry2 = TSI108_ETH_READ_REG(TSI108_STAT_CARRY2);
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY1, carry1);
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRY2, carry2);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXBYTES,
> + TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXPKTS,
> + TSI108_STAT_RXPKTS_CARRY,
> + &data->stats.rx_packets);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFCS,
> + TSI108_STAT_RXFCS_CARRY, &data->rx_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXMCAST,
> + TSI108_STAT_RXMCAST_CARRY,
> + &data->stats.multicast);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXALIGN,
> + TSI108_STAT_RXALIGN_CARRY,
> + &data->stats.rx_frame_errors);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXLENGTH,
> + TSI108_STAT_RXLENGTH_CARRY,
> + &data->stats.rx_length_errors);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXRUNT,
> + TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJUMBO,
> + TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXFRAG,
> + TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXJABBER,
> + TSI108_STAT_RXJABBER_CARRY, &data->rx_long_fcs);
> +
> + tsi108_stat_carry_one(carry1, TSI108_STAT_CARRY1_RXDROP,
> + TSI108_STAT_RXDROP_CARRY,
> + &data->stats.rx_missed_errors);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXBYTES,
> + TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPKTS,
> + TSI108_STAT_TXPKTS_CARRY,
> + &data->stats.tx_packets);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXDEF,
> + TSI108_STAT_TXEXDEF_CARRY,
> + &data->stats.tx_aborted_errors);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXEXCOL,
> + TSI108_STAT_TXEXCOL_CARRY, &data->tx_coll_abort);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXTCOL,
> + TSI108_STAT_TXTCOL_CARRY,
> + &data->stats.collisions);
> +
> + tsi108_stat_carry_one(carry2, TSI108_STAT_CARRY2_TXPAUSE,
> + TSI108_STAT_TXPAUSEDROP_CARRY,
> + &data->tx_pause_drop);
> +
> + spin_unlock_irq(&data->misclock);
> +}
> +
> +/* Read a stat counter atomically with respect to carries.
> + * data->misclock must be held.
> + */
> +static inline unsigned long
> +tsi108_read_stat(tsi108_prv_data * data, int reg, int carry_bit,
> + int carry_shift, unsigned long *upper)
> +{
> + int carryreg;
> + unsigned long val;
> +
> + if (reg < 0xb0)
> + carryreg = TSI108_STAT_CARRY1;
> + else
> + carryreg = TSI108_STAT_CARRY2;
> +
> + again:
> + val = TSI108_ETH_READ_REG(reg) | *upper;
> +
> + rmb();
> +
> + /* Check to see if it overflowed, but the interrupt hasn't
> + * been serviced yet. If so, handle the carry here, and
> + * try again.
> + */
> +
> + if (unlikely(TSI108_ETH_READ_REG(carryreg) & carry_bit)) {
> + *upper += carry_shift;
> + TSI108_ETH_WRITE_REG(carryreg, carry_bit);
> + mb();
> +
> + goto again;
> + }
> +
> + return val;
> +}
> +
> +static struct net_device_stats *tsi108_get_stats(net_device * dev)
> +{
> + unsigned long excol;
> +
> + tsi108_prv_data *data = netdev_priv(dev);
> + spin_lock_irq(&data->misclock);
> +
> + data->tmpstats.rx_packets =
> + tsi108_read_stat(data, TSI108_STAT_RXPKTS,
> + TSI108_STAT_CARRY1_RXPKTS,
> + TSI108_STAT_RXPKTS_CARRY, &data->stats.rx_packets);
> +
> + data->tmpstats.tx_packets =
> + tsi108_read_stat(data, TSI108_STAT_TXPKTS,
> + TSI108_STAT_CARRY2_TXPKTS,
> + TSI108_STAT_TXPKTS_CARRY, &data->stats.tx_packets);
> +
> + data->tmpstats.rx_bytes =
> + tsi108_read_stat(data, TSI108_STAT_RXBYTES,
> + TSI108_STAT_CARRY1_RXBYTES,
> + TSI108_STAT_RXBYTES_CARRY, &data->stats.rx_bytes);
> +
> + data->tmpstats.tx_bytes =
> + tsi108_read_stat(data, TSI108_STAT_TXBYTES,
> + TSI108_STAT_CARRY2_TXBYTES,
> + TSI108_STAT_TXBYTES_CARRY, &data->stats.tx_bytes);
> +
> + data->tmpstats.multicast =
> + tsi108_read_stat(data, TSI108_STAT_RXMCAST,
> + TSI108_STAT_CARRY1_RXMCAST,
> + TSI108_STAT_RXMCAST_CARRY, &data->stats.multicast);
> +
> + excol = tsi108_read_stat(data, TSI108_STAT_TXEXCOL,
> + TSI108_STAT_CARRY2_TXEXCOL,
> + TSI108_STAT_TXEXCOL_CARRY,
> + &data->tx_coll_abort);
> +
> + data->tmpstats.collisions =
> + tsi108_read_stat(data, TSI108_STAT_TXTCOL,
> + TSI108_STAT_CARRY2_TXTCOL,
> + TSI108_STAT_TXTCOL_CARRY, &data->stats.collisions);
> +
> + data->tmpstats.collisions += excol;
> +
> + data->tmpstats.rx_length_errors =
> + tsi108_read_stat(data, TSI108_STAT_RXLENGTH,
> + TSI108_STAT_CARRY1_RXLENGTH,
> + TSI108_STAT_RXLENGTH_CARRY,
> + &data->stats.rx_length_errors);
> +
> + data->tmpstats.rx_length_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXRUNT,
> + TSI108_STAT_CARRY1_RXRUNT,
> + TSI108_STAT_RXRUNT_CARRY, &data->rx_underruns);
> +
> + data->tmpstats.rx_length_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXJUMBO,
> + TSI108_STAT_CARRY1_RXJUMBO,
> + TSI108_STAT_RXJUMBO_CARRY, &data->rx_overruns);
> +
> + data->tmpstats.rx_frame_errors =
> + tsi108_read_stat(data, TSI108_STAT_RXALIGN,
> + TSI108_STAT_CARRY1_RXALIGN,
> + TSI108_STAT_RXALIGN_CARRY,
> + &data->stats.rx_frame_errors);
> +
> + data->tmpstats.rx_frame_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXFCS,
> + TSI108_STAT_CARRY1_RXFCS, TSI108_STAT_RXFCS_CARRY,
> + &data->rx_fcs);
> +
> + data->tmpstats.rx_frame_errors +=
> + tsi108_read_stat(data, TSI108_STAT_RXFRAG,
> + TSI108_STAT_CARRY1_RXFRAG,
> + TSI108_STAT_RXFRAG_CARRY, &data->rx_short_fcs);
> +
> + data->tmpstats.rx_missed_errors =
> + tsi108_read_stat(data, TSI108_STAT_RXDROP,
> + TSI108_STAT_CARRY1_RXDROP,
> + TSI108_STAT_RXDROP_CARRY,
> + &data->stats.rx_missed_errors);
> +
> + /* These three are maintained by software. */
> + data->tmpstats.rx_fifo_errors = data->stats.rx_fifo_errors;
> + data->tmpstats.rx_crc_errors = data->stats.rx_crc_errors;
> +
> + data->tmpstats.tx_aborted_errors =
> + tsi108_read_stat(data, TSI108_STAT_TXEXDEF,
> + TSI108_STAT_CARRY2_TXEXDEF,
> + TSI108_STAT_TXEXDEF_CARRY,
> + &data->stats.tx_aborted_errors);
> +
> + data->tmpstats.tx_aborted_errors +=
> + tsi108_read_stat(data, TSI108_STAT_TXPAUSEDROP,
> + TSI108_STAT_CARRY2_TXPAUSE,
> + TSI108_STAT_TXPAUSEDROP_CARRY,
> + &data->tx_pause_drop);
> +
> + data->tmpstats.tx_aborted_errors += excol;
> +
> + data->tmpstats.tx_errors = data->tmpstats.tx_aborted_errors;
> + data->tmpstats.rx_errors = data->tmpstats.rx_length_errors +
> + data->tmpstats.rx_crc_errors +
> + data->tmpstats.rx_frame_errors +
> + data->tmpstats.rx_fifo_errors + data->tmpstats.rx_missed_errors;
> +
> + spin_unlock_irq(&data->misclock);
> + return &data->tmpstats;
> +}
> +
> +static void tsi108_restart_rx(tsi108_prv_data * data, net_device *
> dev)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRHIGH,
> + TSI108_EC_RXQ_PTRHIGH_VALID);
> +
> + wmb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, TSI108_EC_RXCTRL_GO
> + | TSI108_EC_RXCTRL_QUEUE0);
> +}
> +
> +static void tsi108_restart_tx(tsi108_prv_data * data)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRHIGH,
> + TSI108_EC_TXQ_PTRHIGH_VALID);
> +
> + wmb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, TSI108_EC_TXCTRL_IDLEINT |
> + TSI108_EC_TXCTRL_GO | TSI108_EC_TXCTRL_QUEUE0);
> +}
> +
> +/* txlock must be held by caller, with IRQs disabled, and
> + * with permission to re-enable them when the lock is dropped.
> + */
> +static void tsi108_check_for_completed_tx(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int tx;
> + struct sk_buff *skb;
> + int release = 0;
> +
> + while (!data->txfree || data->txhead != data->txtail) {
> + tx = data->txtail;
> +
> + if (data->txring[tx].misc & TSI108_TX_OWN)
> + break;
> +
> + skb = data->txskbs[tx];
> +
> + if (!(data->txring[tx].misc & TSI108_TX_OK))
> + printk("%s: bad tx packet, misc %x\n",
> + dev->name, data->txring[tx].misc);
> +
> + data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
> + data->txfree++;
> +
> + if (data->txring[tx].misc & TSI108_TX_EOF) {
> + dev_kfree_skb_any(skb);
> + release++;
> + }
> + }
> +
> + if (release) {
> +
> + if (netif_queue_stopped(dev)
> + && is_valid_ether_addr(dev->dev_addr) && data->link_up)
> + netif_wake_queue(dev);
> + }
> +}
> +
> +static int tsi108_send_packet(sk_buff * skb, net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int frags = skb_shinfo(skb)->nr_frags + 1;
> + int i;
> +#ifdef FIXME_SG_CSUM_NOT_TESTED /* FIXME: Not supported now. */
> + long csstart;
> + long csum;
> +
> + csstart = skb->len - skb->data_len;
> + if (csstart > skb->len - skb->data_len)
> + BUG();
> + csum = 0;
> + if (csstart != skb->len)
> + csum = skb_checksum(skb, csstart, skb->len - csstart, 0);
> +#endif
> +
> + if (!data->phy_ok && net_ratelimit())
> + printk(KERN_ERR "%s: Transmit while PHY is down!\n", dev->name);
> +
> + if (!data->link_up) {
> + printk(KERN_ERR "%s: Transmit while link is down!\n",
> + dev->name);
> + netif_stop_queue(dev);
> + return 1;
> + }
> +
> + if (data->txfree < MAX_SKB_FRAGS + 1) {
> + netif_stop_queue(dev);
> +
> + if (net_ratelimit())
> + printk(KERN_ERR "%s: Transmit with full tx ring!\n",
> + dev->name);
> + return 1;
> + }
> +
> + if (data->txfree - frags < MAX_SKB_FRAGS + 1) {
> + netif_stop_queue(dev);
> + }
> +
> + spin_lock_irq(&data->txlock);
> +
> + for (i = 0; i < frags; i++) {
> + int misc = 0;
> + int tx = data->txhead;
> +
> + /* This is done to mark every TSI108_TX_INT_FREQ tx buffers with
> + * the interrupt bit. TX descriptor-complete interrupts are
> + * enabled when the queue fills up, and masked when there is
> + * still free space. This way, when saturating the outbound
> + * link, the tx interrupts are kept to a reasonable level.
> + * When the queue is not full, reclamation of skbs still occurs
> + * as new packets are transmitted, or on a queue-empty
> + * interrupt.
> + */
> +
> + if ((tx % TSI108_TX_INT_FREQ == 0) &&
> + ((TSI108_TXRING_LEN - data->txfree) >= TSI108_TX_INT_FREQ)
> + )
> + misc = TSI108_TX_INT;
> +
> + data->txskbs[tx] = skb;
> +
> + if (i == 0) {
> + data->txring[tx].buf0 = virt_to_phys(skb->data);
> + data->txring[tx].len = skb->len - skb->data_len;
> + misc |= TSI108_TX_SOF;
> + } else {
> + skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
> +
> + data->txring[tx].buf0 =
> + page_to_phys(frag->page) + frag->page_offset;
> + data->txring[tx].len = frag->size;
> + }
> +
> + if (i == frags - 1)
> + misc |= TSI108_TX_EOF;
> +
> +#ifdef TSI108_PRINT_TX_FRAME
> + {
> + int i;
> + printk("%s: Tx Frame contents (%d)\n", dev->name,
> + skb->len);
> + for (i = 0; i < skb->len; i++)
> + printk(" %2.2x", skb->data[i]);
> + printk(".\n");
> + }
> +#endif /* TSI108_PRINT_TX_FRAME */
> +
> + mb();
> + data->txring[tx].misc = misc | TSI108_TX_OWN;
> +
> + data->txhead = (data->txhead + 1) % TSI108_TXRING_LEN;
> + data->txfree--;
> + }
> +
> + tsi108_check_for_completed_tx(dev);
> +
> + /* This must be done after the check for completed tx descriptors,
> + * so that the tail pointer is correct.
> + */
> +
> + if (!(TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) &
> TSI108_EC_TXSTAT_QUEUE0))
> + tsi108_restart_tx(data);
> +
> + spin_unlock_irq(&data->txlock);
> + return 0;
> +}
> +
> +static int tsi108_check_for_completed_rx(net_device * dev, int
> budget)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int done = 0;
> +
> + while (data->rxfree && done != budget) {
> + int rx = data->rxtail;
> + struct sk_buff *skb;
> +
> + if (data->rxring[rx].misc & TSI108_RX_OWN)
> + break;
> +
> + skb = data->rxskbs[rx];
> + data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
> + data->rxfree--;
> + done++;
> +
> + if (data->rxring[rx].misc & TSI108_RX_BAD) {
> + spin_lock_irq(&data->misclock);
> +
> + if (data->rxring[rx].misc & TSI108_RX_CRC)
> + data->stats.rx_crc_errors++;
> + if (data->rxring[rx].misc & TSI108_RX_OVER)
> + data->stats.rx_fifo_errors++;
> +
> + spin_unlock_irq(&data->misclock);
> +
> + dev_kfree_skb_any(skb);
> + continue;
> + }
> +#ifdef TSI108_PRINT_RX_FRAME
> + {
> + int i;
> + printk("%s: Rx Frame contents (%d)\n",
> + dev->name, data->rxring[rx].len);
> + for (i = 0; i < data->rxring[rx].len; i++)
> + printk(" %2.2x", skb->data[i]);
> + printk(".\n");
> + }
> +#endif /* TSI108_PRINT_RX_FRAME */
> +
> + skb->dev = dev;
> + skb_put(skb, data->rxring[rx].len);
> + skb->protocol = eth_type_trans(skb, dev);
> + netif_receive_skb(skb);
> + dev->last_rx = jiffies;
> + }
> +
> + return done;
> +}
> +
> +static int tsi108_refill_rx(net_device * dev, int budget)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + int done = 0;
> +
> + while (data->rxfree != TSI108_RXRING_LEN && done != budget) {
> + int rx = data->rxhead;
> + sk_buff *skb;
> +
> + data->rxskbs[rx] = skb = dev_alloc_skb(TSI108_RXBUF_SIZE + 2);
> + if (!skb)
> + break;
> +
> + skb_reserve(skb, 2); /* Align the data on a 4-byte boundary. */
> +
> + data->rxring[rx].buf0 = virt_to_phys(skb->data);
> +
> + /* Sometimes the hardware sets blen to zero after packet
> + * reception, even though the manual says that it's only ever
> + * modified by the driver.
> + */
> +
> + data->rxring[rx].blen = 1536;
> + mb();
> + data->rxring[rx].misc = TSI108_RX_OWN | TSI108_RX_INT;
> +
> + data->rxhead = (data->rxhead + 1) % TSI108_RXRING_LEN;
> + data->rxfree++;
> + done++;
> + }
> +
> + mb();
> +
> + if (done != 0 && !(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_QUEUE0))
> + tsi108_restart_rx(data, dev);
> +
> + return done;
> +}
> +
> +static int tsi108_poll(net_device * dev, int *budget)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 estat = TSI108_ETH_READ_REG(TSI108_EC_RXESTAT);
> + u32 intstat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> + int total_budget = min(*budget, dev->quota);
> + int num_received = 0, num_filled = 0, budget_used;
> +
> + intstat &= TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR | TSI108_INT_RXWAIT;
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXESTAT, estat);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, intstat);
> +
> + if (data->rxpending || (estat & TSI108_EC_RXESTAT_Q0_DESCINT))
> + num_received = tsi108_check_for_completed_rx(dev, total_budget);
> +
> + /* This should normally fill no more slots than the number of
> + * packets received in tsi108_check_for_completed_rx(). The
> exception
> + * is when we previously ran out of memory for RX SKBs. In that
> + * case, it's helpful to obey the budget, not only so that the
> + * CPU isn't hogged, but so that memory (which may still be low)
> + * is not hogged by one device.
> + *
> + * A work unit is considered to be two SKBs to allow us to catch
> + * up when the ring has shrunk due to out-of-memory but we're
> + * still removing the full budget's worth of packets each time.
> + */
> +
> + if (data->rxfree < TSI108_RXRING_LEN)
> + num_filled = tsi108_refill_rx(dev, total_budget * 2);
> +
> + if (intstat & TSI108_INT_RXERROR) {
> + u32 err = TSI108_ETH_READ_REG(TSI108_EC_RXERR);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXERR, err);
> +
> + if (err) {
> + if (net_ratelimit())
> + printk(KERN_DEBUG "%s: RX error %x\n",
> + dev->name, err);
> +
> + if (!(TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_QUEUE0))
> + tsi108_restart_rx(data, dev);
> + }
> + }
> +
> + if (intstat & TSI108_INT_RXOVERRUN) {
> + spin_lock_irq(&data->misclock);
> + data->stats.rx_fifo_errors++;
> + spin_unlock_irq(&data->misclock);
> + }
> +
> + budget_used = max(num_received, num_filled / 2);
> +
> + *budget -= budget_used;
> + dev->quota -= budget_used;
> +
> + if (budget_used != total_budget) {
> + data->rxpending = 0;
> + netif_rx_complete(dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK)
> + & ~(TSI108_INT_RXQUEUE0
> + | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT));
> +
> + mb();
> +
> + /* IRQs are level-triggered, so no need to re-check */
> + return 0;
> + } else {
> + data->rxpending = 1;
> + }
> +
> + return 1;
> +}
> +
> +static void tsi108_rx_int(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + /* A race could cause dev to already be scheduled, so it's not an
> + * error if that happens (and interrupts shouldn't be re-masked,
> + * because that can cause harmful races, if poll has already
> + * unmasked them but not cleared LINK_STATE_SCHED).
> + *
> + * This can happen if this code races with tsi108_poll(), which
> masks
> + * the interrupts after tsi108_irq_one() read the mask, but before
> + * netif_rx_schedule is called. It could also happen due to calls
> + * from tsi108_check_rxring().
> + */
> +
> + if (netif_rx_schedule_prep(dev)) {
> + /* Mask, rather than ack, the receive interrupts. The ack
> + * will happen in tsi108_poll().
> + */
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG(TSI108_EC_INTMASK) |
> + TSI108_INT_RXQUEUE0
> + | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT);
> + mb();
> + __netif_rx_schedule(dev);
> + } else {
> + if (!netif_running(dev)) {
> + /* This can happen if an interrupt occurs while the
> + * interface is being brought down, as the START
> + * bit is cleared before the stop function is called.
> + *
> + * In this case, the interrupts must be masked, or
> + * they will continue indefinitely.
> + *
> + * There's a race here if the interface is brought down
> + * and then up in rapid succession, as the device could
> + * be made running after the above check and before
> + * the masking below. This will only happen if the IRQ
> + * thread has a lower priority than the task brining
> + * up the interface. Fixing this race would likely
> + * require changes in generic code.
> + */
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + TSI108_ETH_READ_REG
> + (TSI108_EC_INTMASK) |
> + TSI108_INT_RXQUEUE0 |
> + TSI108_INT_RXTHRESH |
> + TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR |
> + TSI108_INT_RXWAIT);
> + mb();
> + }
> + }
> +}
> +
> +/* If the RX ring has run out of memory, try periodically
> + * to allocate some more, as otherwise poll would never
> + * get called (apart from the initial end-of-queue condition).
> + *
> + * This is called once per second (by default) from the thread.
> + */
> +
> +static void tsi108_check_rxring(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + /* A poll is scheduled, as opposed to caling tsi108_refill_rx
> + * directly, so as to keep the receive path single-threaded
> + * (and thus not needing a lock).
> + */
> +
> + if (netif_running(dev) && data->rxfree < TSI108_RXRING_LEN / 4)
> + tsi108_rx_int(dev);
> +}
> +
> +static void tsi108_tx_int(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 estat = TSI108_ETH_READ_REG(TSI108_EC_TXESTAT);
> +
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXESTAT, estat);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_TXQUEUE0 |
> + TSI108_INT_TXIDLE | TSI108_INT_TXERROR);
> + mb();
> + if (estat & TSI108_EC_TXESTAT_Q0_ERR) {
> + u32 err = TSI108_ETH_READ_REG(TSI108_EC_TXERR);
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXERR, err);
> +
> + if (err && net_ratelimit())
> + printk(KERN_ERR "%s: TX error %x\n", dev->name, err);
> + }
> +
> + if (estat & (TSI108_EC_TXESTAT_Q0_DESCINT |
> TSI108_EC_TXESTAT_Q0_EOQ)) {
> + spin_lock(&data->txlock);
> + tsi108_check_for_completed_tx(dev);
> + spin_unlock(&data->txlock);
> + }
> +}
> +
> +static irqreturn_t tsi108_irq_one(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 stat = TSI108_ETH_READ_REG(TSI108_EC_INTSTAT);
> +
> + if (!(stat & TSI108_INT_ANY))
> + return IRQ_NONE; /* Not our interrupt */
> +
> + stat &= ~TSI108_ETH_READ_REG(TSI108_EC_INTMASK);
> +
> + if (stat & (TSI108_INT_TXQUEUE0 | TSI108_INT_TXIDLE |
> + TSI108_INT_TXERROR))
> + tsi108_tx_int(dev);
> + if (stat & (TSI108_INT_RXQUEUE0 | TSI108_INT_RXTHRESH |
> + TSI108_INT_RXWAIT | TSI108_INT_RXOVERRUN |
> + TSI108_INT_RXERROR))
> + tsi108_rx_int(dev);
> +
> + if (stat & TSI108_INT_SFN) {
> + if (net_ratelimit())
> + printk(KERN_DEBUG "%s: SFN error\n", dev->name);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_SFN);
> + }
> +
> + if (stat & TSI108_INT_STATCARRY) {
> + tsi108_stat_carry(dev);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, TSI108_INT_STATCARRY);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t tsi108_irq(int irq, void *dev_id, struct
> pt_regs *regs)
> +{
> + if ((IRQ_TSI108_GIGE0 != irq) && (IRQ_TSI108_GIGE1 != irq))
> + return IRQ_NONE; /* Not our interrupt */
> +
> + return tsi108_irq_one((struct net_device *)dev_id);
> +}
> +
> +static void tsi108_stop_ethernet(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + /* Disable all TX and RX queues ... */
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCTRL, 0);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCTRL, 0);
> +
> + /* ...and wait for them to become idle */
> + mb();
> + while (TSI108_ETH_READ_REG(TSI108_EC_TXSTAT) &
> + TSI108_EC_TXSTAT_ACTIVE) ;
> + while (TSI108_ETH_READ_REG(TSI108_EC_RXSTAT) &
> + TSI108_EC_RXSTAT_ACTIVE) ;
> +}
> +
> +static void tsi108_reset_ether(tsi108_prv_data * data)
> +{
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, TSI108_MAC_CFG1_SOFTRST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL,
> TSI108_EC_PORTCTRL_STATRST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL,
> + TSI108_ETH_READ_REG(TSI108_EC_PORTCTRL) &
> + ~TSI108_EC_PORTCTRL_STATRST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG, TSI108_EC_TXCFG_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXCFG,
> + TSI108_ETH_READ_REG(TSI108_EC_TXCFG) &
> + ~TSI108_EC_TXCFG_RST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, TSI108_EC_RXCFG_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> + TSI108_ETH_READ_REG(TSI108_EC_RXCFG) &
> + ~TSI108_EC_RXCFG_RST);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> + TSI108_MAC_MII_MGMT_RST);
> + udelay(100);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) &
> + ~(TSI108_MAC_MII_MGMT_RST |
> + TSI108_MAC_MII_MGMT_CLK));
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_MII_MGMT_CFG,
> + TSI108_ETH_READ_REG(TSI108_MAC_MII_MGMT_CFG) |
> + TSI108_MAC_MII_MGMT_CLK);
> +}
> +
> +static int tsi108_get_mac(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + u32 word1 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR1);
> + u32 word2 = TSI108_ETH_READ_REG(TSI108_MAC_ADDR2);
> +
> + /* Note that the octets are reversed from what the manual says,
> + * producing an even weirder ordering...
> + */
> + if (word2 == 0 && word1 == 0) {
> + dev->dev_addr[0] = 0x00;
> + dev->dev_addr[1] = 0x06;
> + dev->dev_addr[2] = 0xd2;
> + dev->dev_addr[3] = 0x00;
> + dev->dev_addr[4] = 0x00;
> + if (0x8 == data->phy)
> + dev->dev_addr[5] = 0x01;
> + else
> + dev->dev_addr[5] = 0x02;
> +
> + word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
> +
> + word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
> + (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> + } else {
> + dev->dev_addr[0] = (word2 >> 16) & 0xff;
> + dev->dev_addr[1] = (word2 >> 24) & 0xff;
> + dev->dev_addr[2] = (word1 >> 0) & 0xff;
> + dev->dev_addr[3] = (word1 >> 8) & 0xff;
> + dev->dev_addr[4] = (word1 >> 16) & 0xff;
> + dev->dev_addr[5] = (word1 >> 24) & 0xff;
> + }
> +
> + if (!is_valid_ether_addr(dev->dev_addr)) {
> + printk("KERN_ERR: word1: %08x, word2: %08x\n", word1, word2);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int tsi108_set_mac(net_device * dev, void *addr)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 word1, word2;
> + int i;
> +
> + if (!is_valid_ether_addr(addr))
> + return -EINVAL;
> +
> + for (i = 0; i < 6; i++)
> + /* +2 is for the offset of the HW addr type */
> + dev->dev_addr[i] = ((unsigned char *)addr)[i + 2];
> +
> + word2 = (dev->dev_addr[0] << 16) | (dev->dev_addr[1] << 24);
> +
> + word1 = (dev->dev_addr[2] << 0) | (dev->dev_addr[3] << 8) |
> + (dev->dev_addr[4] << 16) | (dev->dev_addr[5] << 24);
> +
> + spin_lock_irq(&data->misclock);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR1, word1);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_ADDR2, word2);
> + spin_lock(&data->txlock);
> +
> + if (netif_queue_stopped(dev) && data->txfree && data->link_up)
> + netif_wake_queue(dev);
> +
> + spin_unlock(&data->txlock);
> + spin_unlock_irq(&data->misclock);
> + return 0;
> +}
> +
> +/* Protected by dev->xmit_lock. */
> +static void tsi108_set_rx_mode(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 rxcfg = TSI108_ETH_READ_REG(TSI108_EC_RXCFG);
> +
> + if (dev->flags & IFF_PROMISC) {
> + rxcfg &= ~(TSI108_EC_RXCFG_UC_HASH | TSI108_EC_RXCFG_MC_HASH);
> + rxcfg |= TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE;
> + goto out;
> + }
> +
> + rxcfg &= ~(TSI108_EC_RXCFG_UFE | TSI108_EC_RXCFG_MFE);
> +
> + if (dev->mc_count) {
> + int i;
> + struct dev_mc_list *mc = dev->mc_list;
> + rxcfg |= TSI108_EC_RXCFG_MFE | TSI108_EC_RXCFG_MC_HASH;
> +
> + memset(data->mc_hash, 0, sizeof(data->mc_hash));
> +
> + while (mc) {
> + u32 hash, crc;
> +
> + if (mc->dmi_addrlen == 6) {
> + crc = ether_crc(6, mc->dmi_addr);
> + hash = crc >> 23;
> +
> + __set_bit(hash, &data->mc_hash[0]);
> + } else {
> + printk(KERN_ERR
> + "%s: got multicast address of length %d "
> + "instead of 6.\n", dev->name,
> + mc->dmi_addrlen);
> + }
> +
> + mc = mc->next;
> + }
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_HASHADDR,
> + TSI108_EC_HASHADDR_AUTOINC |
> + TSI108_EC_HASHADDR_MCAST);
> +
> + for (i = 0; i < 16; i++) {
> + /* The manual says that the hardware may drop
> + * back-to-back writes to the data register.
> + */
> + udelay(1);
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_HASHDATA,
> + data->mc_hash[i]);
> + mb();
> + }
> + }
> +
> + out:
> + mb();
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG, rxcfg);
> +}
> +
> +static void tsi108_init_phy(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + u32 i = 0;
> + u16 phyVal = 0;
> +
> + spin_lock_irq(&phy_lock);
> +
> + tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_RESET);
> + mb();
> + while (tsi108_read_mii(data, PHY_CTRL, NULL) & PHY_CTRL_RESET) ;
> +
> +#if (TSI108_PHY_TYPE == PHY_BCM54XX) /* Broadcom BCM54xx PHY */
> + tsi108_write_mii(data, 0x09, 0x0300);
> + tsi108_write_mii(data, 0x10, 0x1020);
> + tsi108_write_mii(data, 0x1c, 0x8c00);
> + mb();
> +#endif
> +
> + tsi108_write_mii(data,
> + PHY_CTRL,
> + PHY_CTRL_AUTONEG_EN | PHY_CTRL_AUTONEG_START);
> + mb();
> + while (tsi108_read_mii(data, PHY_CTRL, NULL) &
> PHY_CTRL_AUTONEG_START) ;
> +
> + /* Set G/MII mode and receive clock select in TBI control #2. The
> + * second port won't work if this isn't done, even though we don't
> + * use TBI mode.
> + */
> +
> + tsi108_write_tbi(data, 0x11, 0x30);
> +
> + /* FIXME: It seems to take more than 2 back-to-back reads to the
> + * PHY_STAT register before the link up status bit is set.
> + */
> +
> + data->link_up = 1;
> +
> + while (!((phyVal = tsi108_read_mii(data, PHY_STAT, NULL)) &
> + PHY_STAT_LINKUP)) {
> + if (i++ > (MII_READ_DELAY / 10)) {
> + data->link_up = 0;
> + break;
> + }
> + mdelay(10);
> + }
> +
> + printk(KERN_DEBUG "PHY_STAT reg contains %08x\n", phyVal);
> + data->phy_ok = 1;
> + spin_unlock_irq(&phy_lock);
> +}
> +
> +static void tsi108_kill_phy(struct net_device *dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + spin_lock_irq(&phy_lock);
> + tsi108_write_mii(data, PHY_CTRL, PHY_CTRL_POWERDOWN);
> + data->phy_ok = 0;
> + spin_unlock_irq(&phy_lock);
> +}
> +
> +static int tsi108_open(struct net_device *dev)
> +{
> + int i;
> + tsi108_prv_data *data = netdev_priv(dev);
> + unsigned int rxring_size = TSI108_RXRING_LEN * sizeof(rx_desc);
> + unsigned int txring_size = TSI108_TXRING_LEN * sizeof(tx_desc);
> +
> + printk(KERN_DEBUG "Inside tsi108_open()!\n");
> +
> + i = request_irq(data->irq_num, tsi108_irq, 0, dev->name, dev);
> + if (i != 0) {
> + printk(KERN_ERR "tsi108_eth%d: Could not allocate IRQ%d.\n",
> + data->irq_num % IRQ_TSI108_GIGE0, data->irq_num);
> + return i;
> + } else {
> + dev->irq = data->irq_num;
> + printk(KERN_NOTICE
> + "tsi108_open : Port %d Assigned IRQ %d to %s\n",
> + data->irq_num % IRQ_TSI108_GIGE0, dev->irq, dev->name);
> + }
> +
> + data->rxring = pci_alloc_consistent(NULL, rxring_size, &data-
> >rxdma);
> +
> + if (!data->rxring) {
> + printk(KERN_DEBUG
> + "TSI108_ETH: failed to allocate memory for rxring!\n");
> + return -ENOMEM;
> + } else {
> + memset(data->rxring, 0, rxring_size);
> + }
> +
> + data->txring = pci_alloc_consistent(NULL, txring_size, &data-
> >txdma);
> +
> + if (!data->txring) {
> + printk(KERN_DEBUG
> + "TSI108_ETH: failed to allocate memory for txring!\n");
> + pci_free_consistent(0, rxring_size, data->rxring, data->rxdma);
> + return -ENOMEM;
> + } else {
> + memset(data->txring, 0, txring_size);
> + }
> +
> + for (i = 0; i < TSI108_RXRING_LEN; i++) {
> + data->rxring[i].next0 = data->rxdma + (i + 1) * sizeof(rx_desc);
> + data->rxring[i].blen = TSI108_RXBUF_SIZE;
> + data->rxring[i].vlan = 0;
> + }
> +
> + data->rxring[TSI108_RXRING_LEN - 1].next0 = data->rxdma;
> +
> + data->rxtail = 0;
> + data->rxhead = 0;
> +
> + for (i = 0; i < TSI108_RXRING_LEN; i++) {
> + sk_buff *skb = dev_alloc_skb(TSI108_RXBUF_SIZE + NET_IP_ALIGN);
> +
> + if (!skb) {
> + /* Bah. No memory for now, but maybe we'll get
> + * some more later.
> + * For now, we'll live with the smaller ring.
> + */
> + printk(KERN_WARNING
> + "%s: Could only allocate %d receive skb(s).\n",
> + dev->name, i);
> + data->rxhead = i;
> + break;
> + }
> +
> + data->rxskbs[i] = skb;
> + /* Align the payload on a 4-byte boundary */
> + skb_reserve(skb, 2);
> + data->rxskbs[i] = skb;
> + data->rxring[i].buf0 = virt_to_phys(data->rxskbs[i]->data);
> + data->rxring[i].misc = TSI108_RX_OWN | TSI108_RX_INT;
> + }
> +
> + data->rxfree = i;
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_PTRLOW, data->rxdma);
> +
> + for (i = 0; i < TSI108_TXRING_LEN; i++) {
> + data->txring[i].next0 = data->txdma + (i + 1) * sizeof(tx_desc);
> + data->txring[i].misc = 0;
> + }
> +
> + data->txring[TSI108_TXRING_LEN - 1].next0 = data->txdma;
> + data->txtail = 0;
> + data->txhead = 0;
> + data->txfree = TSI108_TXRING_LEN;
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_PTRLOW, data->txdma);
> + tsi108_init_phy(dev);
> +
> + init_timer(&data->timer);
> + data->timer.expires = jiffies + 1;
> + data->timer.data = (unsigned long)dev;
> + data->timer.function = &tsi108_timed_checker; /* timer handler */
> + add_timer(&data->timer);
> +
> + tsi108_restart_rx(data, dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTSTAT, ~0);
> + mb();
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK,
> + ~(TSI108_INT_TXQUEUE0 | TSI108_INT_RXERROR |
> + TSI108_INT_RXTHRESH | TSI108_INT_RXQUEUE0 |
> + TSI108_INT_RXOVERRUN | TSI108_INT_RXWAIT |
> + TSI108_INT_SFN | TSI108_INT_STATCARRY));
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1,
> + TSI108_MAC_CFG1_RXEN | TSI108_MAC_CFG1_TXEN);
> + netif_start_queue(dev);
> + return 0;
> +}
> +
> +static int tsi108_close(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + netif_stop_queue(dev);
> +
> + del_timer_sync(&data->timer);
> +
> + printk(KERN_DEBUG "Inside tsi108_ifdown!\n");
> +
> + tsi108_stop_ethernet(dev);
> + tsi108_kill_phy(dev);
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + /* Check for any pending TX packets, and drop them. */
> +
> + while (!data->txfree || data->txhead != data->txtail) {
> + int tx = data->txtail;
> + struct sk_buff *skb;
> + skb = data->txskbs[tx];
> + data->txtail = (data->txtail + 1) % TSI108_TXRING_LEN;
> + data->txfree++;
> + dev_kfree_skb(skb);
> + }
> +
> + synchronize_irq(data->irq_num);
> + free_irq(data->irq_num, dev);
> +
> + /* Discard the RX ring. */
> +
> + while (data->rxfree) {
> + int rx = data->rxtail;
> + struct sk_buff *skb;
> +
> + skb = data->rxskbs[rx];
> + data->rxtail = (data->rxtail + 1) % TSI108_RXRING_LEN;
> + data->rxfree--;
> + dev_kfree_skb(skb);
> + }
> +
> + pci_free_consistent(0,
> + TSI108_RXRING_LEN * sizeof(rx_desc),
> + data->rxring, data->rxdma);
> + pci_free_consistent(0,
> + TSI108_TXRING_LEN * sizeof(tx_desc),
> + data->txring, data->txdma);
> +
> + return 0;
> +}
> +
> +static void tsi108_init_mac(net_device * dev)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG2,
> TSI108_MAC_CFG2_DFLT_PREAMBLE |
> + TSI108_MAC_CFG2_PADCRC);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXTHRESH,
> + (192 << TSI108_EC_TXTHRESH_STARTFILL) |
> + (192 << TSI108_EC_TXTHRESH_STOPFILL));
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK1,
> + ~(TSI108_STAT_CARRY1_RXBYTES |
> + TSI108_STAT_CARRY1_RXPKTS |
> + TSI108_STAT_CARRY1_RXFCS |
> + TSI108_STAT_CARRY1_RXMCAST |
> + TSI108_STAT_CARRY1_RXALIGN |
> + TSI108_STAT_CARRY1_RXLENGTH |
> + TSI108_STAT_CARRY1_RXRUNT |
> + TSI108_STAT_CARRY1_RXJUMBO |
> + TSI108_STAT_CARRY1_RXFRAG |
> + TSI108_STAT_CARRY1_RXJABBER |
> + TSI108_STAT_CARRY1_RXDROP));
> +
> + TSI108_ETH_WRITE_REG(TSI108_STAT_CARRYMASK2,
> + ~(TSI108_STAT_CARRY2_TXBYTES |
> + TSI108_STAT_CARRY2_TXPKTS |
> + TSI108_STAT_CARRY2_TXEXDEF |
> + TSI108_STAT_CARRY2_TXEXCOL |
> + TSI108_STAT_CARRY2_TXTCOL |
> + TSI108_STAT_CARRY2_TXPAUSE));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_PORTCTRL, TSI108_EC_PORTCTRL_STATEN);
> + TSI108_ETH_WRITE_REG(TSI108_MAC_CFG1, 0);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXCFG,
> + TSI108_EC_RXCFG_SE | TSI108_EC_RXCFG_BFE);
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_CFG, TSI108_EC_TXQ_CFG_DESC_INT |
> + TSI108_EC_TXQ_CFG_EOQ_OWN_INT |
> + TSI108_EC_TXQ_CFG_WSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_TXQ_CFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_CFG, TSI108_EC_RXQ_CFG_DESC_INT |
> + TSI108_EC_RXQ_CFG_EOQ_OWN_INT |
> + TSI108_EC_RXQ_CFG_WSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_RXQ_CFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_TXQ_BUFCFG,
> + TSI108_EC_TXQ_BUFCFG_BURST256 |
> + TSI108_EC_TXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_TXQ_BUFCFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_RXQ_BUFCFG,
> + TSI108_EC_RXQ_BUFCFG_BURST256 |
> + TSI108_EC_RXQ_BUFCFG_BSWP | (TSI108_PBM_PORT <<
> + TSI108_EC_RXQ_BUFCFG_SFNPORT));
> +
> + TSI108_ETH_WRITE_REG(TSI108_EC_INTMASK, ~0);
> +}
> +
> +static int tsi108_ioctl(net_device * dev, struct ifreq *rq, int cmd)
> +{
> + tsi108_prv_data *data = netdev_priv(dev);
> + struct mii_ioctl_data *mii_data =
> + (struct mii_ioctl_data *)&rq->ifr_data;
> + int ret;
> +
> + switch (cmd) {
> + case SIOCGMIIPHY:
> + mii_data->phy_id = data->phy;
> + ret = 0;
> + break;
> +
> + case SIOCGMIIREG:
> + spin_lock_irq(&phy_lock);
> + mii_data->val_out =
> + tsi108_read_mii(data, mii_data->reg_num, &ret);
> + spin_unlock_irq(&phy_lock);
> + break;
> +
> + default:
> + ret = -EOPNOTSUPP;
> + }
> +
> + return ret;
> +}
> +
> +static int
> +tsi108_init_one(unsigned long regs, unsigned long phyregs, u16
> phy, u16 irq_num)
> +{
> + net_device *dev = alloc_etherdev(sizeof(tsi108_prv_data));
> + tsi108_prv_data *data;
> + int ret;
> +
> + if (!dev) {
> + printk("tsi108_eth: Could not allocate a device structure\n");
> + return -ENOMEM;
> + }
> +
> + data = netdev_priv(dev);
> + memset(data, 0, sizeof(tsi108_prv_data));
> +
> + data->regs = (volatile u32)regs;
> + data->phyregs = (volatile u32)phyregs;
> + data->phy = phy;
> + data->irq_num = irq_num;
> +
> + dev->open = tsi108_open;
> + dev->stop = tsi108_close;
> + dev->hard_start_xmit = tsi108_send_packet;
> + dev->set_mac_address = tsi108_set_mac;
> + dev->set_multicast_list = tsi108_set_rx_mode;
> + dev->get_stats = tsi108_get_stats;
> + dev->poll = tsi108_poll;
> + dev->do_ioctl = tsi108_ioctl;
> + dev->weight = 64; /* 64 is more suitable for GigE interface -
> klai */
> +
> + /* Apparently, the Linux networking code won't use scatter-gather
> + * if the hardware doesn't do checksums. However, it's faster
> + * to checksum in place and use SG, as (among other reasons)
> + * the cache won't be dirtied (which then has to be flushed
> + * before DMA). The checksumming is done by the driver (via
> + * a new function skb_csum_dev() in net/core/skbuff.c).
> + */
> +
> +#ifdef FIXME_SG_CSUM_NOT_TESTED /* FIXME: Not supported now. */
> + dev->features = NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_HW_CSUM;
> +#else
> + dev->features = NETIF_F_HIGHDMA;
> +#endif
> + SET_MODULE_OWNER(dev);
> +
> + spin_lock_init(&data->txlock);
> + spin_lock_init(&data->misclock);
> +
> + tsi108_reset_ether(data);
> + tsi108_kill_phy(dev);
> +
> + if (tsi108_get_mac(dev) != 0)
> + printk(KERN_ERR "%s: Invalid MAC address. Please correct.\n",
> + dev->name);
> +
> + tsi108_init_mac(dev);
> +
> + tsi108_devs[irq_num % IRQ_TSI108_GIGE0] = dev;
> +
> + ret = register_netdev(dev);
> + if (ret < 0) {
> + kfree(dev);
> + return ret;
> + }
> +
> + printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: "
> + "%02x:%02x:%02x:%02x:%02x:%02x\n", dev->name,
> + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
> + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
> +
> + return 0;
> +}
> +
> +/* There's no way to either get interrupts from the PHY when
> + * something changes, or to have the Tsi108 automatically communicate
> + * with the PHY to reconfigure itself.
> + *
> + * Thus, we have to do it using a timer.
> + */
> +
> +static void tsi108_timed_checker(unsigned long dev_ptr)
> +{
> + struct net_device *dev = (struct net_device *)dev_ptr;
> + tsi108_prv_data *data = netdev_priv(dev);
> +
> + tsi108_check_phy(dev);
> + tsi108_check_rxring(dev);
> + data->timer.expires = jiffies + CHECK_PHY_INTERVAL;
> + add_timer(&data->timer);
> +}
> +
> +static int tsi108_ether_init(void)
> +{
> + int ret;
> + int dev_count = 0;
> + int i;
> +
> + hw_info_table[0].regs = (u32) ioremap(hw_info_table[0].regs, 0x400);
> + hw_info_table[0].phyregs = hw_info_table[0].regs;
> +
> + hw_info_table[1].regs = (u32) ioremap(hw_info_table[1].regs, 0x400);
> + hw_info_table[1].phyregs = hw_info_table[0].regs;
> +
> + for (i = 0; hw_info_table[i].regs != TBL_END; i++) {
> + ret = tsi108_init_one(hw_info_table[i].regs,
> + hw_info_table[i].phyregs,
> + hw_info_table[i].phy,
> + hw_info_table[i].irq_num);
> + if (ret < 0)
> + printk("tsi108_ether_init: error initializing ethernet "
> + "device%d\n", i);
> + else
> + dev_count++;
> + }
> +
> + printk("tsi108_ether_init: found %d device(s)\n", dev_count);
> +
> + return 0;
> +}
> +
> +static void tsi108_ether_exit(void)
> +{
> + int i;
> + net_device *dev;
> +
> + for (i = 0; hw_info_table[i].regs != TBL_END; i++) {
> + if ((dev = tsi108_devs[i]) != NULL) {
> + unregister_netdev(dev);
> + tsi108_stop_ethernet(dev);
> + kfree(dev);
> + tsi108_devs[i] = NULL;
> + }
> + }
> +}
> +
> +module_init(tsi108_ether_init);
> +module_exit(tsi108_ether_exit);
> +
> +MODULE_AUTHOR("Tundra Semiconductor Corporation");
> +MODULE_DESCRIPTION("Tsi108 Gigabit Ethernet driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/tsi108_eth.h b/drivers/net/tsi108_eth.h
> new file mode 100644
> index 0000000..cb54f92
> --- /dev/null
> +++ b/drivers/net/tsi108_eth.h
> @@ -0,0 +1,404 @@
> +/*
> + * (C) Copyright 2005 Tundra Semiconductor Corp.
> + * Kong Lai, <kong.lai@tundra.com).
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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 Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +/*
> + * net/tsi108_eth.h - definitions for Tsi108 GIGE network controller.
> + */
> +
> +#ifndef __TSI108_ETH_H
> +#define __TSI108_ETH_H
> +
> +#include <linux/config.h>
> +#include <linux/types.h>
> +
> +#define TSI108_ETH_WRITE_REG(offset, val) \
> + out_be32((volatile u32 *)(data->regs + offset), val)
> +
> +#define TSI108_ETH_READ_REG(offset) \
> + in_be32((volatile u32 *)(data->regs + offset))
> +
> +#define TSI108_ETH_WRITE_PHYREG(offset, val) \
> + out_be32((volatile u32 *)(data->phyregs + offset), val)
> +
> +#define TSI108_ETH_READ_PHYREG(offset) \
> + in_be32((volatile u32 *)(data->phyregs + offset))
> +
> +/*
> + * PHY Configuration Options
> + *
> + * NOTE: Enable set of definitions corresponding to your board type
> + */
> +#define PHY_MV88E 1 /* Marvel 88Exxxx PHY */
> +#define PHY_BCM54XX 2 /* Broardcom BCM54xx PHY */
> +#define TSI108_PHY_TYPE PHY_MV88E
> +
> +/*
> + * TSI108 GIGE port registers
> + */
> +
> +#define TSI108_ETH_PORT_NUM 2
> +#define TSI108_PBM_PORT 2
> +#define TSI108_SDRAM_PORT 4
> +
> +#define TSI108_MAC_CFG1 (0x000)
> +#define TSI108_MAC_CFG1_SOFTRST (1 << 31)
> +#define TSI108_MAC_CFG1_LOOPBACK (1 << 8)
> +#define TSI108_MAC_CFG1_RXEN (1 << 2)
> +#define TSI108_MAC_CFG1_TXEN (1 << 0)
> +
> +#define TSI108_MAC_CFG2 (0x004)
> +#define TSI108_MAC_CFG2_DFLT_PREAMBLE (7 << 12)
> +#define TSI108_MAC_CFG2_IFACE_MASK (3 << 8)
> +#define TSI108_MAC_CFG2_NOGIG (1 << 8)
> +#define TSI108_MAC_CFG2_GIG (2 << 8)
> +#define TSI108_MAC_CFG2_PADCRC (1 << 2)
> +#define TSI108_MAC_CFG2_FULLDUPLEX (1 << 0)
> +
> +#define TSI108_MAC_MII_MGMT_CFG (0x020)
> +#define TSI108_MAC_MII_MGMT_CLK (7 << 0)
> +#define TSI108_MAC_MII_MGMT_RST (1 << 31)
> +
> +#define TSI108_MAC_MII_CMD (0x024)
> +#define TSI108_MAC_MII_CMD_READ (1 << 0)
> +
> +#define TSI108_MAC_MII_ADDR (0x028)
> +#define TSI108_MAC_MII_ADDR_REG 0
> +#define TSI108_MAC_MII_ADDR_PHY 8
> +
> +#define TSI108_MAC_MII_DATAOUT (0x02c)
> +#define TSI108_MAC_MII_DATAIN (0x030)
> +
> +#define TSI108_MAC_MII_IND (0x034)
> +#define TSI108_MAC_MII_IND_NOTVALID (1 << 2)
> +#define TSI108_MAC_MII_IND_SCANNING (1 << 1)
> +#define TSI108_MAC_MII_IND_BUSY (1 << 0)
> +
> +#define TSI108_MAC_IFCTRL (0x038)
> +#define TSI108_MAC_IFCTRL_PHYMODE (1 << 24)
> +
> +#define TSI108_MAC_ADDR1 (0x040)
> +#define TSI108_MAC_ADDR2 (0x044)
> +
> +#define TSI108_STAT_RXBYTES (0x06c)
> +#define TSI108_STAT_RXBYTES_CARRY (1 << 24)
> +
> +#define TSI108_STAT_RXPKTS (0x070)
> +#define TSI108_STAT_RXPKTS_CARRY (1 << 18)
> +
> +#define TSI108_STAT_RXFCS (0x074)
> +#define TSI108_STAT_RXFCS_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXMCAST (0x078)
> +#define TSI108_STAT_RXMCAST_CARRY (1 << 18)
> +
> +#define TSI108_STAT_RXALIGN (0x08c)
> +#define TSI108_STAT_RXALIGN_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXLENGTH (0x090)
> +#define TSI108_STAT_RXLENGTH_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXRUNT (0x09c)
> +#define TSI108_STAT_RXRUNT_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXJUMBO (0x0a0)
> +#define TSI108_STAT_RXJUMBO_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXFRAG (0x0a4)
> +#define TSI108_STAT_RXFRAG_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXJABBER (0x0a8)
> +#define TSI108_STAT_RXJABBER_CARRY (1 << 12)
> +
> +#define TSI108_STAT_RXDROP (0x0ac)
> +#define TSI108_STAT_RXDROP_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXBYTES (0x0b0)
> +#define TSI108_STAT_TXBYTES_CARRY (1 << 24)
> +
> +#define TSI108_STAT_TXPKTS (0x0b4)
> +#define TSI108_STAT_TXPKTS_CARRY (1 << 18)
> +
> +#define TSI108_STAT_TXEXDEF (0x0c8)
> +#define TSI108_STAT_TXEXDEF_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXEXCOL (0x0d8)
> +#define TSI108_STAT_TXEXCOL_CARRY (1 << 12)
> +
> +#define TSI108_STAT_TXTCOL (0x0dc)
> +#define TSI108_STAT_TXTCOL_CARRY (1 << 13)
> +
> +#define TSI108_STAT_TXPAUSEDROP (0x0e4)
> +#define TSI108_STAT_TXPAUSEDROP_CARRY (1 << 12)
> +
> +#define TSI108_STAT_CARRY1 (0x100)
> +#define TSI108_STAT_CARRY1_RXBYTES (1 << 16)
> +#define TSI108_STAT_CARRY1_RXPKTS (1 << 15)
> +#define TSI108_STAT_CARRY1_RXFCS (1 << 14)
> +#define TSI108_STAT_CARRY1_RXMCAST (1 << 13)
> +#define TSI108_STAT_CARRY1_RXALIGN (1 << 8)
> +#define TSI108_STAT_CARRY1_RXLENGTH (1 << 7)
> +#define TSI108_STAT_CARRY1_RXRUNT (1 << 4)
> +#define TSI108_STAT_CARRY1_RXJUMBO (1 << 3)
> +#define TSI108_STAT_CARRY1_RXFRAG (1 << 2)
> +#define TSI108_STAT_CARRY1_RXJABBER (1 << 1)
> +#define TSI108_STAT_CARRY1_RXDROP (1 << 0)
> +
> +#define TSI108_STAT_CARRY2 (0x104)
> +#define TSI108_STAT_CARRY2_TXBYTES (1 << 13)
> +#define TSI108_STAT_CARRY2_TXPKTS (1 << 12)
> +#define TSI108_STAT_CARRY2_TXEXDEF (1 << 7)
> +#define TSI108_STAT_CARRY2_TXEXCOL (1 << 3)
> +#define TSI108_STAT_CARRY2_TXTCOL (1 << 2)
> +#define TSI108_STAT_CARRY2_TXPAUSE (1 << 0)
> +
> +#define TSI108_STAT_CARRYMASK1 (0x108)
> +#define TSI108_STAT_CARRYMASK2 (0x10c)
> +
> +#define TSI108_EC_PORTCTRL (0x200)
> +#define TSI108_EC_PORTCTRL_STATRST (1 << 31)
> +#define TSI108_EC_PORTCTRL_STATEN (1 << 28)
> +#define TSI108_EC_PORTCTRL_NOGIG (1 << 18)
> +#define TSI108_EC_PORTCTRL_HALFDUPLEX (1 << 16)
> +
> +#define TSI108_EC_INTSTAT (0x204)
> +#define TSI108_EC_INTMASK (0x208)
> +
> +#define TSI108_INT_ANY (1 << 31)
> +#define TSI108_INT_SFN (1 << 30)
> +#define TSI108_INT_RXIDLE (1 << 29)
> +#define TSI108_INT_RXABORT (1 << 28)
> +#define TSI108_INT_RXERROR (1 << 27)
> +#define TSI108_INT_RXOVERRUN (1 << 26)
> +#define TSI108_INT_RXTHRESH (1 << 25)
> +#define TSI108_INT_RXWAIT (1 << 24)
> +#define TSI108_INT_RXQUEUE0 (1 << 16)
> +#define TSI108_INT_STATCARRY (1 << 15)
> +#define TSI108_INT_TXIDLE (1 << 13)
> +#define TSI108_INT_TXABORT (1 << 12)
> +#define TSI108_INT_TXERROR (1 << 11)
> +#define TSI108_INT_TXUNDERRUN (1 << 10)
> +#define TSI108_INT_TXTHRESH (1 << 9)
> +#define TSI108_INT_TXWAIT (1 << 8)
> +#define TSI108_INT_TXQUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXCFG (0x220)
> +#define TSI108_EC_TXCFG_RST (1 << 31)
> +
> +#define TSI108_EC_TXCTRL (0x224)
> +#define TSI108_EC_TXCTRL_IDLEINT (1 << 31)
> +#define TSI108_EC_TXCTRL_ABORT (1 << 30)
> +#define TSI108_EC_TXCTRL_GO (1 << 15)
> +#define TSI108_EC_TXCTRL_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXSTAT (0x228)
> +#define TSI108_EC_TXSTAT_ACTIVE (1 << 15)
> +#define TSI108_EC_TXSTAT_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_TXESTAT (0x22c)
> +#define TSI108_EC_TXESTAT_Q0_ERR (1 << 24)
> +#define TSI108_EC_TXESTAT_Q0_DESCINT (1 << 16)
> +#define TSI108_EC_TXESTAT_Q0_EOF (1 << 8)
> +#define TSI108_EC_TXESTAT_Q0_EOQ (1 << 0)
> +
> +#define TSI108_EC_TXERR (0x278)
> +
> +#define TSI108_EC_TXQ_CFG (0x280)
> +#define TSI108_EC_TXQ_CFG_DESC_INT (1 << 20)
> +#define TSI108_EC_TXQ_CFG_EOQ_OWN_INT (1 << 19)
> +#define TSI108_EC_TXQ_CFG_WSWP (1 << 11)
> +#define TSI108_EC_TXQ_CFG_BSWP (1 << 10)
> +#define TSI108_EC_TXQ_CFG_SFNPORT 0
> +
> +#define TSI108_EC_TXQ_BUFCFG (0x284)
> +#define TSI108_EC_TXQ_BUFCFG_BURST8 (0 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST32 (1 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST128 (2 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_BURST256 (3 << 8)
> +#define TSI108_EC_TXQ_BUFCFG_WSWP (1 << 11)
> +#define TSI108_EC_TXQ_BUFCFG_BSWP (1 << 10)
> +#define TSI108_EC_TXQ_BUFCFG_SFNPORT 0
> +
> +#define TSI108_EC_TXQ_PTRLOW (0x288)
> +
> +#define TSI108_EC_TXQ_PTRHIGH (0x28c)
> +#define TSI108_EC_TXQ_PTRHIGH_VALID (1 << 31)
> +
> +#define TSI108_EC_TXTHRESH (0x230)
> +#define TSI108_EC_TXTHRESH_STARTFILL 0
> +#define TSI108_EC_TXTHRESH_STOPFILL 16
> +
> +#define TSI108_EC_RXCFG (0x320)
> +#define TSI108_EC_RXCFG_RST (1 << 31)
> +
> +#define TSI108_EC_RXSTAT (0x328)
> +#define TSI108_EC_RXSTAT_ACTIVE (1 << 15)
> +#define TSI108_EC_RXSTAT_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_RXESTAT (0x32c)
> +#define TSI108_EC_RXESTAT_Q0_ERR (1 << 24)
> +#define TSI108_EC_RXESTAT_Q0_DESCINT (1 << 16)
> +#define TSI108_EC_RXESTAT_Q0_EOF (1 << 8)
> +#define TSI108_EC_RXESTAT_Q0_EOQ (1 << 0)
> +
> +#define TSI108_EC_HASHADDR (0x360)
> +#define TSI108_EC_HASHADDR_AUTOINC (1 << 31)
> +#define TSI108_EC_HASHADDR_DO1STREAD (1 << 30)
> +#define TSI108_EC_HASHADDR_UNICAST (0 << 4)
> +#define TSI108_EC_HASHADDR_MCAST (1 << 4)
> +
> +#define TSI108_EC_HASHDATA (0x364)
> +
> +#define TSI108_EC_RXQ_PTRLOW (0x388)
> +
> +#define TSI108_EC_RXQ_PTRHIGH (0x38c)
> +#define TSI108_EC_RXQ_PTRHIGH_VALID (1 << 31)
> +
> +/* Station Enable -- accept packets destined for us */
> +#define TSI108_EC_RXCFG_SE (1 << 13)
> +/* Unicast Frame Enable -- for packets not destined for us */
> +#define TSI108_EC_RXCFG_UFE (1 << 12)
> +/* Multicast Frame Enable */
> +#define TSI108_EC_RXCFG_MFE (1 << 11)
> +/* Broadcast Frame Enable */
> +#define TSI108_EC_RXCFG_BFE (1 << 10)
> +#define TSI108_EC_RXCFG_UC_HASH (1 << 9)
> +#define TSI108_EC_RXCFG_MC_HASH (1 << 8)
> +
> +#define TSI108_EC_RXQ_CFG (0x380)
> +#define TSI108_EC_RXQ_CFG_DESC_INT (1 << 20)
> +#define TSI108_EC_RXQ_CFG_EOQ_OWN_INT (1 << 19)
> +#define TSI108_EC_RXQ_CFG_WSWP (1 << 11)
> +#define TSI108_EC_RXQ_CFG_BSWP (1 << 10)
> +#define TSI108_EC_RXQ_CFG_SFNPORT 0
> +
> +#define TSI108_EC_RXQ_BUFCFG (0x384)
> +#define TSI108_EC_RXQ_BUFCFG_BURST8 (0 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST32 (1 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST128 (2 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_BURST256 (3 << 8)
> +#define TSI108_EC_RXQ_BUFCFG_WSWP (1 << 11)
> +#define TSI108_EC_RXQ_BUFCFG_BSWP (1 << 10)
> +#define TSI108_EC_RXQ_BUFCFG_SFNPORT 0
> +
> +#define TSI108_EC_RXCTRL (0x324)
> +#define TSI108_EC_RXCTRL_ABORT (1 << 30)
> +#define TSI108_EC_RXCTRL_GO (1 << 15)
> +#define TSI108_EC_RXCTRL_QUEUE0 (1 << 0)
> +
> +#define TSI108_EC_RXERR (0x378)
> +
> +#define PHY_CTRL 0
> +#define PHY_CTRL_RESET (1 << 15)
> +#define PHY_CTRL_AUTONEG_EN (1 << 12)
> +#define PHY_CTRL_POWERDOWN (1 << 11)
> +#define PHY_CTRL_AUTONEG_START (1 << 9)
> +
> +#define PHY_STAT 1
> +#define PHY_STAT_LINKUP (1 << 2)
> +
> +#if (TSI108_PHY_TYPE == PHY_MV88E)
> +/* Marvel 88E1xxx-specific */
> +#define PHY_SUM_STAT 0x11
> +#define PHY_SUM_STAT_SPEED_MASK (3 << 14)
> +#define PHY_SUM_STAT_SPEED_10 (0 << 14)
> +#define PHY_SUM_STAT_SPEED_100 (1 << 14)
> +#define PHY_SUM_STAT_SPEED_1000 (2 << 14)
> +#define PHY_SUM_STAT_FULLDUPLEX (1 << 13)
> +
> +#define PHY_SUM_STAT_1000T_FD (PHY_SUM_STAT_SPEED_1000 |
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_1000T_HD (PHY_SUM_STAT_SPEED_1000)
> +#define PHY_SUM_STAT_100TX_FD (PHY_SUM_STAT_SPEED_100 |
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_100TX_HD (PHY_SUM_STAT_SPEED_100)
> +#define PHY_SUM_STAT_10T_FD (PHY_SUM_STAT_SPEED_10 |
> PHY_SUM_STAT_FULLDUPLEX)
> +#define PHY_SUM_STAT_10T_HD (PHY_SUM_STAT_SPEED_10)
> +#elif (TSI108_PHY_TYPE == PHY_BCM54XX)
> +/*Broadcom BCM54xx */
> +#define PHY_SUM_STAT 0x19
> +#define PHY_SUM_STAT_SPEED_MASK (7 << 8) /* Auto Negotiation HCD */
> +#define PHY_SUM_STAT_1000T_FD (7 << 8) /* 1000BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_1000T_HD (6 << 8) /* 1000BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_100TX_FD (5 << 8) /* 100BASE-TX Full-Duplex */
> +#define PHY_SUM_STAT_100T4 (4 << 8) /* 100BASE-T4 */
> +#define PHY_SUM_STAT_100TX_HD (3 << 8) /* 100BASE-TX Half-Duplex */
> +#define PHY_SUM_STAT_10T_FD (2 << 8) /* 10BASE-T Full-Duplex */
> +#define PHY_SUM_STAT_10T_HD (1 << 8) /* 10BASE-T Half-Duplex */
> +#define PHY_SUM_STAT_AN_FAIL (0 << 8) /* 1000BASE-T Half-Duplex */
> +#else
> +#error "PHY Device not specified"
> +#endif /* MV88_PHY */
> +
> +#define TSI108_TX_EOF (1 << 0) /* End of frame; last fragment of
> packet */
> +#define TSI108_TX_SOF (1 << 1) /* Start of frame; first frag. of
> packet */
> +#define TSI108_TX_VLAN (1 << 2) /* Per-frame VLAN: enables VLAN
> override */
> +#define TSI108_TX_HUGE (1 << 3) /* Huge frame enable */
> +#define TSI108_TX_PAD (1 << 4) /* Pad the packet if too short */
> +#define TSI108_TX_CRC (1 << 5) /* Generate CRC for this packet */
> +#define TSI108_TX_INT (1 << 14) /* Generate an IRQ after frag.
> processed */
> +#define TSI108_TX_RETRY 16 /* 4 bit field indicating num. of
> retries */
> +#define TSI108_TX_COL (1 << 20) /* Set if a collision occured */
> +#define TSI108_TX_LCOL (1 << 24) /* Set if a late collision
> occured */
> +#define TSI108_TX_UNDER (1 << 25) /* Set if a FIFO underrun
> occured */
> +#define TSI108_TX_RLIM (1 << 26) /* Set if the retry limit was
> reached */
> +#define TSI108_TX_OK (1 << 30) /* Set if the frame TX was
> successful */
> +#define TSI108_TX_OWN (1 << 31) /* Set if the device owns the
> descriptor */
> +
> +/* Note: the descriptor layouts assume big-endian byte order. */
> +typedef struct {
> + u32 buf0;
> + u32 buf1; /* Base address of buffer */
> + u32 next0; /* Address of next descriptor, if any */
> + u32 next1;
> + u16 vlan; /* VLAN, if override enabled for this packet */
> + u16 len; /* Length of buffer in bytes */
> + u32 misc; /* See TSI108_TX_* above */
> + u32 reserved0; /*reserved0 and reserved1 are added to make the
> desc */
> + u32 reserved1; /* 32-byte aligned */
> +} __attribute__ ((aligned(32))) tx_desc;
> +
> +#define TSI108_RX_EOF (1 << 0) /* End of frame; last fragment of
> packet */
> +#define TSI108_RX_SOF (1 << 1) /* Start of frame; first frag. of
> packet */
> +#define TSI108_RX_VLAN (1 << 2) /* Set on SOF if packet has a VLAN */
> +#define TSI108_RX_FTYPE (1 << 3) /* Length/Type field is type, not
> length */
> +#define TSI108_RX_RUNT (1 << 4)/* Packet is less than minimum size */
> +#define TSI108_RX_HASH (1 << 7)/* Hash table match */
> +#define TSI108_RX_BAD (1 << 8) /* Bad frame */
> +#define TSI108_RX_OVER (1 << 9) /* FIFO overrun occured */
> +#define TSI108_RX_TRUNC (1 << 11) /* Packet truncated due to
> excess length */
> +#define TSI108_RX_CRC (1 << 12) /* Packet had a CRC error */
> +#define TSI108_RX_INT (1 << 13) /* Generate an IRQ after frag.
> processed */
> +#define TSI108_RX_OWN (1 << 15) /* Set if the device owns the
> descriptor */
> +
> +typedef struct {
> + u32 buf0; /* Base address of buffer */
> + u32 buf1; /* Base address of buffer */
> + u32 next0; /* Address of next descriptor, if any */
> + u32 next1; /* Address of next descriptor, if any */
> + u16 vlan; /* VLAN of received packet, first frag only */
> + u16 len; /* Length of received fragment in bytes */
> + u16 blen; /* Length of buffer in bytes */
> + u16 misc; /* See TSI108_RX_* above */
> + u32 reserved0; /* reserved0 and reserved1 are added to make the
> desc */
> + u32 reserved1; /* 32-byte aligned */
> +} __attribute__ ((aligned(32))) rx_desc;
> +
> +#endif /* __TSI108_ETH_H */
> --
> 1.3.0
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev
^ permalink raw reply
* RE: [PATCH] Gianfar SKB Recycling Support
From: Haruki Dai-r35557 @ 2006-05-18 2:03 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev, Fleming Andy-afleming, Kumar Gala
> -----Original Message-----
> From: Stephen Hemminger [mailto:shemminger@osdl.org]
> Sent: Wednesday, May 17, 2006 5:49 PM
> To: Haruki Dai-r35557
> Cc: netdev@vger.kernel.org; Fleming Andy-afleming; Kumar
> Gala; Haruki Dai-r35557
> Subject: Re: [PATCH] Gianfar SKB Recycling Support
>
> On Wed, 17 May 2006 15:45:14 -0700
> "Haruki Dai-r35557" <Dai.Haruki@freescale.com> wrote:
>
> > This patch improves the IP forwarding throughput of
> > the Freescale TSEC/eTSEC Gianfar driver. By recycling
> > the Socket buffer and Data buffer, reduce the unnecessary
> > memory allocation and de-allocation in the forwarding
> > processing chain.
> >
> > Signed-off-by: Dai Haruki <dai.haruki@freescale.com>
> > Signed-off-by: Andy Fleming <afleming@freescale.com>
> >
>
> What if I am forwarding from gianfar to e1000?
>
If the traffic is bidirectional, then the throughput is going up.
If, the e1000 driver also has the buffer recycling routine implemented,
boost up in the both direction.
Regards
Dai
^ permalink raw reply
* RE: [PATCH] Gianfar SKB Recycling Support
From: Haruki Dai-r35557 @ 2006-05-18 2:01 UTC (permalink / raw)
To: Andi Kleen; +Cc: netdev, Fleming Andy-afleming, Kumar Gala
> -----Original Message-----
> From: Andi Kleen [mailto:ak@suse.de]
> Sent: Wednesday, May 17, 2006 5:56 PM
> To: Haruki Dai-r35557
> Cc: netdev@vger.kernel.org; Fleming Andy-afleming; Kumar Gala
> Subject: Re: [PATCH] Gianfar SKB Recycling Support
>
> On Thursday 18 May 2006 00:45, Haruki Dai-r35557 wrote:
> > This patch improves the IP forwarding throughput of
> > the Freescale TSEC/eTSEC Gianfar driver. By recycling
> > the Socket buffer and Data buffer, reduce the unnecessary
> > memory allocation and de-allocation in the forwarding
> > processing chain.
>
> Seems very hackish because it only works inside the driver.
>
> Also there is a reason why Linux doesn't support this normally -
> it wrecks the unified memory management because you won't free
> your pools on memory pressure.
>
> -Andi
>
With grant of the description, it looks negative in the memory management, but actually, the amount of memory usage in the driver layer is less than the ordinaly gianfar (around half), especially the NAPI is enable. This recycling is introduced in order to chop down the critical path memory usage.
Forwarding performance goes up 60 to 100% better, and amount of memory usage is half.
Thanks for comment,
Dai
^ permalink raw reply
* Re: [PATCH 3/4] myri10ge - Driver core
From: Arnd Bergmann @ 2006-05-17 23:08 UTC (permalink / raw)
To: Brice Goglin; +Cc: netdev, gallatin, linux-kernel
In-Reply-To: <20060517220608.GD13411@myri.com>
Am Thursday 18 May 2006 00:06 schrieb Brice Goglin:
> +static char *myri10ge_fw_name = NULL;
> +static char *myri10ge_fw_unaligned = "myri10ge_ethp_z8e.dat";
> +static char *myri10ge_fw_aligned = "myri10ge_eth_z8e.dat";
> +static int myri10ge_ecrc_enable = 1;
> +static int myri10ge_max_intr_slots = 1024;
> +static int myri10ge_small_bytes = -1; /* -1 == auto */
> +static int myri10ge_msi = 1; /* enable msi by default */
> +static int myri10ge_intr_coal_delay = 25;
> +static int myri10ge_flow_control = 1;
> +static int myri10ge_deassert_wait = 1;
> +static int myri10ge_force_firmware = 0;
> +static int myri10ge_skb_cross_4k = 0;
> +static int myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
> +static int myri10ge_napi_weight = 64;
> +static int myri10ge_watchdog_timeout = 1;
> +static int myri10ge_max_irq_loops = 1048576;
> +
> +module_param(myri10ge_fw_name, charp, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(myri10ge_fw_name, "Firmware image name\n");
> +module_param(myri10ge_max_intr_slots, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_max_intr_slots, "Interrupt queue slots\n");
> +module_param(myri10ge_small_bytes, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(myri10ge_small_bytes, "Threshold of small packets\n");
> +module_param(myri10ge_msi, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_msi, "Enable Message Signalled Interrupts\n");
> +module_param(myri10ge_intr_coal_delay, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_intr_coal_delay, "Interrupt coalescing
> delay\n"); +module_param(myri10ge_ecrc_enable, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_ecrc_enable, "Enable Extended CRC on PCI-E\n");
> +module_param(myri10ge_flow_control, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_flow_control, "Pause parameter\n");
> +module_param(myri10ge_deassert_wait, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(myri10ge_deassert_wait,
> + "Wait when deasserting legacy interrupts\n");
> +module_param(myri10ge_force_firmware, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_force_firmware,
> + "Force firmware to assume aligned completions\n");
> +module_param(myri10ge_skb_cross_4k, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(myri10ge_skb_cross_4k,
> + "Can a small skb cross a 4KB boundary?\n");
> +module_param(myri10ge_initial_mtu, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_initial_mtu, "Initial MTU\n");
> +module_param(myri10ge_napi_weight, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_napi_weight, "Set NAPI weight\n");
> +module_param(myri10ge_watchdog_timeout, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_watchdog_timeout, "Set watchdog timeout\n");
> +module_param(myri10ge_max_irq_loops, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_max_irq_loops,
> + "Set stuck legacy IRQ detection threshold\n");
How about writing the module_param() and MODULE_PARM_DESC() calls
directly after each declaration? That would make it clearer
that they are all parameters.
> + response->result = 0xffffffff;
0xffffffff appears throughout your code as a return value. maybe
use a named constant for it?
> + for (sleep_total = 0;
> + sleep_total < (15 * 1000) && response->result == 0xffffffff;
> + sleep_total += 10) {
> + udelay(10);
> + }
udelay does not sleep. If you want to sleep, use msleep instead.
> +
> + myri10ge_pio_copy((void __iomem *)submit, &buf, sizeof(buf));
> + for (i = 0; *confirm != 0xffffffff && i < 20; i++)
> + udelay(1000);
> + if (*confirm != 0xffffffff) {
> + dev_err(&mgp->pdev->dev, "dummy rdma %s failed\n",
> + (enable ? "enable" : "disable"));
> + }
> +}
Can you use msleep here instead of udelay?
> +static int myri10ge_load_firmware(struct myri10ge_priv *mgp)
> +{
> + volatile u32 *confirm;
> + volatile char __iomem *submit;
The __iomem variable need not be volatile.
> + myri10ge_pio_copy((void __iomem *)submit, &buf, sizeof(buf));
> + mb();
> + udelay(1000);
> + mb();
can't you use msleep(1) instead?
> +static inline void
> +myri10ge_submit_8rx(struct mcp_kreq_ether_recv __iomem * dst,
> + struct mcp_kreq_ether_recv *src)
> +{
> + u32 low;
> +
> + low = src->addr_low;
> + src->addr_low = DMA_32BIT_MASK;
> + myri10ge_pio_copy(dst, src, 8 * sizeof(*src));
> + mb();
> + src->addr_low = low;
> + *(u32 __force *) & dst->addr_low = src->addr_low;
> + mb();
> +}
The __force dereference seems fishy.
> + if (unlikely(((end >> 12) != (data >> 12)) && (data & 4095UL))) {
> + printk
> + ("myri10ge_alloc_small: small skb crossed 4KB boundary\n");
Printk level is missing.
> +static int myri10ge_open(struct net_device *dev)
This function is too long to read easily.
> + /* allocate the host shadow rings */
> +
> + bytes = 8 + (MYRI10GE_MCP_ETHER_MAX_SEND_DESC_TSO + 4)
> + * sizeof(*mgp->tx.req_list);
> + mgp->tx.req_bytes = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->tx.req_bytes == NULL)
> + goto abort_with_nothing;
> + memset(mgp->tx.req_bytes, 0, bytes);
> +
> + /* ensure req_list entries are aligned to 8 bytes */
> + mgp->tx.req_list = (struct mcp_kreq_ether_send *)
> + ALIGN((unsigned long)mgp->tx.req_bytes, 8);
> +
> + bytes = rx_ring_entries * sizeof(*mgp->rx_small.shadow);
> + mgp->rx_small.shadow = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->rx_small.shadow == NULL)
> + goto abort_with_tx_req_bytes;
> + memset(mgp->rx_small.shadow, 0, bytes);
> +
> + bytes = rx_ring_entries * sizeof(*mgp->rx_big.shadow);
> + mgp->rx_big.shadow = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->rx_big.shadow == NULL)
> + goto abort_with_rx_small_shadow;
> + memset(mgp->rx_big.shadow, 0, bytes);
> +
> + /* allocate the host info rings */
> +
> + bytes = tx_ring_entries * sizeof(*mgp->tx.info);
> + mgp->tx.info = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->tx.info == NULL)
> + goto abort_with_rx_big_shadow;
> + memset(mgp->tx.info, 0, bytes);
> +
> + bytes = rx_ring_entries * sizeof(*mgp->rx_small.info);
> + mgp->rx_small.info = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->rx_small.info == NULL)
> + goto abort_with_tx_info;
> + memset(mgp->rx_small.info, 0, bytes);
> +
> + bytes = rx_ring_entries * sizeof(*mgp->rx_big.info);
> + mgp->rx_big.info = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->rx_big.info == NULL)
> + goto abort_with_rx_small_info;
> + memset(mgp->rx_big.info, 0, bytes);
> +
Can you do all these allocations at once? Maybe you can even
move them into the size passed to alloc_etherdev.
If you need separate allocations, using kzalloc simplifies
your code.
> + /* re-write the last 32-bits with the valid flags */
> + src->flags = last_flags;
> + src_ints = (u32 *) src;
> + src_ints += 3;
> + dst_ints = (u32 __iomem *) dst;
> + dst_ints += 3;
> + *(u32 __force *) dst_ints = *src_ints;
> + tx->req += cnt;
> + mb();
> +}
All these casts indicate that you do something wrong here.
In particular, dereferencing an __iomem pointer should
not be done in a device driver.
> + /* Break the SKB or Fragment up into pieces which
> + * do not cross mgp->tx.boundary */
> + low = MYRI10GE_LOWPART_TO_U32(bus);
> + high_swapped = htonl(MYRI10GE_HIGHPART_TO_U32(bus));
> + while (len) {
> + u8 flags_next;
> + int cum_len_next;
> +
> + if (unlikely(count == max_segments))
> + goto abort_linearize;
> +
> + boundary = (low + tx->boundary) & ~(tx->boundary - 1);
> + seglen = boundary - low;
> + if (seglen > len)
> + seglen = len;
> + flags_next = flags & ~MYRI10GE_MCP_ETHER_FLAGS_FIRST;
> + cum_len_next = cum_len + seglen;
> +#ifdef NETIF_F_TSO
> + if (mss) { /* TSO */
> + (req - rdma_count)->rdma_count = rdma_count + 1;
> +
> + if (likely(cum_len >= 0)) { /* payload */
> + int next_is_first, chop;
> +
> + chop = (cum_len_next > mss);
> + cum_len_next = cum_len_next % mss;
> + next_is_first = (cum_len_next == 0);
> + flags |= chop *
> + MYRI10GE_MCP_ETHER_FLAGS_TSO_CHOP;
> + flags_next |= next_is_first *
> + MYRI10GE_MCP_ETHER_FLAGS_FIRST;
> + rdma_count |= -(chop | next_is_first);
> + rdma_count += chop & !next_is_first;
> + } else if (likely(cum_len_next >= 0)) { /* header ends */
> + int small;
> +
> + rdma_count = -1;
> + cum_len_next = 0;
> + seglen = -cum_len;
> + small =
> + (mss <=
> + MYRI10GE_MCP_ETHER_SEND_SMALL_SIZE);
> + flags_next =
> + MYRI10GE_MCP_ETHER_FLAGS_TSO_PLD |
> + MYRI10GE_MCP_ETHER_FLAGS_FIRST |
> + (small *
> + MYRI10GE_MCP_ETHER_FLAGS_SMALL);
> + }
> + }
100 characters per line are too much, as are six levels of intentation,
or the number of lines in this function.
You should try to split into into smaller ones.
>
> + if ((new_mtu < 68) || (ETH_HLEN + new_mtu > MYRI10GE_MAX_ETHER_MTU)) {
> + printk(KERN_ERR "myri10ge: %s: new mtu (%d) is not valid\n",
> + dev->name, new_mtu);
> + return -EINVAL;
> + }
> + printk("%s: changing mtu from %d to %d\n",
> + dev->name, dev->mtu, new_mtu);
You shouldn't use printk as a basic feedback mechanism to user space.
The return code already contains the information.
Also the printk misses a message level.
> + pci_set_power_state(pdev, 0); /* zeros conf space as a side effect */
> + udelay(5000); /* give card time to respond */
> + pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor);
5000 is a long time for udelay. If you can't convert this to msleep,
use at least mdelay.
> + printk(KERN_ERR "myri10ge: %s: device timeout, resetting\n",
> + mgp->dev->name);
> + printk("myri10ge: %s: %d %d %d %d %d\n", mgp->dev->name,
> + mgp->tx.req, mgp->tx.done, mgp->tx.pkt_start,
> + mgp->tx.pkt_done,
> + (int)ntohl(mgp->fw_stats->send_done_count));
> + set_current_state(TASK_UNINTERRUPTIBLE);
> + schedule_timeout(HZ * 2);
> + set_current_state(TASK_RUNNING);
> + printk("myri10ge: %s: %d %d %d %d %d\n", mgp->dev->name,
> + mgp->tx.req, mgp->tx.done, mgp->tx.pkt_start,
> + mgp->tx.pkt_done,
> + (int)ntohl(mgp->fw_stats->send_done_count));
missing printk levels here.
instead of schedule_timeout, you probably want to use msleep().
> + if (status != 0) {
> + printk(KERN_ERR "myri10ge: %s: failed to load firmware\n",
> + mgp->dev->name);
dev_err?
> + for (i = 0; i < ETH_ALEN; i++) {
> + netdev->dev_addr[i] = mgp->mac_addr[i];
> + }
Don't need the curly braces here.
> +
> + printk("myri10ge: %s: %s IRQ %d, tx bndry %d, fw %s, WC %s\n",
> + netdev->name, (mgp->msi_enabled ? "MSI" : "xPIC"),
> + pdev->irq, mgp->tx.boundary, mgp->fw_name,
> + (mgp->mtrr >= 0 ? "Enabled" : "Disabled"));
> +
missing printk level (KERN_DEBUG?). Could probably use dev_printk.
> + abort_with_irq:
> + free_irq(pdev->irq, mgp);
> + if (mgp->msi_enabled)
> + pci_disable_msi(pdev);
> +
> + abort_with_firmware:
> + myri10ge_dummy_rdma(mgp, 0);
> +
> + abort_with_rx_done:
> + bytes = myri10ge_max_intr_slots * sizeof(*mgp->rx_done.entry);
> + pci_free_consistent(pdev, bytes, mgp->rx_done.entry, mgp->rx_done.bus);
> +
> + abort_with_ioremap:
> + iounmap((void __iomem *)mgp->sram);
> +
> + abort_with_wc:
> +#ifdef CONFIG_MTRR
> + if (mgp->mtrr >= 0)
> + mtrr_del(mgp->mtrr, mgp->iomem_base, mgp->board_span);
> +#endif
> + pci_free_consistent(pdev, sizeof(*mgp->fw_stats),
> + mgp->fw_stats, mgp->fw_stats_bus);
> +
> + abort_with_cmd:
> + pci_free_consistent(pdev, sizeof(*mgp->cmd), mgp->cmd, mgp->cmd_bus);
> +
> + abort_with_netdev:
> +
> + free_netdev(netdev);
> + return status;
> +}
Goto labels are conventionally indented all the way
to the left. Yes, lindent/indent gets this wrong.
> + iounmap((void __iomem *)mgp->sram);
unnecessary cast.
> +
> +#define MYRI10GE_PCI_VENDOR_MYRICOM 0x14c1
> +#define MYRI10GE_PCI_DEVICE_Z8E 0x0008
Shouldn't the vendor ID go to pci_ids.h?
> +
> +static __init int myri10ge_init_module(void)
> +{
> + printk("%s: Version %s\n", myri10ge_driver.name, MYRI10GE_VERSION_STR);
> + return pci_register_driver(&myri10ge_driver);
> +}
> +
> +static __exit void myri10ge_cleanup_module(void)
> +{
> + pci_unregister_driver(&myri10ge_driver);
> +}
> +
> +module_init(myri10ge_init_module);
This line should go right under the function it refers to.
Arnd <><
^ permalink raw reply
* Re: [PATCH] Gianfar SKB Recycling Support
From: Andi Kleen @ 2006-05-17 22:55 UTC (permalink / raw)
To: Haruki Dai-r35557; +Cc: netdev, Fleming Andy-afleming, Kumar Gala
In-Reply-To: <18AEF66AFDF06F4CAAA1D419D000FD33524B79@az33exm24.fsl.freescale.net>
On Thursday 18 May 2006 00:45, Haruki Dai-r35557 wrote:
> This patch improves the IP forwarding throughput of
> the Freescale TSEC/eTSEC Gianfar driver. By recycling
> the Socket buffer and Data buffer, reduce the unnecessary
> memory allocation and de-allocation in the forwarding
> processing chain.
Seems very hackish because it only works inside the driver.
Also there is a reason why Linux doesn't support this normally -
it wrecks the unified memory management because you won't free
your pools on memory pressure.
-Andi
^ permalink raw reply
* Re: [PATCH] Gianfar SKB Recycling Support
From: Stephen Hemminger @ 2006-05-17 22:49 UTC (permalink / raw)
To: Haruki Dai-r35557
Cc: netdev, Fleming Andy-afleming, Kumar Gala, Haruki Dai-r35557
In-Reply-To: <18AEF66AFDF06F4CAAA1D419D000FD33524B79@az33exm24.fsl.freescale.net>
On Wed, 17 May 2006 15:45:14 -0700
"Haruki Dai-r35557" <Dai.Haruki@freescale.com> wrote:
> This patch improves the IP forwarding throughput of
> the Freescale TSEC/eTSEC Gianfar driver. By recycling
> the Socket buffer and Data buffer, reduce the unnecessary
> memory allocation and de-allocation in the forwarding
> processing chain.
>
> Signed-off-by: Dai Haruki <dai.haruki@freescale.com>
> Signed-off-by: Andy Fleming <afleming@freescale.com>
>
What if I am forwarding from gianfar to e1000?
^ permalink raw reply
* [PATCH] Gianfar SKB Recycling Support
From: Haruki Dai-r35557 @ 2006-05-17 22:45 UTC (permalink / raw)
To: netdev; +Cc: Fleming Andy-afleming, Kumar Gala, Haruki Dai-r35557
This patch improves the IP forwarding throughput of
the Freescale TSEC/eTSEC Gianfar driver. By recycling
the Socket buffer and Data buffer, reduce the unnecessary
memory allocation and de-allocation in the forwarding
processing chain.
Signed-off-by: Dai Haruki <dai.haruki@freescale.com>
Signed-off-by: Andy Fleming <afleming@freescale.com>
---
drivers/net/Kconfig | 10 +
drivers/net/gianfar.c | 351 ++++++++++++++++++++++++++++++++++-------
drivers/net/gianfar.h | 60 +++++--
drivers/net/gianfar_ethtool.c | 23 ++-
4 files changed, 357 insertions(+), 87 deletions(-)
9b63ff0fd0d2cf12425e7211f10b571b8bf3c66e
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index bdaaad8..0fe6e74 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2188,6 +2188,16 @@ config GIANFAR
config GFAR_NAPI
bool "NAPI Support"
depends on GIANFAR
+ help
+
+config GFAR_SKBUFF_RECYCLING
+ default y if GIANFAR
+ bool "Socket Buffer Recycling Support"
+ depends on GIANFAR
+ help
+ This implements a new private socket data buffer recycling algorithm
+ used for fast IPv4 packet forwarding. Select this if you would like
+ to improve your latency and throughput performance.
config MV643XX_ETH
tristate "MV-643XX Ethernet support"
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 218d317..c7b34fa 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -7,6 +7,7 @@
* Based on 8260_io/fcc_enet.c
*
* Author: Andy Fleming
+ * Dai Haruki
* Maintainer: Kumar Gala
*
* Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
@@ -110,13 +111,13 @@ #define RECEIVE(x) netif_rx(x)
#endif
const char gfar_driver_name[] = "Gianfar Ethernet";
-const char gfar_driver_version[] = "1.3";
+const char gfar_driver_version[] = "1.4";
static int gfar_enet_open(struct net_device *dev);
static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
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, struct rxbd8 *bdp);
+inline struct sk_buff *gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp);
static struct net_device_stats *gfar_get_stats(struct net_device *dev);
static int gfar_set_mac_address(struct net_device *dev);
static int gfar_change_mtu(struct net_device *dev, int new_mtu);
@@ -128,7 +129,7 @@ static void init_registers(struct net_de
static int init_phy(struct net_device *dev);
static int gfar_probe(struct platform_device *pdev);
static int gfar_remove(struct platform_device *pdev);
-static void free_skb_resources(struct gfar_private *priv);
+static void gfar_free_skb_resources(struct gfar_private *priv);
static void gfar_set_multi(struct net_device *dev);
static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
#ifdef CONFIG_GFAR_NAPI
@@ -150,6 +151,19 @@ MODULE_AUTHOR("Freescale Semiconductor,
MODULE_DESCRIPTION("Gianfar Ethernet Driver");
MODULE_LICENSE("GPL");
+#ifdef CONFIG_GFAR_SKBUFF_RECYCLING
+static void gfar_free_recycle_queue(struct gfar_private *priv );
+static inline void gfar_kfree_skb(struct sk_buff *skb,
+ unsigned long int recyclable);
+static void gfar_reset_skb_handler(void* dummy);
+
+/*
+ * Local SKB recycling pool (per CPU)
+ */
+DEFINE_PER_CPU(struct gfar_skb_handler, gfar_skb_handler);
+
+#endif
+
/* Returns 1 if incoming frames use an FCB */
static inline int gfar_uses_fcb(struct gfar_private *priv)
{
@@ -193,7 +207,8 @@ static int gfar_probe(struct platform_de
priv->interruptTransmit = platform_get_irq_byname(pdev, "tx");
priv->interruptReceive = platform_get_irq_byname(pdev, "rx");
priv->interruptError = platform_get_irq_byname(pdev, "error");
- if (priv->interruptTransmit < 0 || priv->interruptReceive < 0 || priv->interruptError < 0)
+ if (priv->interruptTransmit < 0 || priv->interruptReceive < 0
+ || priv->interruptError < 0)
goto regs_fail;
} else {
priv->interruptTransmit = platform_get_irq(pdev, 0);
@@ -264,7 +279,6 @@ #endif
dev->stop = gfar_close;
dev->get_stats = gfar_get_stats;
dev->change_mtu = gfar_change_mtu;
- dev->mtu = 1500;
dev->set_multicast_list = gfar_set_multi;
dev->ethtool_ops = &gfar_ethtool_ops;
@@ -360,6 +374,9 @@ #endif
printk("%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':');
printk("\n");
+ /* Setup MTU */
+ gfar_change_mtu(dev, 1500);
+
/* Even more device info helps when determining which kernel */
/* provided which set of benchmarks. */
#ifdef CONFIG_GFAR_NAPI
@@ -370,6 +387,10 @@ #endif
printk(KERN_INFO "%s: %d/%d RX/TX BD ring size\n",
dev->name, priv->rx_ring_size, priv->tx_ring_size);
+#ifdef CONFIG_GFAR_SKBUFF_RECYCLING
+ printk(KERN_INFO "%s: Socket buffer recycling mode enabled\n", dev->name);
+#endif
+
return 0;
register_fail:
@@ -409,7 +430,8 @@ static int init_phy(struct net_device *d
priv->oldspeed = 0;
priv->oldduplex = -1;
- snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, priv->einfo->bus_id, priv->einfo->phy_id);
+ snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, priv->einfo->bus_id,
+ priv->einfo->phy_id);
phydev = phy_connect(dev, phy_id, &adjust_link, 0);
@@ -533,7 +555,7 @@ void stop_gfar(struct net_device *dev)
free_irq(priv->interruptTransmit, dev);
}
- free_skb_resources(priv);
+ gfar_free_skb_resources(priv);
dma_free_coherent(NULL,
sizeof(struct txbd8)*priv->tx_ring_size
@@ -542,14 +564,67 @@ void stop_gfar(struct net_device *dev)
gfar_read(®s->tbase0));
}
+#ifdef CONFIG_GFAR_SKBUFF_RECYCLING
+/*
+ * function: gfar_reset_skb_handler
+ * Resetting skb handler spin lock entry in the driver initialization.
+ *
+ */
+static void gfar_reset_skb_handler(void* dummy) {
+ unsigned long flags = 0;
+ struct gfar_skb_handler* sh = &__get_cpu_var(gfar_skb_handler);
+ spin_lock_init(&sh->lock);
+ spin_lock_irqsave(&sh->lock, flags);
+ sh->recycle_max = GFAR_RECYCLE_MAX;
+ sh->recycle_count = 0;
+ sh->recycle_queue = NULL;
+ spin_unlock_irqrestore(&sh->lock, flags);
+ printk(KERN_INFO"SKB Handler initialized(max=%d)\n",sh->recycle_max);
+}
+
+/*
+ * function: gfar_free_recycle_queue
+ * Reset SKB handler struction and free existance socket buffer
+ * and data buffer in the recycling queue.
+ */
+void gfar_free_recycle_queue( struct gfar_private *priv )
+{
+ unsigned long flags;
+ struct sk_buff *clist = NULL;
+ struct sk_buff *skb;
+ /* Get recycling queue */
+ struct gfar_skb_handler* sh = &__get_cpu_var(gfar_skb_handler);
+ /* just for making sure there is recycle_queue */
+ spin_lock_irqsave(&sh->lock, flags);
+ if ( (sh->recycle_queue) ) {
+ /* pick one from head; most recent one */
+ clist = sh->recycle_queue;
+ sh->recycle_count = 0;
+ sh->recycle_queue = NULL;
+ }
+ spin_unlock_irqrestore(&sh->lock, flags);
+ while (clist) {
+ skb = clist;
+ BUG_TRAP(!atomic_read(&skb->users));
+ dev_kfree_skb_any(skb);
+ clist = clist->next;
+ }
+
+}
+#endif
+
/* 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 gfar_free_skb_resources(struct gfar_private *priv)
{
struct rxbd8 *rxbdp;
struct txbd8 *txbdp;
int i;
+
+#ifdef CONFIG_GFAR_SKBUFF_RECYCLING
+ gfar_free_recycle_queue(priv);
+#endif
/* Go through all the buffer descriptors and free their data buffers */
txbdp = priv->tx_bd_base;
@@ -574,8 +649,8 @@ static void free_skb_resources(struct gf
for (i = 0; i < priv->rx_ring_size; i++) {
if (priv->rx_skbuff[i]) {
dma_unmap_single(NULL, rxbdp->bufPtr,
- priv->rx_buffer_size,
- DMA_FROM_DEVICE);
+ priv->rx_buffer_size,
+ DMA_FROM_DEVICE);
dev_kfree_skb_any(priv->rx_skbuff[i]);
priv->rx_skbuff[i] = NULL;
@@ -778,17 +853,19 @@ int startup_gfar(struct net_device *dev)
phy_start(priv->phydev);
/* Configure the coalescing support */
+ priv->txic = 0;
if (priv->txcoalescing)
- gfar_write(®s->txic,
- mk_ic_value(priv->txcount, priv->txtime));
- else
- gfar_write(®s->txic, 0);
+ priv->txic = mk_ic_value(priv->txcount, priv->txtime);
+ gfar_write(®s->txic, 0);
+ gfar_write(®s->txic, priv->txic);
+
+ priv->rxic = 0;
if (priv->rxcoalescing)
- gfar_write(®s->rxic,
- mk_ic_value(priv->rxcount, priv->rxtime));
- else
- gfar_write(®s->rxic, 0);
+ priv->rxic = mk_ic_value(priv->rxcount, priv->rxtime);
+
+ gfar_write(®s->rxic, 0);
+ gfar_write(®s->rxic, priv->rxic);
if (priv->rx_csum_enable)
rctrl |= RCTRL_CHECKSUMMING;
@@ -847,7 +924,7 @@ tx_irq_fail:
free_irq(priv->interruptError, dev);
err_irq_fail:
rx_skb_fail:
- free_skb_resources(priv);
+ gfar_free_skb_resources(priv);
tx_skb_fail:
dma_free_coherent(NULL,
sizeof(struct txbd8)*priv->tx_ring_size
@@ -858,6 +935,7 @@ tx_skb_fail:
return err;
}
+
/* Called when something needs to use the ethernet device */
/* Returns 0 for success. */
static int gfar_enet_open(struct net_device *dev)
@@ -983,6 +1061,9 @@ static int gfar_start_xmit(struct sk_buf
/* in need of CRC */
status |= (TXBD_READY | TXBD_LAST | TXBD_CRC);
+ /* Tell the DMA to go go go */
+ gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
+
dev->trans_start = jiffies;
txbdp->status = status;
@@ -1005,9 +1086,6 @@ static int gfar_start_xmit(struct sk_buf
/* Update the current txbd to the next one */
priv->cur_tx = txbdp;
- /* Tell the DMA to go go go */
- gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
-
/* Unlock priv */
spin_unlock_irqrestore(&priv->txlock, flags);
@@ -1134,6 +1212,20 @@ static int gfar_change_mtu(struct net_de
dev->mtu = new_mtu;
+#ifdef CONFIG_GFAR_SKBUFF_RECYCLING
+ priv->skbuff_truesize =
+ SKB_DATA_ALIGN(tempsize + RXBUF_ALIGNMENT +
+ GFAR_SKB_USER_OPT_HEADROOM)
+ + sizeof(struct sk_buff);
+ printk(KERN_INFO"%s: MTU = %d (frame size=%d,truesize=%lu)\n",
+ dev->name,dev->mtu,frame_size,
+ priv->skbuff_truesize );
+#else
+ printk(KERN_INFO"%s: MTU = %d (frame size=%d)\n", dev->name,
+ dev->mtu, frame_size);
+#endif /*CONFIG_GFAR_SKBUFF_RECYCLING*/
+
+
gfar_write(&priv->regs->mrblr, priv->rx_buffer_size);
gfar_write(&priv->regs->maxfrm, priv->rx_buffer_size);
@@ -1173,18 +1265,13 @@ static void gfar_timeout(struct net_devi
netif_schedule(dev);
}
-/* Interrupt Handler for Transmit complete */
-static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs)
+static inline int gfar_clean_tx_ring(struct net_device *dev)
{
- struct net_device *dev = (struct net_device *) dev_id;
- struct gfar_private *priv = netdev_priv(dev);
struct txbd8 *bdp;
+ struct gfar_private *priv = netdev_priv(dev);
+ int howmany = 0;
+ struct sk_buff* skb;
- /* Clear IEVENT */
- gfar_write(&priv->regs->ievent, IEVENT_TX_MASK);
-
- /* Lock priv */
- spin_lock(&priv->txlock);
bdp = priv->dirty_tx;
while ((bdp->status & TXBD_READY) == 0) {
/* If dirty_tx and cur_tx are the same, then either the */
@@ -1192,16 +1279,18 @@ static irqreturn_t gfar_transmit(int irq
/* obviously). If it is empty, we are done. */
if ((bdp == priv->cur_tx) && (netif_queue_stopped(dev) == 0))
break;
-
- priv->stats.tx_packets++;
-
+ howmany++;
+
/* Deferred means some collisions occurred during transmit, */
/* but we eventually sent the packet. */
if (bdp->status & TXBD_DEF)
priv->stats.collisions++;
-
+
/* Free the sk buffer associated with this TxBD */
- dev_kfree_skb_irq(priv->tx_skbuff[priv->skb_dirtytx]);
+ skb = priv->tx_skbuff[priv->skb_dirtytx];
+
+ GFAR_KFREE_SKB(skb,priv->skbuff_truesize);
+
priv->tx_skbuff[priv->skb_dirtytx] = NULL;
priv->skb_dirtytx =
(priv->skb_dirtytx +
@@ -1221,19 +1310,144 @@ static irqreturn_t gfar_transmit(int irq
netif_wake_queue(dev);
} /* while ((bdp->status & TXBD_READY) == 0) */
+ priv->stats.tx_packets += howmany;
+
+ return howmany;
+}
+
+
+/* Interrupt Handler for Transmit complete */
+static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs) {
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct gfar_private *priv = netdev_priv(dev);
+
+ /* Clear IEVENT */
+ gfar_write(&priv->regs->ievent, IEVENT_TX_MASK);
+
+ /* Lock priv */
+ spin_lock(&priv->txlock);
+
+ gfar_clean_tx_ring(dev);
+
/* If we are coalescing the interrupts, reset the timer */
- /* Otherwise, clear it */
- if (priv->txcoalescing)
- gfar_write(&priv->regs->txic,
- mk_ic_value(priv->txcount, priv->txtime));
- else
- gfar_write(&priv->regs->txic, 0);
+ gfar_write(&priv->regs->txic, 0);
+ gfar_write(&priv->regs->txic, priv->txic);
spin_unlock(&priv->txlock);
-
return IRQ_HANDLED;
}
+
+#ifdef CONFIG_GFAR_SKBUFF_RECYCLING
+struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct sk_buff *skb = NULL;
+ unsigned int timeout = SKB_ALLOC_TIMEOUT;
+ unsigned long flags = 0;
+
+ struct gfar_skb_handler* sh = &__get_cpu_var(gfar_skb_handler);
+
+ spin_lock_irqsave(sh->lock, flags);
+ if (sh->recycle_queue) {
+ unsigned int size;
+ int truesize;
+ /* pick one from head; most recent one */
+ skb = sh->recycle_queue;
+ sh->recycle_queue = skb->next;
+ sh->recycle_count--;
+ spin_unlock_irqrestore(sh->lock, flags);
+
+ /* re-initialization
+ * We are not going to touch the buffer size, so
+ * skb->truesize can be used as the truesize again
+ */
+ truesize = skb->truesize;
+
+ size = SKB_DATA_ALIGN(priv->rx_buffer_size + RXBUF_ALIGNMENT +
+ GFAR_SKB_USER_OPT_HEADROOM);
+ /* clear structure by &truesize */
+ memset(skb, 0, offsetof(struct sk_buff, truesize));
+ atomic_set(&skb->users, 1);
+ /* reserve for optimization. Comply to __dev_alloc_skb() */
+ skb->data = skb->head + GFAR_SKB_USER_OPT_HEADROOM;
+ skb->tail = skb->head + GFAR_SKB_USER_OPT_HEADROOM;
+ skb->end = skb->head + size;
+
+ /* shared info clean up */
+ atomic_set(&(skb_shinfo(skb)->dataref), 1);
+ skb_shinfo(skb)->nr_frags = 0;
+ skb_shinfo(skb)->tso_size = 0;
+ skb_shinfo(skb)->tso_segs = 0;
+ skb_shinfo(skb)->frag_list = NULL;
+
+ } else {
+ spin_unlock_irqrestore(sh->lock, flags);
+ while ((!skb) && timeout--)
+ skb = dev_alloc_skb(priv->rx_buffer_size + RXBUF_ALIGNMENT);
+ /* We have to allocate the skb, so keep trying till we succeed */
+ if (skb == NULL)
+ return NULL;
+
+ /* Activate this code if you want to know the recycle backlog*/
+ /* printk("newly alloc: count=%d\n",sh->recycle_count); */
+
+ }
+
+ /* We need the data buffer to be aligned properly. We will reserve
+ * as many bytes as needed to align the data properly
+ */
+ if (RXBUF_ALIGNMENT != 0)
+ skb_reserve(skb,
+ RXBUF_ALIGNMENT -
+ (((unsigned) skb->data) & (RXBUF_ALIGNMENT - 1)));
+
+ skb->dev = dev;
+
+ bdp->bufPtr = dma_map_single(NULL, skb->data,
+ priv->rx_buffer_size,
+ DMA_FROM_DEVICE);
+
+ bdp->length = 0;
+
+ /* Mark the buffer empty */
+ bdp->status |= (RXBD_EMPTY | RXBD_INTERRUPT);
+
+ return skb;
+}
+
+static inline void gfar_kfree_skb(struct sk_buff *skb,
+ unsigned long int recyclable) {
+ if ((skb->truesize >= recyclable)&&
+ (!skb->destructor) && (!skb->cloned)) {
+ if (atomic_dec_and_test(&skb->users)) {
+ struct gfar_skb_handler *sh;
+ unsigned long flags = 0;
+ sh = &__get_cpu_var(gfar_skb_handler);
+ spin_lock_irqsave(sh->lock, flags);
+ if (likely(sh->recycle_count < sh->recycle_max)) {
+ sh->recycle_count++;
+ skb->next = sh->recycle_queue;
+ sh->recycle_queue = skb;
+ } else {
+ struct softnet_data *sd;
+ sd = &__get_cpu_var(softnet_data);
+ skb->next = sd->completion_queue;
+ sd->completion_queue = skb;
+ raise_softirq_irqoff(NET_TX_SOFTIRQ);
+ }
+ spin_unlock_irqrestore(sh->lock, flags);
+ }
+ } else {
+ /* skb is not recyclable */
+ dev_kfree_skb_any(skb);
+ }
+}
+
+#else
+/*
+ * normal new skb routine
+ */
struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
{
unsigned int alignamount;
@@ -1259,7 +1473,8 @@ struct sk_buff * gfar_new_skb(struct net
skb->dev = dev;
bdp->bufPtr = dma_map_single(NULL, skb->data,
- priv->rx_buffer_size, DMA_FROM_DEVICE);
+ priv->rx_buffer_size,
+ DMA_FROM_DEVICE);
bdp->length = 0;
@@ -1268,6 +1483,7 @@ struct sk_buff * gfar_new_skb(struct net
return skb;
}
+#endif /*CONFIG_GFAR_SKBUFF_RECYCLING*/
static inline void count_errors(unsigned short status, struct gfar_private *priv)
{
@@ -1324,7 +1540,7 @@ #endif
#ifdef CONFIG_GFAR_NAPI
if (netif_rx_schedule_prep(dev)) {
tempval = gfar_read(&priv->regs->imask);
- tempval &= IMASK_RX_DISABLED;
+ tempval &= IMASK_NAPI_DISABLED;
gfar_write(&priv->regs->imask, tempval);
__netif_rx_schedule(dev);
@@ -1334,21 +1550,18 @@ #ifdef CONFIG_GFAR_NAPI
dev->name, gfar_read(&priv->regs->ievent),
gfar_read(&priv->regs->imask));
}
-#else
+#else /* CONFIG_GFAR_NAPI */
spin_lock_irqsave(&priv->rxlock, flags);
gfar_clean_rx_ring(dev, priv->rx_ring_size);
/* If we are coalescing interrupts, update the timer */
- /* Otherwise, clear it */
+ gfar_write(&priv->regs->rxic, 0);
if (priv->rxcoalescing)
- gfar_write(&priv->regs->rxic,
- mk_ic_value(priv->rxcount, priv->rxtime));
- else
- gfar_write(&priv->regs->rxic, 0);
+ gfar_write(&priv->regs->rxic, priv->rxic);
spin_unlock_irqrestore(&priv->rxlock, flags);
-#endif
+#endif /* CONFIG_GFAR_NAPI */
return IRQ_HANDLED;
}
@@ -1509,8 +1722,11 @@ static int gfar_poll(struct net_device *
if (rx_work_limit > dev->quota)
rx_work_limit = dev->quota;
+
howmany = gfar_clean_rx_ring(dev, rx_work_limit);
+ gfar_clean_tx_ring(dev);
+
dev->quota -= howmany;
rx_work_limit -= howmany;
*budget -= howmany;
@@ -1521,15 +1737,14 @@ static int gfar_poll(struct net_device *
/* Clear the halt bit in RSTAT */
gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
- gfar_write(&priv->regs->imask, IMASK_DEFAULT);
-
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
+ gfar_write(&priv->regs->rxic, 0);
if (priv->rxcoalescing)
- gfar_write(&priv->regs->rxic,
- mk_ic_value(priv->rxcount, priv->rxtime));
- else
- gfar_write(&priv->regs->rxic, 0);
+ gfar_write(&priv->regs->rxic, priv->rxic);
+
+ gfar_write(&priv->regs->imask, IMASK_DEFAULT);
+
}
/* Return 1 if there's more work to do */
@@ -1542,6 +1757,7 @@ static irqreturn_t gfar_interrupt(int ir
{
struct net_device *dev = dev_id;
struct gfar_private *priv = netdev_priv(dev);
+ unsigned long tempval;
/* Save ievent for future reference */
u32 events = gfar_read(&priv->regs->ievent);
@@ -1567,13 +1783,19 @@ static irqreturn_t gfar_interrupt(int ir
priv->stats.tx_aborted_errors++;
if (events & IEVENT_XFUN) {
if (netif_msg_tx_err(priv))
- printk(KERN_WARNING "%s: tx underrun. dropped packet\n", dev->name);
+ printk(KERN_WARNING
+ "%s: tx underrun. dropped packet\n",
+ dev->name);
priv->stats.tx_dropped++;
priv->extra_stats.tx_underrun++;
/* Reactivate the Tx Queues */
gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
}
+ /* Make sure we aren't stopped */
+ tempval = gfar_read(&priv->regs->dmactrl);
+ tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
+ gfar_write(&priv->regs->dmactrl, tempval);
}
if (events & IEVENT_BSY) {
priv->stats.rx_errors++;
@@ -1600,6 +1822,8 @@ #endif
}
if (events & IEVENT_EBERR) {
priv->extra_stats.eberr++;
+ /* Reactivate the Tx Queues */
+ gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
if (netif_msg_rx_err(priv))
printk(KERN_DEBUG "%s: EBERR\n", dev->name);
}
@@ -1668,8 +1892,9 @@ static void adjust_link(struct net_devic
default:
if (netif_msg_link(priv))
printk(KERN_WARNING
- "%s: Ack! Speed (%d) is not 10/100/1000!\n",
- dev->name, phydev->speed);
+ "%s: Ack! Speed (%d) is "
+ "not 10/100/1000!\n",
+ dev->name, phydev->speed);
break;
}
@@ -1958,7 +2183,9 @@ static int __init gfar_init(void)
if (err)
gfar_mdio_exit();
-
+
+ on_each_cpu(gfar_reset_skb_handler, NULL, 0, 1);
+
return err;
}
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 127c98c..31c788a 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -6,6 +6,7 @@
* Based on 8260_io/fcc_enet.c
*
* Author: Andy Fleming
+ * Dai Haruki
* Maintainer: Kumar Gala
*
* Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
@@ -51,7 +52,7 @@ #include <linux/fsl_devices.h>
#include "gianfar_mii.h"
/* The maximum number of packets to be handled in one call of gfar_poll */
-#define GFAR_DEV_WEIGHT 64
+#define GFAR_DEV_WEIGHT 16
/* Length for FCB */
#define GMAC_FCB_LEN 8
@@ -62,6 +63,8 @@ #define DEFAULT_PADDING 2
/* Number of bytes to align the rx bufs to */
#define RXBUF_ALIGNMENT 64
+#define GFAR_SKB_USER_OPT_HEADROOM 16
+
/* The number of bytes which composes a unit for the purpose of
* allocating data buffers. ie-for any given MTU, the data buffer
* will be the next highest multiple of 512 bytes. */
@@ -73,15 +76,15 @@ #define MAC_ADDR_LEN 6
#define PHY_INIT_TIMEOUT 100000
#define GFAR_PHY_CHANGE_TIME 2
-#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.2, "
+#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.4, "
#define DRV_NAME "gfar-enet"
extern const char gfar_driver_name[];
extern const char gfar_driver_version[];
/* These need to be powers of 2 for this driver */
#ifdef CONFIG_GFAR_NAPI
-#define DEFAULT_TX_RING_SIZE 256
-#define DEFAULT_RX_RING_SIZE 256
+#define DEFAULT_TX_RING_SIZE 64
+#define DEFAULT_RX_RING_SIZE 64
#else
#define DEFAULT_TX_RING_SIZE 64
#define DEFAULT_RX_RING_SIZE 64
@@ -101,10 +104,10 @@ #define JUMBO_BUFFER_SIZE 9728
#define JUMBO_FRAME_SIZE 9600
#define DEFAULT_FIFO_TX_THR 0x100
-#define DEFAULT_FIFO_TX_STARVE 0x40
-#define DEFAULT_FIFO_TX_STARVE_OFF 0x80
+#define DEFAULT_FIFO_TX_STARVE 0x80
+#define DEFAULT_FIFO_TX_STARVE_OFF 0x100
#define DEFAULT_BD_STASH 1
-#define DEFAULT_STASH_LENGTH 64
+#define DEFAULT_STASH_LENGTH 96
#define DEFAULT_STASH_INDEX 0
/* The number of Exact Match registers */
@@ -125,12 +128,12 @@ #define GFAR_100_TIME 2560
#define GFAR_10_TIME 25600
#define DEFAULT_TX_COALESCE 1
-#define DEFAULT_TXCOUNT 16
-#define DEFAULT_TXTIME 4
+#define DEFAULT_TXCOUNT 24
+#define DEFAULT_TXTIME 64
#define DEFAULT_RX_COALESCE 1
-#define DEFAULT_RXCOUNT 16
-#define DEFAULT_RXTIME 4
+#define DEFAULT_RXCOUNT 2
+#define DEFAULT_RXTIME 64
#define TBIPA_VALUE 0x1f
#define MIIMCFG_INIT_VALUE 0x00000007
@@ -235,6 +238,7 @@ #define IEVENT_DPE 0x00000002
#define IEVENT_PERR 0x00000001
#define IEVENT_RX_MASK (IEVENT_RXB0 | IEVENT_RXF0)
#define IEVENT_TX_MASK (IEVENT_TXB | IEVENT_TXF)
+#define IEVENT_RTX_MASK (IEVENT_RX_MASK | IEVENT_TX_MASK)
#define IEVENT_ERR_MASK \
(IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \
IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \
@@ -262,11 +266,11 @@ #define IMASK_FIR 0x00000008
#define IMASK_FIQ 0x00000004
#define IMASK_DPE 0x00000002
#define IMASK_PERR 0x00000001
-#define IMASK_RX_DISABLED ~(IMASK_RXFEN0 | IMASK_BSY)
-#define IMASK_DEFAULT (IMASK_TXEEN | IMASK_TXFEN | IMASK_TXBEN | \
+#define IMASK_NAPI_DISABLED ~(IMASK_RXFEN0 | IMASK_BSY | IMASK_TXFEN)
+#define IMASK_DEFAULT (IMASK_TXEEN | IMASK_TXFEN | \
IMASK_RXFEN0 | IMASK_BSY | IMASK_EBERR | IMASK_BABR | \
- IMASK_XFUN | IMASK_RXC | IMASK_BABT | IMASK_DPE \
- | IMASK_PERR)
+ IMASK_XFUN | IMASK_RXC | IMASK_BABT | \
+ IMASK_DPE | IMASK_PERR)
/* Fifo management */
#define FIFO_TX_THR_MASK 0x01ff
@@ -691,6 +695,13 @@ struct gfar_private {
unsigned char rxcoalescing;
unsigned short rxcount;
unsigned short rxtime;
+ unsigned long txic;
+ unsigned long rxic;
+ /* Buffer size for Advanced SKB Handling */
+#ifdef CONFIG_GFAR_SKBUFF_RECYCLING
+ unsigned long skbuff_truesize;
+ unsigned long skbuff_buffsize;
+#endif
struct rxbd8 *rx_bd_base; /* First Rx buffers */
struct rxbd8 *cur_rx; /* Next free rx ring entry */
@@ -765,4 +776,23 @@ extern void gfar_phy_test(struct mii_bus
int enable, u32 regnum, u32 read);
void gfar_init_sysfs(struct net_device *dev);
+
+#ifdef CONFIG_GFAR_SKBUFF_RECYCLING
+#define GFAR_RECYCLE_MAX 64
+struct gfar_skb_handler {
+ spinlock_t lock;
+ long int recycle_max;
+ long int recycle_count; /* should be atomic */
+ struct sk_buff *recycle_queue;
+};
+
+DECLARE_PER_CPU(struct gfar_skb_handler, gfar_skb_handler);
+
+#define GFAR_KFREE_SKB(skb,size) gfar_kfree_skb( skb, size )
+
+#else /*CONFIG_GFAR_SKBUFF_RECYCLING*/
+/* use dev_kfree_skb_irq directly */
+#define GFAR_KFREE_SKB(skb,arg...) dev_kfree_skb_irq( skb )
+#endif /*CONFIG_GFAR_SKBUFF_RECYCLING*/
+
#endif /* __GIANFAR_H */
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index d69698c..026f07c 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -363,6 +363,10 @@ static int gfar_scoalesce(struct net_dev
priv->rxtime = gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs);
priv->rxcount = cvals->rx_max_coalesced_frames;
+ if (priv->rxcoalescing)
+ priv->rxic = mk_ic_value(priv->rxcount, priv->rxtime);
+ else
+ priv->rxic = 0;
/* Set up tx coalescing */
if ((cvals->tx_coalesce_usecs == 0) ||
@@ -371,6 +375,11 @@ static int gfar_scoalesce(struct net_dev
else
priv->txcoalescing = 1;
+ if (priv->txcoalescing)
+ priv->txic = mk_ic_value(priv->txcount, priv->txtime);
+ else
+ priv->txic = 0;
+
/* Check the bounds of the values */
if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
pr_info("Coalescing is limited to %d microseconds\n",
@@ -387,17 +396,11 @@ static int gfar_scoalesce(struct net_dev
priv->txtime = gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs);
priv->txcount = cvals->tx_max_coalesced_frames;
- if (priv->rxcoalescing)
- gfar_write(&priv->regs->rxic,
- mk_ic_value(priv->rxcount, priv->rxtime));
- else
- gfar_write(&priv->regs->rxic, 0);
+ gfar_write(&priv->regs->rxic, 0);
+ gfar_write(&priv->regs->rxic, priv->rxic);
- if (priv->txcoalescing)
- gfar_write(&priv->regs->txic,
- mk_ic_value(priv->txcount, priv->txtime));
- else
- gfar_write(&priv->regs->txic, 0);
+ gfar_write(&priv->regs->txic, 0);
+ gfar_write(&priv->regs->txic, priv->txic);
return 0;
}
--
1.3.1.g7464-dirty
^ permalink raw reply related
* Re: [PATCH 3/4] myri10ge - Driver core
From: Roland Dreier @ 2006-05-17 22:36 UTC (permalink / raw)
To: Brice Goglin; +Cc: netdev, gallatin, linux-kernel
In-Reply-To: <20060517220608.GD13411@myri.com>
Still some suspicious uses of volatile here.
For example:
> +struct myri10ge_priv {
...
> + volatile u8 __iomem *sram;
as far as I can see this is always used with proper __iomem accessors,
often with casts to strip the volatile anyway. So why is volatile needed?
I would suggest an audit of all uses of volatile in the driver, since
"volatile" in drivers really should be read "there's probably a bug
here, and if not something very tricky is going on." If there are any
valid uses of volatile then a comment should explain why, so that
future reviewers don't have to try and puzzle out which of the
two possible translations of volatile is correct.
- R.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox