* [net-next PATCH 0/6] net: frag performance tuning cachelines for NUMA/SMP systems
From: Jesper Dangaard Brouer @ 2013-01-24 14:04 UTC (permalink / raw)
To: Eric Dumazet, David S. Miller, Florian Westphal
Cc: Jesper Dangaard Brouer, netdev, Pablo Neira Ayuso, Cong Wang,
Patrick McHardy, Herbert Xu, Daniel Borkmann
This patchset is a new partly respin of my fragmentation optimization
patches: http://thread.gmane.org/gmane.linux.network/250914
This is not the complete patchset, from previously. In this patchset,
I primarily focus on adjusting cacheline for better SMP/NUMA
performance.
Once this patchset have been agreed upon, I will continue and respin
the rest of my patches.
This time around, I have created a frag DoS generator, via the tool
trafgen (http://netsniff-ng.org/). To create a stable DoS scenario
(no longer relying on frame dropping due to disabled flow-control).
Two 10G interfaces are under-test, and uses Ethernet flow-control. A
third interface is used for generating the DoS attack (this interface
is also 10G, but it does not need to be, as 500Kpps DoS is enough).
Test types summary (netperf):
Test-20G64K == 2x10G with 65K fragments
Test-20G3F == 2x10G with 3x fragments (3*1472 bytes)
Test-20G64K+DoS == Same as 20G64K with frag DoS
Test-20G3F+DoS == Same as 20G3F with frag DoS
Patch list:
Patch-01 - net: cacheline adjust struct netns_frags for better frag performance
Patch-02 - net: cacheline adjust struct inet_frags for better frag performance
Patch-03 - net: cacheline adjust struct inet_frag_queue
Patch-04 - net: frag helper functions for mem limit tracking
Patch-05 - net: use lib/percpu_counter API for fragmentation mem accounting
Patch-06 - net: frag, move LRU list maintenance outside of rwlock
Performance table summary:
Test-type: Test-20G64K Test-20G3F 20G64K+DoS 20G3F+DoS
---------- ----------- ---------- ---------- ---------
net-next: 15114.5 Mbit/s 8954.21 2444.28 3918.01 Mbit/s
Patch-01: 16075.8 Mbit/s 8976.18 2621.49 4072.79 Mbit/s
Patch-02: 17806.9 Mbit/s 9280.32 2478.62 4274.59 Mbit/s
Patch-03: 17317.4 Mbit/s 9308.62 2546.05 4336.59 Mbit/s
Patch-04: 17635.9 Mbit/s 9256.16 2535.25 4327.63 Mbit/s
Patch-05: 18027.0 Mbit/s 9918.99 2492.62 3621.68 Mbit/s
Patch-06: 18486.7 Mbit/s 10723.20 3657.85 4560.64 Mbit/s
I cannot explain the under-DoS regression that patch-05/percpu_counter
introduces. But patch-06/LRU-lock corrects the situation again.
Below is a testlab setup description, with links to the trafgen DoS
packet config used.
---
Jesper Dangaard Brouer (6):
net: frag, move LRU list maintenance outside of rwlock
net: use lib/percpu_counter API for fragmentation mem accounting
net: frag helper functions for mem limit tracking
net: cacheline adjust struct inet_frag_queue
net: cacheline adjust struct inet_frags for better frag performance
net: cacheline adjust struct netns_frags for better frag performance
include/linux/percpu_counter.h | 2 -
include/net/inet_frag.h | 85 ++++++++++++++++++++++++++++---
include/net/ipv6.h | 2 -
net/ipv4/inet_fragment.c | 39 ++++++++------
net/ipv4/ip_fragment.c | 28 ++++------
net/ipv6/netfilter/nf_conntrack_reasm.c | 11 ++--
net/ipv6/reassembly.c | 10 +---
7 files changed, 120 insertions(+), 57 deletions(-)
Testlab
=======
Server setup
------------
The machine acting as a server:
- 2x CPU (E5-2630)
- Thus a NUMA arch/machine
- 4x 10Gbit/s ports
- NICs 2x Intel Dual port 82599 based (driver ixgbe)
Setup:
- Interfaces uses Ethernet flow control
- Flush all iptables
- Remove all iptables related module.
- Kill irqbalance
- Pin each 10G NIC port to a *single* CPU each
Pinning can easily be done by command hacks::
for x in /proc/irq/*/eth8*/../smp_affinity_list ; do echo 1 > $x; done
for x in /proc/irq/*/eth9*/../smp_affinity_list ; do echo 3 > $x; done
for x in /proc/irq/*/eth31*/../smp_affinity_list; do echo 6 > $x; done
for x in /proc/irq/*/eth32*/../smp_affinity_list; do echo 8 > $x; done
Notice NUMA setting: The CPU to NIC tying is carefully choosen
according to the NUMA node setup. Thus, NICs connected to a PCI-e
slot that is connected to a physical CPU socket are tied together.
Choosing only a single CPU per NIC (port) is just to ease provoking
and debugging this performance issue. (In real setups, you can choose
more CPU, just remember the NUMA node in the equation).
Tools
-----
Netperf is used, with option -T to ensure CPU binding.
The netserver processes, are NAPI pinned::
numactl -m0 -c0 netserver
numactl -m1 -c 1 netserver -p 1337
I now have a frag DoS generator, created via the tool:
trafgen (see: http://netsniff-ng.org/)
Trafgen packet config file:
http://people.netfilter.org/hawk/frag_work/trafgen/frag_packet03_small_frag.txf
Notice, I'm using features of trafgen, recently developed by Daniel
Borkmann, thus you need the latest git tree to use my trafgen packet
config.
git://github.com/borkmann/netsniff-ng.git
Command line:
trafgen --dev eth51 --conf frag_packet03_small_frag.txf -V -k 100 --cpus 2
Tests types
-----------
Test(20G64K) UDP-64K 2x 10Gbit/s with no DoS traffic:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
export SIZE=$((65507)); export TIME=$((20)); export LOG=/tmp/netperf.log ;\
netperf -p 1337 -H 192.168.31.2 -T7,7 -t UDP_STREAM -l $TIME -- -m $SIZE >> ${LOG}.31 &\
netperf -H 192.168.81.2 -T2,2 -t UDP_STREAM -l $TIME -- -m $SIZE >> ${LOG}.81 && \
wait $! && tail -n3 ${LOG}.* && \
tail -n3 ${LOG}.{31,81} | awk 'BEGIN{sum=0;} /212992 / {sum+=$4; print " +"$4} /==/ {print " file:"$2} END{print "sum:"sum" Mbit/s"}'
Test(20G3F) UDP-3xfrags 2x 10Gbit/s with no DoS traffic:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
export SIZE=$((3*1472)); export TIME=$((20)); export LOG=/tmp/netperf.log ;\
netperf -p 1337 -H 192.168.31.2 -T7,7 -t UDP_STREAM -l $TIME -- -m $SIZE >> ${LOG}.31 &\
netperf -H 192.168.81.2 -T2,2 -t UDP_STREAM -l $TIME -- -m $SIZE >> ${LOG}.81 && \
wait $! && tail -n3 ${LOG}.* && \
tail -n3 ${LOG}.{31,81} | awk 'BEGIN{sum=0;} /212992 / {sum+=$4; print " +"$4} /==/ {print " file:"$2} END{print "sum:"sum" Mbit/s"}'
Awk script for summming results:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tail -n3 ${LOG}.{31,81} | awk 'BEGIN{sum=0;} /212992 / {sum+=$4; print " +"$4} /==/ {print " file:"$2} END{print "sum:"sum" Mbit/s"}'
--
Best regards,
Jesper Dangaard Brouer
MSc.CS, Sr. Network Kernel Developer at Red Hat
Author of http://www.iptv-analyzer.org
LinkedIn: http://www.linkedin.com/in/brouer
^ permalink raw reply
* [net-next PATCH 1/6] net: cacheline adjust struct netns_frags for better frag performance
From: Jesper Dangaard Brouer @ 2013-01-24 14:04 UTC (permalink / raw)
To: Eric Dumazet, David S. Miller, Florian Westphal
Cc: Jesper Dangaard Brouer, netdev, Pablo Neira Ayuso, Cong Wang,
Patrick McHardy, Herbert Xu, Daniel Borkmann
In-Reply-To: <20130124140343.14119.77712.stgit@dragon>
This small cacheline adjustment of struct netns_frags improves
performance significantly for the fragmentation code.
Struct members 'lru_list' and 'mem' are both hot elements, and it
hurts performance, due to cacheline bouncing at every call point,
when they share a cacheline. Also notice, how mem is placed
together with 'high_thresh' and 'low_thresh', as they are used in
the compare operations together.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
---
include/net/inet_frag.h | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index 32786a0..91e7797 100644
--- a/include/net/inet_frag.h
+++ b/include/net/inet_frag.h
@@ -3,9 +3,12 @@
struct netns_frags {
int nqueues;
- atomic_t mem;
struct list_head lru_list;
+ /* Its important for performance to keep lru_list and mem on
+ * separate cachelines
+ */
+ atomic_t mem ____cacheline_aligned_in_smp;
/* sysctls */
int timeout;
int high_thresh;
^ permalink raw reply related
* [net-next PATCH 2/6] net: cacheline adjust struct inet_frags for better frag performance
From: Jesper Dangaard Brouer @ 2013-01-24 14:04 UTC (permalink / raw)
To: Eric Dumazet, David S. Miller, Florian Westphal
Cc: Jesper Dangaard Brouer, netdev, Pablo Neira Ayuso, Cong Wang,
Patrick McHardy, Herbert Xu, Daniel Borkmann
In-Reply-To: <20130124140343.14119.77712.stgit@dragon>
The globally shared rwlock, of struct inet_frags, shares
cacheline with the 'rnd' number, which is used by the hash
calculations. Fix this, as this obviously is a bad idea, as
unnecessary cache-misses will occur when accessing the 'rnd'
number.
Also small note that, moving function ptr (*match) up in struct,
is to avoid it lands on the next cacheline (on 64-bit).
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
---
include/net/inet_frag.h | 12 ++++++++----
1 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index 91e7797..99eb4e0 100644
--- a/include/net/inet_frag.h
+++ b/include/net/inet_frag.h
@@ -40,18 +40,22 @@ struct inet_frag_queue {
struct inet_frags {
struct hlist_head hash[INETFRAGS_HASHSZ];
- rwlock_t lock;
- u32 rnd;
- int qsize;
+ /* This rwlock is a global lock (seperate per IPv4, IPv6 and
+ * netfilter). Important to keep this on a seperate cacheline.
+ */
+ rwlock_t lock ____cacheline_aligned_in_smp;
int secret_interval;
struct timer_list secret_timer;
+ /* --- read-mostly cacheline boundary (was 24 bytes ago) --- */
+ u32 rnd;
+ int qsize;
unsigned int (*hashfn)(struct inet_frag_queue *);
+ bool (*match)(struct inet_frag_queue *q, void *arg);
void (*constructor)(struct inet_frag_queue *q,
void *arg);
void (*destructor)(struct inet_frag_queue *);
void (*skb_free)(struct sk_buff *);
- bool (*match)(struct inet_frag_queue *q, void *arg);
void (*frag_expire)(unsigned long data);
};
^ permalink raw reply related
* [net-next PATCH 3/6] net: cacheline adjust struct inet_frag_queue
From: Jesper Dangaard Brouer @ 2013-01-24 14:04 UTC (permalink / raw)
To: Eric Dumazet, David S. Miller, Florian Westphal
Cc: Jesper Dangaard Brouer, netdev, Pablo Neira Ayuso, Cong Wang,
Patrick McHardy, Herbert Xu, Daniel Borkmann
In-Reply-To: <20130124140343.14119.77712.stgit@dragon>
Fragmentation code cacheline adjusting of struct inet_frag_queue.
Take advantage of the size of struct timer_list, and move all but
spinlock_t lock, below the timer struct. On 64-bit 'lru_list',
'list' and 'refcnt', fits exactly into the next cacheline, and a
new cacheline starts at 'fragments'.
The netns_frags *net pointer is moved to the end of the struct,
because its used in a compare, with "next/close-by" elements of
which this struct is embedded into.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
---
include/net/inet_frag.h | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index 99eb4e0..a530bf4 100644
--- a/include/net/inet_frag.h
+++ b/include/net/inet_frag.h
@@ -16,12 +16,11 @@ struct netns_frags {
};
struct inet_frag_queue {
- struct hlist_node list;
- struct netns_frags *net;
- struct list_head lru_list; /* lru list member */
spinlock_t lock;
- atomic_t refcnt;
struct timer_list timer; /* when will this queue expire? */
+ struct list_head lru_list; /* lru list member */
+ struct hlist_node list;
+ atomic_t refcnt;
struct sk_buff *fragments; /* list of received fragments */
struct sk_buff *fragments_tail;
ktime_t stamp;
@@ -34,6 +33,8 @@ struct inet_frag_queue {
#define INET_FRAG_LAST_IN 1
u16 max_size;
+
+ struct netns_frags *net;
};
#define INETFRAGS_HASHSZ 64
^ permalink raw reply related
* [net-next PATCH 4/6] net: frag helper functions for mem limit tracking
From: Jesper Dangaard Brouer @ 2013-01-24 14:04 UTC (permalink / raw)
To: Eric Dumazet, David S. Miller, Florian Westphal
Cc: Jesper Dangaard Brouer, netdev, Pablo Neira Ayuso, Cong Wang,
Patrick McHardy, Herbert Xu, Daniel Borkmann
In-Reply-To: <20130124140343.14119.77712.stgit@dragon>
This change is primarily a preparation to ease the extension of memory
limit tracking.
The change does reduce the number atomic operation, during freeing of
a frag queue. This does introduce a some performance improvement, as
these atomic operations are at the core of the performance problems
seen on NUMA systems.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
---
include/net/inet_frag.h | 27 +++++++++++++++++++++++++++
include/net/ipv6.h | 2 +-
net/ipv4/inet_fragment.c | 25 ++++++++++++-------------
net/ipv4/ip_fragment.c | 24 +++++++++++-------------
net/ipv6/netfilter/nf_conntrack_reasm.c | 6 +++---
net/ipv6/reassembly.c | 6 +++---
6 files changed, 57 insertions(+), 33 deletions(-)
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index a530bf4..9f69514 100644
--- a/include/net/inet_frag.h
+++ b/include/net/inet_frag.h
@@ -80,4 +80,31 @@ static inline void inet_frag_put(struct inet_frag_queue *q, struct inet_frags *f
inet_frag_destroy(q, f, NULL);
}
+/* Memory Tracking Functions. */
+
+static inline int frag_mem_limit(struct netns_frags *nf)
+{
+ return atomic_read(&nf->mem);
+}
+
+static inline void sub_frag_mem_limit(struct inet_frag_queue *q, int i)
+{
+ atomic_sub(i, &q->net->mem);
+}
+
+static inline void add_frag_mem_limit(struct inet_frag_queue *q, int i)
+{
+ atomic_add(i, &q->net->mem);
+}
+
+static inline void init_frag_mem_limit(struct netns_frags *nf)
+{
+ atomic_set(&nf->mem, 0);
+}
+
+static inline int sum_frag_mem_limit(struct netns_frags *nf)
+{
+ return atomic_read(&nf->mem);
+}
+
#endif
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index c1878f7..dc30b60 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -288,7 +288,7 @@ static inline int ip6_frag_nqueues(struct net *net)
static inline int ip6_frag_mem(struct net *net)
{
- return atomic_read(&net->ipv6.frags.mem);
+ return sum_frag_mem_limit(&net->ipv6.frags);
}
#endif
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index 4750d2b..e348c84 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -73,7 +73,7 @@ EXPORT_SYMBOL(inet_frags_init);
void inet_frags_init_net(struct netns_frags *nf)
{
nf->nqueues = 0;
- atomic_set(&nf->mem, 0);
+ init_frag_mem_limit(nf);
INIT_LIST_HEAD(&nf->lru_list);
}
EXPORT_SYMBOL(inet_frags_init_net);
@@ -117,12 +117,8 @@ void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f)
EXPORT_SYMBOL(inet_frag_kill);
static inline void frag_kfree_skb(struct netns_frags *nf, struct inet_frags *f,
- struct sk_buff *skb, int *work)
+ struct sk_buff *skb)
{
- if (work)
- *work -= skb->truesize;
-
- atomic_sub(skb->truesize, &nf->mem);
if (f->skb_free)
f->skb_free(skb);
kfree_skb(skb);
@@ -133,6 +129,7 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f,
{
struct sk_buff *fp;
struct netns_frags *nf;
+ unsigned int sum, sum_truesize = 0;
WARN_ON(!(q->last_in & INET_FRAG_COMPLETE));
WARN_ON(del_timer(&q->timer) != 0);
@@ -143,13 +140,14 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f,
while (fp) {
struct sk_buff *xp = fp->next;
- frag_kfree_skb(nf, f, fp, work);
+ sum_truesize += fp->truesize;
+ frag_kfree_skb(nf, f, fp);
fp = xp;
}
-
+ sum = sum_truesize + f->qsize;
if (work)
- *work -= f->qsize;
- atomic_sub(f->qsize, &nf->mem);
+ *work -= sum;
+ sub_frag_mem_limit(q, sum);
if (f->destructor)
f->destructor(q);
@@ -164,11 +162,11 @@ int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force)
int work, evicted = 0;
if (!force) {
- if (atomic_read(&nf->mem) <= nf->high_thresh)
+ if (frag_mem_limit(nf) <= nf->high_thresh)
return 0;
}
- work = atomic_read(&nf->mem) - nf->low_thresh;
+ work = frag_mem_limit(nf) - nf->low_thresh;
while (work > 0) {
read_lock(&f->lock);
if (list_empty(&nf->lru_list)) {
@@ -250,7 +248,8 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf,
q->net = nf;
f->constructor(q, arg);
- atomic_add(f->qsize, &nf->mem);
+ add_frag_mem_limit(q, f->qsize);
+
setup_timer(&q->timer, f->frag_expire, (unsigned long)q);
spin_lock_init(&q->lock);
atomic_set(&q->refcnt, 1);
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index f55a4e6..927fe58 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -122,7 +122,7 @@ int ip_frag_nqueues(struct net *net)
int ip_frag_mem(struct net *net)
{
- return atomic_read(&net->ipv4.frags.mem);
+ return sum_frag_mem_limit(&net->ipv4.frags);
}
static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
@@ -161,13 +161,6 @@ static bool ip4_frag_match(struct inet_frag_queue *q, void *a)
qp->user == arg->user;
}
-/* Memory Tracking Functions. */
-static void frag_kfree_skb(struct netns_frags *nf, struct sk_buff *skb)
-{
- atomic_sub(skb->truesize, &nf->mem);
- kfree_skb(skb);
-}
-
static void ip4_frag_init(struct inet_frag_queue *q, void *a)
{
struct ipq *qp = container_of(q, struct ipq, q);
@@ -340,6 +333,7 @@ static inline int ip_frag_too_far(struct ipq *qp)
static int ip_frag_reinit(struct ipq *qp)
{
struct sk_buff *fp;
+ unsigned int sum_truesize = 0;
if (!mod_timer(&qp->q.timer, jiffies + qp->q.net->timeout)) {
atomic_inc(&qp->q.refcnt);
@@ -349,9 +343,12 @@ static int ip_frag_reinit(struct ipq *qp)
fp = qp->q.fragments;
do {
struct sk_buff *xp = fp->next;
- frag_kfree_skb(qp->q.net, fp);
+
+ sum_truesize += fp->truesize;
+ kfree_skb(fp);
fp = xp;
} while (fp);
+ sub_frag_mem_limit(&qp->q, sum_truesize);
qp->q.last_in = 0;
qp->q.len = 0;
@@ -496,7 +493,8 @@ found:
qp->q.fragments = next;
qp->q.meat -= free_it->len;
- frag_kfree_skb(qp->q.net, free_it);
+ sub_frag_mem_limit(&qp->q, free_it->truesize);
+ kfree_skb(free_it);
}
}
@@ -519,7 +517,7 @@ found:
qp->q.stamp = skb->tstamp;
qp->q.meat += skb->len;
qp->ecn |= ecn;
- atomic_add(skb->truesize, &qp->q.net->mem);
+ add_frag_mem_limit(&qp->q, skb->truesize);
if (offset == 0)
qp->q.last_in |= INET_FRAG_FIRST_IN;
@@ -617,7 +615,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
head->len -= clone->len;
clone->csum = 0;
clone->ip_summed = head->ip_summed;
- atomic_add(clone->truesize, &qp->q.net->mem);
+ add_frag_mem_limit(&qp->q, clone->truesize);
}
skb_push(head, head->data - skb_network_header(head));
@@ -645,7 +643,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
}
fp = next;
}
- atomic_sub(sum_truesize, &qp->q.net->mem);
+ sub_frag_mem_limit(&qp->q, sum_truesize);
head->next = NULL;
head->dev = dev;
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 3dacecc..07ef294 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -319,7 +319,7 @@ found:
fq->q.meat += skb->len;
if (payload_len > fq->q.max_size)
fq->q.max_size = payload_len;
- atomic_add(skb->truesize, &fq->q.net->mem);
+ add_frag_mem_limit(&fq->q, skb->truesize);
/* The first fragment.
* nhoffset is obtained from the first fragment, of course.
@@ -398,7 +398,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
clone->ip_summed = head->ip_summed;
NFCT_FRAG6_CB(clone)->orig = NULL;
- atomic_add(clone->truesize, &fq->q.net->mem);
+ add_frag_mem_limit(&fq->q, clone->truesize);
}
/* We have to remove fragment header from datagram and to relocate
@@ -422,7 +422,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
head->csum = csum_add(head->csum, fp->csum);
head->truesize += fp->truesize;
}
- atomic_sub(head->truesize, &fq->q.net->mem);
+ sub_frag_mem_limit(&fq->q, head->truesize);
head->local_df = 1;
head->next = NULL;
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index e5253ec..18cb8de 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -327,7 +327,7 @@ found:
}
fq->q.stamp = skb->tstamp;
fq->q.meat += skb->len;
- atomic_add(skb->truesize, &fq->q.net->mem);
+ add_frag_mem_limit(&fq->q, skb->truesize);
/* The first fragment.
* nhoffset is obtained from the first fragment, of course.
@@ -429,7 +429,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
head->len -= clone->len;
clone->csum = 0;
clone->ip_summed = head->ip_summed;
- atomic_add(clone->truesize, &fq->q.net->mem);
+ add_frag_mem_limit(&fq->q, clone->truesize);
}
/* We have to remove fragment header from datagram and to relocate
@@ -467,7 +467,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
}
fp = next;
}
- atomic_sub(sum_truesize, &fq->q.net->mem);
+ sub_frag_mem_limit(&fq->q, sum_truesize);
head->next = NULL;
head->dev = dev;
^ permalink raw reply related
* [net-next PATCH 5/6] net: use lib/percpu_counter API for fragmentation mem accounting
From: Jesper Dangaard Brouer @ 2013-01-24 14:04 UTC (permalink / raw)
To: Eric Dumazet, David S. Miller, Florian Westphal
Cc: Jesper Dangaard Brouer, netdev, Pablo Neira Ayuso, Cong Wang,
Patrick McHardy, Herbert Xu, Daniel Borkmann
In-Reply-To: <20130124140343.14119.77712.stgit@dragon>
Replace the per network namespace shared atomic "mem" accounting
variable, in the fragmentation code, with a lib/percpu_counter.
Getting percpu_counter to scale to the fragmentation code usage
requires some tweaks.
At first view, percpu_counter looks superfast, but it does not
scale on multi-CPU/NUMA machines, because the default batch size
is too small, for frag code usage. Thus, I have adjusted the
batch size by using __percpu_counter_add() directly, instead of
percpu_counter_sub() and percpu_counter_add().
The batch size is increased to 130.000, based on the largest 64K
fragment memory usage. This does introduce some imprecise
memory accounting, but its does not need to be strict for this
use-case.
It is also essential, that the percpu_counter, does not
share cacheline with other writers, to make this scale.
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
---
include/linux/percpu_counter.h | 2 +-
include/net/inet_frag.h | 26 ++++++++++++++++++--------
net/ipv4/inet_fragment.c | 2 ++
3 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h
index b9df9ed..eded1aa 100644
--- a/include/linux/percpu_counter.h
+++ b/include/linux/percpu_counter.h
@@ -83,7 +83,7 @@ static inline int percpu_counter_initialized(struct percpu_counter *fbc)
return (fbc->counters != NULL);
}
-#else
+#else /* No CONFIG_SMP */
struct percpu_counter {
s64 count;
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index 9f69514..85db72e 100644
--- a/include/net/inet_frag.h
+++ b/include/net/inet_frag.h
@@ -1,14 +1,17 @@
#ifndef __NET_FRAG_H__
#define __NET_FRAG_H__
+#include <linux/percpu_counter.h>
+
struct netns_frags {
int nqueues;
struct list_head lru_list;
- /* Its important for performance to keep lru_list and mem on
- * separate cachelines
+ /* The percpu_counter "mem" need to be cacheline aligned.
+ * mem.count must not share cacheline with other writers
*/
- atomic_t mem ____cacheline_aligned_in_smp;
+ struct percpu_counter mem ____cacheline_aligned_in_smp;
+
/* sysctls */
int timeout;
int high_thresh;
@@ -82,29 +85,36 @@ static inline void inet_frag_put(struct inet_frag_queue *q, struct inet_frags *f
/* Memory Tracking Functions. */
+/* The default percpu_counter batch size is not big enough to scale to
+ * fragmentation mem acct sizes.
+ * The mem size of a 64K fragment is approx:
+ * (44 fragments * 2944 truesize) + frag_queue struct(200) = 129736 bytes
+ */
+static unsigned int frag_percpu_counter_batch = 130000;
+
static inline int frag_mem_limit(struct netns_frags *nf)
{
- return atomic_read(&nf->mem);
+ return percpu_counter_read(&nf->mem);
}
static inline void sub_frag_mem_limit(struct inet_frag_queue *q, int i)
{
- atomic_sub(i, &q->net->mem);
+ __percpu_counter_add(&q->net->mem, -i, frag_percpu_counter_batch);
}
static inline void add_frag_mem_limit(struct inet_frag_queue *q, int i)
{
- atomic_add(i, &q->net->mem);
+ __percpu_counter_add(&q->net->mem, i, frag_percpu_counter_batch);
}
static inline void init_frag_mem_limit(struct netns_frags *nf)
{
- atomic_set(&nf->mem, 0);
+ percpu_counter_init(&nf->mem, 0);
}
static inline int sum_frag_mem_limit(struct netns_frags *nf)
{
- return atomic_read(&nf->mem);
+ return percpu_counter_sum_positive(&nf->mem);
}
#endif
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index e348c84..b825205 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -91,6 +91,8 @@ void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f)
local_bh_disable();
inet_frag_evictor(nf, f, true);
local_bh_enable();
+
+ percpu_counter_destroy(&nf->mem);
}
EXPORT_SYMBOL(inet_frags_exit_net);
^ permalink raw reply related
* [net-next PATCH 6/6] net: frag, move LRU list maintenance outside of rwlock
From: Jesper Dangaard Brouer @ 2013-01-24 14:05 UTC (permalink / raw)
To: Eric Dumazet, David S. Miller, Florian Westphal
Cc: Jesper Dangaard Brouer, netdev, Pablo Neira Ayuso, Cong Wang,
Patrick McHardy, Herbert Xu, Daniel Borkmann
In-Reply-To: <20130124140343.14119.77712.stgit@dragon>
Updating the fragmentation queues LRU (Least-Recently-Used) list,
required taking the hash writer lock. However, the LRU list isn't
tied to the hash at all, so we can use a separate lock for it.
Original-idea-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
---
include/net/inet_frag.h | 22 ++++++++++++++++++++++
net/ipv4/inet_fragment.c | 12 +++++++-----
net/ipv4/ip_fragment.c | 4 +---
net/ipv6/netfilter/nf_conntrack_reasm.c | 5 ++---
net/ipv6/reassembly.c | 4 +---
5 files changed, 33 insertions(+), 14 deletions(-)
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index 85db72e..dd3cf75 100644
--- a/include/net/inet_frag.h
+++ b/include/net/inet_frag.h
@@ -6,6 +6,7 @@
struct netns_frags {
int nqueues;
struct list_head lru_list;
+ spinlock_t lru_lock;
/* The percpu_counter "mem" need to be cacheline aligned.
* mem.count must not share cacheline with other writers
@@ -117,4 +118,25 @@ static inline int sum_frag_mem_limit(struct netns_frags *nf)
return percpu_counter_sum_positive(&nf->mem);
}
+static inline void inet_frag_lru_move(struct inet_frag_queue *q)
+{
+ spin_lock(&q->net->lru_lock);
+ list_move_tail(&q->lru_list, &q->net->lru_list);
+ spin_unlock(&q->net->lru_lock);
+}
+
+static inline void inet_frag_lru_del(struct inet_frag_queue *q)
+{
+ spin_lock(&q->net->lru_lock);
+ list_del(&q->lru_list);
+ spin_unlock(&q->net->lru_lock);
+}
+
+static inline void inet_frag_lru_add(struct netns_frags *nf,
+ struct inet_frag_queue *q)
+{
+ spin_lock(&nf->lru_lock);
+ list_add_tail(&q->lru_list, &nf->lru_list);
+ spin_unlock(&nf->lru_lock);
+}
#endif
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index b825205..2e453bd 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -75,6 +75,7 @@ void inet_frags_init_net(struct netns_frags *nf)
nf->nqueues = 0;
init_frag_mem_limit(nf);
INIT_LIST_HEAD(&nf->lru_list);
+ spin_lock_init(&nf->lru_lock);
}
EXPORT_SYMBOL(inet_frags_init_net);
@@ -100,9 +101,9 @@ static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f)
{
write_lock(&f->lock);
hlist_del(&fq->list);
- list_del(&fq->lru_list);
fq->net->nqueues--;
write_unlock(&f->lock);
+ inet_frag_lru_del(fq);
}
void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f)
@@ -170,16 +171,17 @@ int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force)
work = frag_mem_limit(nf) - nf->low_thresh;
while (work > 0) {
- read_lock(&f->lock);
+ spin_lock(&nf->lru_lock);
+
if (list_empty(&nf->lru_list)) {
- read_unlock(&f->lock);
+ spin_unlock(&nf->lru_lock);
break;
}
q = list_first_entry(&nf->lru_list,
struct inet_frag_queue, lru_list);
atomic_inc(&q->refcnt);
- read_unlock(&f->lock);
+ spin_unlock(&nf->lru_lock);
spin_lock(&q->lock);
if (!(q->last_in & INET_FRAG_COMPLETE))
@@ -233,9 +235,9 @@ static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf,
atomic_inc(&qp->refcnt);
hlist_add_head(&qp->list, &f->hash[hash]);
- list_add_tail(&qp->lru_list, &nf->lru_list);
nf->nqueues++;
write_unlock(&f->lock);
+ inet_frag_lru_add(nf, qp);
return qp;
}
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 927fe58..1211613 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -529,9 +529,7 @@ found:
qp->q.meat == qp->q.len)
return ip_frag_reasm(qp, prev, dev);
- write_lock(&ip4_frags.lock);
- list_move_tail(&qp->q.lru_list, &qp->q.net->lru_list);
- write_unlock(&ip4_frags.lock);
+ inet_frag_lru_move(&qp->q);
return -EINPROGRESS;
err:
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 07ef294..c674f15 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -328,9 +328,8 @@ found:
fq->nhoffset = nhoff;
fq->q.last_in |= INET_FRAG_FIRST_IN;
}
- write_lock(&nf_frags.lock);
- list_move_tail(&fq->q.lru_list, &fq->q.net->lru_list);
- write_unlock(&nf_frags.lock);
+
+ inet_frag_lru_move(&fq->q);
return 0;
discard_fq:
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 18cb8de..bab2c27 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -341,9 +341,7 @@ found:
fq->q.meat == fq->q.len)
return ip6_frag_reasm(fq, prev, dev);
- write_lock(&ip6_frags.lock);
- list_move_tail(&fq->q.lru_list, &fq->q.net->lru_list);
- write_unlock(&ip6_frags.lock);
+ inet_frag_lru_move(&fq->q);
return -1;
discard_fq:
^ permalink raw reply related
* Re: [patch net-next] bond: have random dev address by default instead of zeroes
From: Andy Gospodarek @ 2013-01-24 14:37 UTC (permalink / raw)
To: Jiri Pirko; +Cc: netdev@vger.kernel.org, David Miller, Jay Vosburgh, stephen
In-Reply-To: <1359022329-6919-1-git-send-email-jiri@resnulli.us>
On Thu, Jan 24, 2013 at 5:12 AM, Jiri Pirko <jiri@resnulli.us> wrote:
> Makes more sense to have randomly generated address by default than to
> have all zeroes. It also allows user to for example put the bond into
> bridge without need to have any slaves in it.
>
> Also, fix dev_assign_type values on the way.
>
> Signed-off-by: Jiri Pirko <jiri@resnulli.us>
Seems like a pretty good plan.
Signed-off-by: Andy Gospodarek <andy@greyhouse.net>
> ---
> drivers/net/bonding/bond_main.c | 28 ++++++++++++++++------------
> 1 file changed, 16 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
> index 564cf42..af3a777 100644
> --- a/drivers/net/bonding/bond_main.c
> +++ b/drivers/net/bonding/bond_main.c
> @@ -1320,14 +1320,14 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
>
> /*---------------------------------- IOCTL ----------------------------------*/
>
> -static int bond_sethwaddr(struct net_device *bond_dev,
> - struct net_device *slave_dev)
> +static void bond_set_dev_addr(struct net_device *bond_dev,
> + struct net_device *slave_dev)
> {
> pr_debug("bond_dev=%p\n", bond_dev);
> pr_debug("slave_dev=%p\n", slave_dev);
> pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len);
> memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
> - return 0;
> + bond_dev->addr_assign_type |= NET_ADDR_SET;
> }
>
> static netdev_features_t bond_fix_features(struct net_device *dev,
> @@ -1628,10 +1628,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
>
> /* If this is the first slave, then we need to set the master's hardware
> * address to be the same as the slave's. */
> - if (is_zero_ether_addr(bond->dev->dev_addr))
> - memcpy(bond->dev->dev_addr, slave_dev->dev_addr,
> - slave_dev->addr_len);
> -
> + if (bond->dev->addr_assign_type != NET_ADDR_SET)
> + bond_set_dev_addr(bond->dev, slave_dev);
>
> new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
> if (!new_slave) {
> @@ -2049,11 +2047,11 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
> if (bond->slave_cnt == 0) {
> bond_set_carrier(bond);
>
> - /* if the last slave was removed, zero the mac address
> - * of the master so it will be set by the application
> - * to the mac address of the first slave
> + /* If the last slave was removed, set random mac address
> + * of the master so it will be set by bond_enslave()
> + * to the mac address of the first slave.
> */
> - memset(bond_dev->dev_addr, 0, bond_dev->addr_len);
> + eth_hw_addr_random(bond_dev);
>
> if (bond_vlan_used(bond)) {
> pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n",
> @@ -3708,7 +3706,8 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
> break;
> case BOND_SETHWADDR_OLD:
> case SIOCBONDSETHWADDR:
> - res = bond_sethwaddr(bond_dev, slave_dev);
> + bond_set_dev_addr(bond_dev, slave_dev);
> + res = 0;
> break;
> case BOND_CHANGE_ACTIVE_OLD:
> case SIOCBONDCHANGEACTIVE:
> @@ -4858,6 +4857,11 @@ static int bond_init(struct net_device *bond_dev)
>
> bond_debug_register(bond);
>
> + /* Ensure valid dev_addr */
> + if (is_zero_ether_addr(bond_dev->dev_addr) &&
> + bond_dev->addr_assign_type == NET_ADDR_PERM)
> + eth_hw_addr_random(bond_dev);
> +
> __hw_addr_init(&bond->mc_list);
> return 0;
> }
> --
> 1.8.1
>
^ permalink raw reply
* Re: [PATCH v2] sctp: set association state to established in dupcook_a handler
From: Vlad Yasevich @ 2013-01-24 15:05 UTC (permalink / raw)
To: xufengzhang.main; +Cc: nhorman, davem, linux-sctp, netdev, linux-kernel
In-Reply-To: <1358995474-28369-1-git-send-email-xufengzhang.main@gmail.com>
On 01/23/2013 09:44 PM, xufengzhang.main@gmail.com wrote:
> From: Xufeng Zhang <xufeng.zhang@windriver.com>
>
> While sctp handling a duplicate COOKIE-ECHO and the action is
> 'Association restart', sctp_sf_do_dupcook_a() will processing
> the unexpected COOKIE-ECHO for peer restart, but it does not set
> the association state to SCTP_STATE_ESTABLISHED, so the association
> could stuck in SCTP_STATE_SHUTDOWN_PENDING state forever.
> This violates the sctp specification:
> RFC 4960 5.2.4. Handle a COOKIE ECHO when a TCB Exists
> Action
> A) In this case, the peer may have restarted. .....
> After this, the endpoint shall enter the ESTABLISHED state.
>
> To resolve this problem, adding a SCTP_CMD_NEW_STATE cmd to the
> command list before SCTP_CMD_REPLY cmd, this will set the restart
> association to SCTP_STATE_ESTABLISHED state properly and also avoid
> I-bit being set in the DATA chunk header when COOKIE_ACK is bundled
> with DATA chunks.
>
> Signed-off-by: Xufeng Zhang <xufeng.zhang@windriver.com>
Acked-by: Vlad Yasevich <vyasevich@gmail.com>
-vlad
> ---
> v2:
> - Put the SCTP_CMD_NEW_STATE command before SCTP_CMD_REPLY and after SCTP_CMD_EVENT_ULP
> suggested by Vlad and Neil
> - Improve the last paragraph of the commit header
>
> net/sctp/sm_statefuns.c | 4 +++-
> 1 files changed, 3 insertions(+), 1 deletions(-)
>
> diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
> index 618ec7e..5131fcf 100644
> --- a/net/sctp/sm_statefuns.c
> +++ b/net/sctp/sm_statefuns.c
> @@ -1779,8 +1779,10 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(struct net *net,
>
> /* Update the content of current association. */
> sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc));
> - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
> sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
> + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
> + SCTP_STATE(SCTP_STATE_ESTABLISHED));
> + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
> return SCTP_DISPOSITION_CONSUME;
>
> nomem_ev:
>
^ permalink raw reply
* Re: [PATCH v5 01/45] percpu_rwlock: Introduce the global reader-writer lock backend
From: Oleg Nesterov @ 2013-01-24 15:58 UTC (permalink / raw)
To: Michel Lespinasse
Cc: linux-doc, peterz, fweisbec, mingo, linux-arch, linux,
xiaoguangrong, wangyun, paulmck, nikunj, linux-pm, rusty,
Steven Rostedt, rjw, namhyung, tglx, linux-arm-kernel, netdev,
linux-kernel, sbw, Srivatsa S. Bhat, tj, akpm, linuxppc-dev
In-Reply-To: <CANN689HOmiO-5714UiyuOcjrRYW2DkR4cPLgt96f5YeB9Ws3RQ@mail.gmail.com>
On 01/23, Michel Lespinasse wrote:
>
> On Tue, Jan 22, 2013 at 11:32 AM, Steven Rostedt <rostedt@goodmis.org> wrote:
> >
> > I thought global locks are now fair. That is, a reader will block if a
> > writer is waiting. Hence, the above should deadlock on the current
> > rwlock_t types.
>
> I believe you are mistaken here. struct rw_semaphore is fair (and
> blocking), but rwlock_t is unfair. The reason we can't easily make
> rwlock_t fair is because tasklist_lock currently depends on the
> rwlock_t unfairness - tasklist_lock readers typically don't disable
> local interrupts, and tasklist_lock may be acquired again from within
> an interrupt, which would deadlock if rwlock_t was fair and a writer
> was queued by the time the interrupt is processed.
Yes.
And, iirc, it was even documented somewhere that while rwlock_t is not
really nice, it is good to share the locking with interrupts. You do
not need to disable irqs.
Oleg.
^ permalink raw reply
* [PATCH] SCTP: Free the per-net sysctl table on net exit.
From: Vlad Yasevich @ 2013-01-24 16:02 UTC (permalink / raw)
To: netdev; +Cc: davem, linux-sctp, mmokrejs, Vlad Yasevich, Eric W. Biederman
In-Reply-To: <51004F2D.9000405@fold.natur.cuni.cz>
Per-net sysctl table needs to be explicitly freed at
net exit. Otherwise we see the following with kmemleak:
unreferenced object 0xffff880402d08000 (size 2048):
comm "chrome_sandbox", pid 18437, jiffies 4310887172 (age 9097.630s)
hex dump (first 32 bytes):
b2 68 89 81 ff ff ff ff 20 04 04 f8 01 88 ff ff .h...... .......
04 00 00 00 a4 01 00 00 00 00 00 00 00 00 00 00 ................
backtrace:
[<ffffffff815b4aad>] kmemleak_alloc+0x21/0x3e
[<ffffffff81110352>] slab_post_alloc_hook+0x28/0x2a
[<ffffffff81113fad>] __kmalloc_track_caller+0xf1/0x104
[<ffffffff810f10c2>] kmemdup+0x1b/0x30
[<ffffffff81571e9f>] sctp_sysctl_net_register+0x1f/0x72
[<ffffffff8155d305>] sctp_net_init+0x100/0x39f
[<ffffffff814ad53c>] ops_init+0xc6/0xf5
[<ffffffff814ad5b7>] setup_net+0x4c/0xd0
[<ffffffff814ada5e>] copy_net_ns+0x6d/0xd6
[<ffffffff810938b1>] create_new_namespaces+0xd7/0x147
[<ffffffff810939f4>] copy_namespaces+0x63/0x99
[<ffffffff81076733>] copy_process+0xa65/0x1233
[<ffffffff81077030>] do_fork+0x10b/0x271
[<ffffffff8100a0e9>] sys_clone+0x23/0x25
[<ffffffff815dda73>] stub_clone+0x13/0x20
[<ffffffffffffffff>] 0xffffffffffffffff
Reported-by: Martin Mokrejs <mmokrejs@fold.natur.cuni.cz>
Signed-off-by: Vlad Yasevich <vyasevich@gmail.com>
CC: "Eric W. Biederman" <ebiederm@xmission.com>
---
net/sctp/sysctl.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 043889a..cd5712f 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -366,7 +366,11 @@ int sctp_sysctl_net_register(struct net *net)
void sctp_sysctl_net_unregister(struct net *net)
{
+ struct ctl_table *table;
+
+ table = net->sctp.sysctl_hdr->ctl_table_arg;
unregister_net_sysctl_table(net->sctp.sysctl_header);
+ kfree(table);
}
static struct ctl_table_header * sctp_sysctl_header;
--
1.7.7.6
^ permalink raw reply related
* [PATCH/RFC 2/3] ethernet: add a PHY reset GPIO DT binding to sh_eth
From: Guennadi Liakhovetski @ 2013-01-24 16:07 UTC (permalink / raw)
To: linux-sh
Cc: Magnus Damm, Simon Horman, linux-arm-kernel,
Guennadi Liakhovetski, devicetree-discuss, netdev
In-Reply-To: <1359043653-11374-1-git-send-email-g.liakhovetski@gmx.de>
If an ethernet PHY can be reset by a GPIO, it can be specified in DT. Add
a binding and code to parse it, request the GPIO and take the PHY out of
reset.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Cc: devicetree-discuss@lists.ozlabs.org
Cc: netdev@vger.kernel.org
---
Documentation/devicetree/bindings/net/sh_ether.txt | 2 ++
drivers/net/ethernet/renesas/sh_eth.c | 9 ++++++++-
2 files changed, 10 insertions(+), 1 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/sh_ether.txt b/Documentation/devicetree/bindings/net/sh_ether.txt
index c11e45d..edaf683 100644
--- a/Documentation/devicetree/bindings/net/sh_ether.txt
+++ b/Documentation/devicetree/bindings/net/sh_ether.txt
@@ -12,6 +12,7 @@ Required properties:
- interrupts: Interrupt mapping for the sh_eth interrupt
sources (vector id).
- phy-mode: String, operation mode of the PHY interface.
+- phy-reset-gpios: PHY reset GPIO tuple
- sh-eth,edmac-endian: String, endian of sh_eth dmac.
- sh-eth,register-type: String, register type of sh_eth.
Please select "gigabit", "fast-sh4" or
@@ -37,6 +38,7 @@ Example (armadillo800eva):
reg = <0xe9a00000 0x800>, <0xe9a01800 0x800>;
interrupts = <0x500>;
phy-mode = "mii";
+ phy-reset-gpios = <&gpio 18 0>;
sh-eth,edmac-endian = "little";
sh-eth,register-type = "gigabit";
sh-eth,phy-id = <0>;
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 1f64848..06035a2 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -20,6 +20,7 @@
* the file called "COPYING".
*/
+#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -33,6 +34,7 @@
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -2376,10 +2378,11 @@ sh_eth_of_get_register_type(struct device_node *np)
static struct sh_eth_plat_data *
sh_eth_parse_dt(struct device *dev, struct net_device *ndev)
{
- int ret;
+ int ret, gpio;
const char *of_str;
struct device_node *np = dev->of_node;
struct sh_eth_plat_data *pdata;
+ enum of_gpio_flags flags;
pdata = devm_kzalloc(dev, sizeof(struct sh_eth_plat_data),
GFP_KERNEL);
@@ -2420,6 +2423,10 @@ sh_eth_parse_dt(struct device *dev, struct net_device *ndev)
else
pdata->needs_init = 0;
+ gpio = of_get_named_gpio_flags(np, "phy-reset-gpios", 0, &flags);
+ if (gpio_is_valid(gpio) && !devm_gpio_request(dev, gpio, NULL))
+ gpio_direction_output(gpio, !!(flags & OF_GPIO_ACTIVE_LOW));
+
#ifdef CONFIG_OF_NET
if (!is_valid_ether_addr(ndev->dev_addr)) {
const char *macaddr = of_get_mac_address(np);
--
1.7.2.5
^ permalink raw reply related
* Re: [PATCH] SCTP: Free the per-net sysctl table on net exit.
From: Neil Horman @ 2013-01-24 16:44 UTC (permalink / raw)
To: Vlad Yasevich; +Cc: netdev, davem, linux-sctp, mmokrejs, Eric W. Biederman
In-Reply-To: <1359043367-30180-1-git-send-email-vyasevich@gmail.com>
On Thu, Jan 24, 2013 at 11:02:47AM -0500, Vlad Yasevich wrote:
> Per-net sysctl table needs to be explicitly freed at
> net exit. Otherwise we see the following with kmemleak:
>
> unreferenced object 0xffff880402d08000 (size 2048):
> comm "chrome_sandbox", pid 18437, jiffies 4310887172 (age 9097.630s)
> hex dump (first 32 bytes):
> b2 68 89 81 ff ff ff ff 20 04 04 f8 01 88 ff ff .h...... .......
> 04 00 00 00 a4 01 00 00 00 00 00 00 00 00 00 00 ................
> backtrace:
> [<ffffffff815b4aad>] kmemleak_alloc+0x21/0x3e
> [<ffffffff81110352>] slab_post_alloc_hook+0x28/0x2a
> [<ffffffff81113fad>] __kmalloc_track_caller+0xf1/0x104
> [<ffffffff810f10c2>] kmemdup+0x1b/0x30
> [<ffffffff81571e9f>] sctp_sysctl_net_register+0x1f/0x72
> [<ffffffff8155d305>] sctp_net_init+0x100/0x39f
> [<ffffffff814ad53c>] ops_init+0xc6/0xf5
> [<ffffffff814ad5b7>] setup_net+0x4c/0xd0
> [<ffffffff814ada5e>] copy_net_ns+0x6d/0xd6
> [<ffffffff810938b1>] create_new_namespaces+0xd7/0x147
> [<ffffffff810939f4>] copy_namespaces+0x63/0x99
> [<ffffffff81076733>] copy_process+0xa65/0x1233
> [<ffffffff81077030>] do_fork+0x10b/0x271
> [<ffffffff8100a0e9>] sys_clone+0x23/0x25
> [<ffffffff815dda73>] stub_clone+0x13/0x20
> [<ffffffffffffffff>] 0xffffffffffffffff
>
> Reported-by: Martin Mokrejs <mmokrejs@fold.natur.cuni.cz>
> Signed-off-by: Vlad Yasevich <vyasevich@gmail.com>
> CC: "Eric W. Biederman" <ebiederm@xmission.com>
> ---
> net/sctp/sysctl.c | 4 ++++
> 1 files changed, 4 insertions(+), 0 deletions(-)
>
> diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
> index 043889a..cd5712f 100644
> --- a/net/sctp/sysctl.c
> +++ b/net/sctp/sysctl.c
> @@ -366,7 +366,11 @@ int sctp_sysctl_net_register(struct net *net)
>
> void sctp_sysctl_net_unregister(struct net *net)
> {
> + struct ctl_table *table;
> +
> + table = net->sctp.sysctl_hdr->ctl_table_arg;
> unregister_net_sysctl_table(net->sctp.sysctl_header);
> + kfree(table);
> }
>
> static struct ctl_table_header * sctp_sysctl_header;
> --
> 1.7.7.6
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
^ permalink raw reply
* Re: [PATCH v3 1/1 net-next] net: fec: enable pause frame to improve rx prefomance for 1G network
From: Ben Hutchings @ 2013-01-24 16:49 UTC (permalink / raw)
To: Frank Li
Cc: Frank Li, shawn.guo, B38611, davem, linux-arm-kernel, netdev,
s.hauer
In-Reply-To: <CAHrpEqSzu97X2PUgUEh30EqkbjLUYyhCN28AEzJ6eLPXx0Qg1Q@mail.gmail.com>
On Thu, 2013-01-24 at 10:16 +0800, Frank Li wrote:
> 2013/1/24 Ben Hutchings <bhutchings@solarflare.com>:
> > On Thu, 2013-01-17 at 10:55 +0800, Frank Li wrote:
> >> The limition of imx6 internal bus cause fec can't achieve 1G perfomance.
> >> There will be many packages lost because FIFO over run.
> >>
> >> This patch enable pause frame flow control.
> > [...]
> >> --- a/drivers/net/ethernet/freescale/fec.c
> >> +++ b/drivers/net/ethernet/freescale/fec.c
> > [...]
> >> +static int fec_enet_set_pauseparam(struct net_device *ndev,
> >> + struct ethtool_pauseparam *pause)
> >> +{
> >> + struct fec_enet_private *fep = netdev_priv(ndev);
> >> +
> >> + if (pause->tx_pause != pause->rx_pause) {
> >> + netdev_info(ndev,
> >> + "hardware only support enable/disable both tx and rx");
> >> + return -EINVAL;
> >> + }
> >> +
> >> + fep->pause_flag = 0;
> >> +
> >> + /* tx pause must be same as rx pause */
> >> + fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0;
> >> + fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0;
> >> +
> >> + if (pause->rx_pause || pause->autoneg) {
> >> + fep->phy_dev->supported |= ADVERTISED_Pause;
> >> + fep->phy_dev->advertising |= ADVERTISED_Pause;
> >> + } else {
> >> + fep->phy_dev->supported &= ~ADVERTISED_Pause;
> >> + fep->phy_dev->advertising &= ~ADVERTISED_Pause;
> >> + }
> > [...]
> >
> > Why is this changing the supported flags, i.e. device capabilities? You
> > need to leave those flags alone and reject an attempt to enable pause
> > frames on a device that doesn't support them.
>
> I go through phylib, I have not found good place set ADVERTISED_Pause
> capabilities.
> genphy_config_init never check Pause capabilities.
I agree that phylib can't initialise pause capabilities because those
depend on the MAC. But look at which function I'm quoting: this is the
ethtool operation, which shouldn't change capabilities.
Ben.
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* YOUR ATM VISA CARD IS READY FOR DELIVERY
From: ATM VISA CARD @ 2013-01-24 16:53 UTC (permalink / raw)
[-- Attachment #1: Type: text/plain, Size: 49 bytes --]
OPEN THE ATTACHED FILE FOR MORE DETAILS
[-- Attachment #2: ATM_VISA_CARD_NOTIFICATION_LETTER-1.doc --]
[-- Type: application/octet-stream, Size: 3670 bytes --]
^ permalink raw reply
* Re: [patch net-next] bond: have random dev address by default instead of zeroes
From: Jay Vosburgh @ 2013-01-24 17:12 UTC (permalink / raw)
To: Jiri Pirko; +Cc: netdev, davem, andy, stephen
In-Reply-To: <1359022329-6919-1-git-send-email-jiri@resnulli.us>
Jiri Pirko <jiri@resnulli.us> wrote:
>Makes more sense to have randomly generated address by default than to
>have all zeroes. It also allows user to for example put the bond into
>bridge without need to have any slaves in it.
>
>Also, fix dev_assign_type values on the way.
>
>Signed-off-by: Jiri Pirko <jiri@resnulli.us>
This patch appears to provide a random MAC, which is then
replaced with the first slave's MAC at enslavement time. A more in
depth commit message would be helpful here; I presumed from the above
that the bond's MAC was being set to a random MAC and the first slave
behavior was changing, but inspection of the code suggests otherwise.
Does this have any practical change other than permitting
enslavement to a bridge prior to the bond having any slaves?
I also have one code comment, below.
> drivers/net/bonding/bond_main.c | 28 ++++++++++++++++------------
> 1 file changed, 16 insertions(+), 12 deletions(-)
>
>diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
>index 564cf42..af3a777 100644
>--- a/drivers/net/bonding/bond_main.c
>+++ b/drivers/net/bonding/bond_main.c
>@@ -1320,14 +1320,14 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
>
> /*---------------------------------- IOCTL ----------------------------------*/
>
>-static int bond_sethwaddr(struct net_device *bond_dev,
>- struct net_device *slave_dev)
>+static void bond_set_dev_addr(struct net_device *bond_dev,
>+ struct net_device *slave_dev)
> {
> pr_debug("bond_dev=%p\n", bond_dev);
> pr_debug("slave_dev=%p\n", slave_dev);
> pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len);
> memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
>- return 0;
>+ bond_dev->addr_assign_type |= NET_ADDR_SET;
This is a bitwise assignment, but addr_assign_type is not a
bitmask. If addr_assign_type is not 0 prior to this, this will create
an illegal value in the field.
-J
> }
>
> static netdev_features_t bond_fix_features(struct net_device *dev,
>@@ -1628,10 +1628,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
>
> /* If this is the first slave, then we need to set the master's hardware
> * address to be the same as the slave's. */
>- if (is_zero_ether_addr(bond->dev->dev_addr))
>- memcpy(bond->dev->dev_addr, slave_dev->dev_addr,
>- slave_dev->addr_len);
>-
>+ if (bond->dev->addr_assign_type != NET_ADDR_SET)
>+ bond_set_dev_addr(bond->dev, slave_dev);
>
> new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
> if (!new_slave) {
>@@ -2049,11 +2047,11 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
> if (bond->slave_cnt == 0) {
> bond_set_carrier(bond);
>
>- /* if the last slave was removed, zero the mac address
>- * of the master so it will be set by the application
>- * to the mac address of the first slave
>+ /* If the last slave was removed, set random mac address
>+ * of the master so it will be set by bond_enslave()
>+ * to the mac address of the first slave.
> */
>- memset(bond_dev->dev_addr, 0, bond_dev->addr_len);
>+ eth_hw_addr_random(bond_dev);
>
> if (bond_vlan_used(bond)) {
> pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n",
>@@ -3708,7 +3706,8 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
> break;
> case BOND_SETHWADDR_OLD:
> case SIOCBONDSETHWADDR:
>- res = bond_sethwaddr(bond_dev, slave_dev);
>+ bond_set_dev_addr(bond_dev, slave_dev);
>+ res = 0;
> break;
> case BOND_CHANGE_ACTIVE_OLD:
> case SIOCBONDCHANGEACTIVE:
>@@ -4858,6 +4857,11 @@ static int bond_init(struct net_device *bond_dev)
>
> bond_debug_register(bond);
>
>+ /* Ensure valid dev_addr */
>+ if (is_zero_ether_addr(bond_dev->dev_addr) &&
>+ bond_dev->addr_assign_type == NET_ADDR_PERM)
>+ eth_hw_addr_random(bond_dev);
>+
> __hw_addr_init(&bond->mc_list);
> return 0;
> }
>--
>1.8.1
>
---
-Jay Vosburgh, IBM Linux Technology Center, fubar@us.ibm.com
^ permalink raw reply
* Re: [PATCH V6 1/3] virtio-net: fix the set affinity bug when CPU IDs are not consecutive
From: Michael S. Tsirkin @ 2013-01-24 17:19 UTC (permalink / raw)
To: Wanlong Gao; +Cc: netdev, linux-kernel, virtualization, Eric Dumazet
In-Reply-To: <1358767524-17934-1-git-send-email-gaowanlong@cn.fujitsu.com>
On Mon, Jan 21, 2013 at 07:25:22PM +0800, Wanlong Gao wrote:
> As Michael mentioned, set affinity and select queue will not work very
> well when CPU IDs are not consecutive, this can happen with hot unplug.
> Fix this bug by traversal the online CPUs, and create a per cpu variable
> to find the mapping from CPU to the preferable virtual-queue.
>
> Cc: Rusty Russell <rusty@rustcorp.com.au>
> Cc: "Michael S. Tsirkin" <mst@redhat.com>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Eric Dumazet <erdnetdev@gmail.com>
> Cc: virtualization@lists.linux-foundation.org
> Cc: netdev@vger.kernel.org
> Signed-off-by: Wanlong Gao <gaowanlong@cn.fujitsu.com>
For the series:
Acked-by: Michael S. Tsirkin <mst@redhat.com>
> ---
> V5->V6:
> remove {get|put}_online_cpus from virtnet_del_vqs (Jason)
> V4->V5:
> Add get/put_online_cpus to avoid CPUs go up and down during operations (Rusty)
>
> V3->V4:
> move vq_index into virtnet_info (Jason)
> change the mapping value when not setting affinity (Jason)
> address the comments about select_queue (Rusty)
>
> drivers/net/virtio_net.c | 58 +++++++++++++++++++++++++++++++++++++++---------
> 1 file changed, 47 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index a6fcf15..70cd957 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -123,6 +123,9 @@ struct virtnet_info {
>
> /* Does the affinity hint is set for virtqueues? */
> bool affinity_hint_set;
> +
> + /* Per-cpu variable to show the mapping from CPU to virtqueue */
> + int __percpu *vq_index;
> };
>
> struct skb_vnet_hdr {
> @@ -1016,6 +1019,7 @@ static int virtnet_vlan_rx_kill_vid(struct net_device *dev, u16 vid)
> static void virtnet_set_affinity(struct virtnet_info *vi, bool set)
> {
> int i;
> + int cpu;
>
> /* In multiqueue mode, when the number of cpu is equal to the number of
> * queue pairs, we let the queue pairs to be private to one cpu by
> @@ -1029,16 +1033,29 @@ static void virtnet_set_affinity(struct virtnet_info *vi, bool set)
> return;
> }
>
> - for (i = 0; i < vi->max_queue_pairs; i++) {
> - int cpu = set ? i : -1;
> - virtqueue_set_affinity(vi->rq[i].vq, cpu);
> - virtqueue_set_affinity(vi->sq[i].vq, cpu);
> - }
> + if (set) {
> + i = 0;
> + for_each_online_cpu(cpu) {
> + virtqueue_set_affinity(vi->rq[i].vq, cpu);
> + virtqueue_set_affinity(vi->sq[i].vq, cpu);
> + *per_cpu_ptr(vi->vq_index, cpu) = i;
> + i++;
> + }
>
> - if (set)
> vi->affinity_hint_set = true;
> - else
> + } else {
> + for(i = 0; i < vi->max_queue_pairs; i++) {
> + virtqueue_set_affinity(vi->rq[i].vq, -1);
> + virtqueue_set_affinity(vi->sq[i].vq, -1);
> + }
> +
> + i = 0;
> + for_each_online_cpu(cpu)
> + *per_cpu_ptr(vi->vq_index, cpu) =
> + ++i % vi->curr_queue_pairs;
> +
> vi->affinity_hint_set = false;
> + }
> }
>
> static void virtnet_get_ringparam(struct net_device *dev,
> @@ -1087,7 +1104,9 @@ static int virtnet_set_channels(struct net_device *dev,
> netif_set_real_num_tx_queues(dev, queue_pairs);
> netif_set_real_num_rx_queues(dev, queue_pairs);
>
> + get_online_cpus();
> virtnet_set_affinity(vi, true);
> + put_online_cpus();
> }
>
> return err;
> @@ -1127,12 +1146,19 @@ static int virtnet_change_mtu(struct net_device *dev, int new_mtu)
>
> /* To avoid contending a lock hold by a vcpu who would exit to host, select the
> * txq based on the processor id.
> - * TODO: handle cpu hotplug.
> */
> static u16 virtnet_select_queue(struct net_device *dev, struct sk_buff *skb)
> {
> - int txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) :
> - smp_processor_id();
> + int txq;
> + struct virtnet_info *vi = netdev_priv(dev);
> +
> + if (skb_rx_queue_recorded(skb)) {
> + txq = skb_get_rx_queue(skb);
> + } else {
> + txq = *__this_cpu_ptr(vi->vq_index);
> + if (txq == -1)
> + txq = 0;
> + }
>
> while (unlikely(txq >= dev->real_num_tx_queues))
> txq -= dev->real_num_tx_queues;
> @@ -1371,7 +1397,10 @@ static int init_vqs(struct virtnet_info *vi)
> if (ret)
> goto err_free;
>
> + get_online_cpus();
> virtnet_set_affinity(vi, true);
> + put_online_cpus();
> +
> return 0;
>
> err_free:
> @@ -1453,6 +1482,10 @@ static int virtnet_probe(struct virtio_device *vdev)
> if (vi->stats == NULL)
> goto free;
>
> + vi->vq_index = alloc_percpu(int);
> + if (vi->vq_index == NULL)
> + goto free_stats;
> +
> mutex_init(&vi->config_lock);
> vi->config_enable = true;
> INIT_WORK(&vi->config_work, virtnet_config_changed_work);
> @@ -1476,7 +1509,7 @@ static int virtnet_probe(struct virtio_device *vdev)
> /* Allocate/initialize the rx/tx queues, and invoke find_vqs */
> err = init_vqs(vi);
> if (err)
> - goto free_stats;
> + goto free_index;
>
> netif_set_real_num_tx_queues(dev, 1);
> netif_set_real_num_rx_queues(dev, 1);
> @@ -1520,6 +1553,8 @@ free_recv_bufs:
> free_vqs:
> cancel_delayed_work_sync(&vi->refill);
> virtnet_del_vqs(vi);
> +free_index:
> + free_percpu(vi->vq_index);
> free_stats:
> free_percpu(vi->stats);
> free:
> @@ -1554,6 +1589,7 @@ static void virtnet_remove(struct virtio_device *vdev)
>
> flush_work(&vi->config_work);
>
> + free_percpu(vi->vq_index);
> free_percpu(vi->stats);
> free_netdev(vi->dev);
> }
> --
> 1.8.1
^ permalink raw reply
* Re: [PATCH 0/3] tg3: add support for Ethernet core in BCM4785
From: Hauke Mehrtens @ 2013-01-24 17:21 UTC (permalink / raw)
To: davem; +Cc: mcarlson, mchan, netdev, m
In-Reply-To: <1358020905-9924-1-git-send-email-hauke@hauke-m.de>
On 01/12/2013 09:01 PM, Hauke Mehrtens wrote:
> These patches are adding support for the Ethernet core found in the BCM4705/BCM4785 SoC.
>
> Hauke Mehrtens (3):
> ssb: add missing method ssb_gige_get_macaddr
> tg3: make it possible to provide phy_id in ioctl
> tg3: add support for Ethernet core in bcm4785
>
> drivers/net/ethernet/broadcom/tg3.c | 137 +++++++++++++++++++++++++++++++----
> drivers/net/ethernet/broadcom/tg3.h | 5 ++
> include/linux/pci_ids.h | 1 +
> include/linux/ssb/ssb_driver_gige.h | 3 +
> 4 files changed, 133 insertions(+), 13 deletions(-)
>
Hi David,
Should I resend these patches with error handling if the mac address
could not be set?
Hauke
^ permalink raw reply
* Re: [PATCH v2 1/1 net-next] net: fec: add napi support to improve proformance
From: Troy Kisky @ 2013-01-24 17:56 UTC (permalink / raw)
To: Frank Li
Cc: lznuaa, shawn.guo, B38611, davem, linux-arm-kernel, netdev,
s.hauer
In-Reply-To: <1359014309-8636-1-git-send-email-Frank.Li@freescale.com>
On 1/24/2013 12:58 AM, Frank Li wrote:
> diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c
> index f52ba33..39be2ab 100644
> --- a/drivers/net/ethernet/freescale/fec.c
> +++ b/drivers/net/ethernet/freescale/fec.c
> @@ -67,6 +67,7 @@
> #endif
>
> #define DRIVER_NAME "fec"
> +#define FEC_NAPI_WEIGHT 64
>
> /* Pause frame feild and FIFO threshold */
> #define FEC_ENET_FCE (1 << 5)
> @@ -565,6 +566,20 @@ fec_timeout(struct net_device *ndev)
> }
>
> static void
> +fec_enet_rx_int_is_enabled(struct net_device *ndev, bool enabled)
> +{
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + uint int_events;
> +
> + int_events = readl(fep->hwp + FEC_IMASK);
> + if (enabled)
> + int_events |= FEC_ENET_RXF;
> + else
> + int_events &= ~FEC_ENET_RXF;
> + writel(int_events, fep->hwp + FEC_IMASK);
> +}
fec_enet_rx_int_is_enabled looks like a question.
fec_enet_rx_int_enable would be a better name
^ permalink raw reply
* Re: Hung tasks related to do_filp_open in net-next (3.8.0-rc3+)
From: Ben Greear @ 2013-01-24 18:42 UTC (permalink / raw)
To: linux-nfs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, netdev
In-Reply-To: <51005858.4040107-my8/4N5VtI7c+919tysfdA@public.gmane.org>
On 01/23/2013 01:38 PM, Ben Greear wrote:
>
> I cranked up my 3000 NFS mounts test case on today's net-next (3.8.0-rc3+)
> and about 8 of these splats. There were around 780 mounts active
> when these occurred.
>
> Seems like the system continues OK, so not sure how important
> this is...
I'm seeing this occasionally on 3.7.3 (plus Eric's patch to fix
the crashes I was seeing), so it's not a regression.
Maybe it's just expected on a very overloaded system....
Thanks,
Ben
>
> INFO: task btserver:8756 blocked for more than 180 seconds.
> "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> btserver D 0000000000000004 0 8756 5781 0x00000080
> ffff880404ab1bf8 0000000000000046 0000000004ab1ba8 0000000000000432
> ffff880404ab0010 ffff880402263f20 ffff880404ab1fd8 00000000000132c0
> 00000000000132c0 00000000000132c0 ffff880404ab1fd8 00000000000132c0
> Call Trace:
> [<ffffffff815491ea>] schedule+0x5f/0x61
> [<ffffffff815494fa>] schedule_preempt_disabled+0x13/0x1f
> [<ffffffff81547ea8>] __mutex_lock_common+0x264/0x42d
> [<ffffffff8115fadb>] ? do_last+0x1db/0xa4e
> [<ffffffff8115fadb>] ? do_last+0x1db/0xa4e
> [<ffffffff815480d0>] mutex_lock_nested+0x16/0x18
> [<ffffffff8115fadb>] do_last+0x1db/0xa4e
> [<ffffffff8115f3a5>] ? path_init+0xcd/0x2b6
> [<ffffffff8116090b>] path_openat+0xcb/0x363
> [<ffffffff810d0055>] ? __lock_acquire+0x1df/0x53d
> [<ffffffff81160ca8>] do_filp_open+0x38/0x84
> [<ffffffff8154a2db>] ? _raw_spin_unlock+0x45/0x52
> [<ffffffff8116c203>] ? __alloc_fd+0x106/0x11a
> [<ffffffff815490f9>] ? __schedule+0x561/0x5a3
> 8021q: adding VLAN 0 to HW filter on device eth4#621
> [<ffffffff81153bfc>] do_sys_open+0x6d/0xff
> 8021q: adding VLAN 0 to HW filter on device eth3#612
> [<ffffffff81153cbb>] sys_open+0x1c/0x1e
> [<ffffffff8154f629>] system_call_fastpath+0x16/0x1b
> 2 locks held by btserver/8756:
> #0: (sb_writers#10){......}, at: [<ffffffff81170013>] mnt_want_write+0x1f/0x48
> #1: (&type->i_mutex_dir_key#4){......}, at: [<ffffffff8115fadb>] do_last+0x1db/0xa4e
>
>
>
--
Ben Greear <greearb-my8/4N5VtI7c+919tysfdA@public.gmane.org>
Candela Technologies Inc http://www.candelatech.com
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: Doubts about listen backlog and tcp_max_syn_backlog
From: Rick Jones @ 2013-01-24 18:44 UTC (permalink / raw)
To: Leandro Lucarella; +Cc: Eric Dumazet, netdev, linux-kernel
In-Reply-To: <20130124122223.GQ4608@sociomantic.com>
On 01/24/2013 04:22 AM, Leandro Lucarella wrote:
> On Wed, Jan 23, 2013 at 11:28:08AM -0800, Rick Jones wrote:
>>> Then if syncookies are enabled, the time spent in connect() shouldn't be
>>> bigger than 3 seconds even if SYNs are being "dropped" by listen, right?
>>
>> Do you mean if "ESTABLISHED" connections are dropped because the
>> listen queue is full? I don't think I would put that as "SYNs being
>> dropped by listen" - too easy to confuse that with an actual
>> dropping of a SYN segment.
>
> I was just kind of quoting the name given by netstat: "SYNs to LISTEN
> sockets dropped" (for kernel 3.0, I noticed newer kernels don't have
> this stat anymore, or the name was changed). I still don't know if we
> are talking about the same thing.
Are you sure those stats are not present in 3.X kernels? I just looked
at /proc/net/netstat on a 3.7 system and noticed both the ListenMumble
stats and the three cookie stats. And I see the code for them in the tree:
aj@tardy:~/net-next/net/ipv4$ grep MIB_LISTEN *.c
proc.c: SNMP_MIB_ITEM("ListenOverflows", LINUX_MIB_LISTENOVERFLOWS),
proc.c: SNMP_MIB_ITEM("ListenDrops", LINUX_MIB_LISTENDROPS),
tcp_ipv4.c: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
tcp_ipv4.c: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
raj@tardy:~/net-next/net/ipv4$ grep MIB_SYN *.c
proc.c: SNMP_MIB_ITEM("SyncookiesSent", LINUX_MIB_SYNCOOKIESSENT),
proc.c: SNMP_MIB_ITEM("SyncookiesRecv", LINUX_MIB_SYNCOOKIESRECV),
proc.c: SNMP_MIB_ITEM("SyncookiesFailed", LINUX_MIB_SYNCOOKIESFAILED),
syncookies.c: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT);
syncookies.c: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED);
syncookies.c: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESRECV);
I will sometimes be tripped-up by netstat's not showing a statistic with
a zero value...
>> But yes, I would not expect a connect() call to remain incomplete
>> for any longer than it took to receive an SYN|ACK from the other
>> end.
>
> So the only reason to experience these high times spent in connect()
> should be because a SYN or SYN|ACK was actually loss in a lower layer,
> like an error in the network device or a transmission error?
Modulo the/some other drop-without-stat point such as Vijay mentioned
yesterday.
You might consider taking some packet traces. If you can I would start
with a trace taken on the system(s) on which the long connect() calls
are happening. I think the tcpdump manpage has an example of a tcpdump
command with a filter expression that catches just SYNchronize and
FINished segments which I suppose you could extend to include ReSeT
segments. Such a filter expression would be missing the client's ACK of
the SYN|ACK but unless you see incrementing stats relating to say
checksum failures or other drops on the "client" side I suppose you
could assume that the client ACKed the server's SYN|ACK.
>> That would be 3 (,9, 21, etc...) seconds on a kernel with 3
>> seconds as the initial retransmission timeout.
>
> Which can't be changed without recompiling, right?
To the best of my knowledge.
rick jones
^ permalink raw reply
* [PATCH net] net: usbnet: prevent buggy devices from killing us
From: Bjørn Mork @ 2013-01-24 19:16 UTC (permalink / raw)
To: Oliver Neukum
Cc: linux-usb-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Bjørn Mork
In-Reply-To: <2556579.vjYlY1iKn5-7ztolUikljGernLeA6q8OA@public.gmane.org>
A device sending 0 length frames as fast as it can has been
observed killing the host system due to the resulting memory
pressure.
Temporarily disable RX skb allocation and URB submission when
the current error ratio is high, preventing us from trying to
allocate an infinite number of skbs. Reenable as soon as we
are finished processing the done queue, allowing the device
to continue working after short error bursts.
Signed-off-by: Bjørn Mork <bjorn-yOkvZcmFvRU@public.gmane.org>
---
So is this starting to look OK?
usbnet already uses "throttle", "halt" and "stop" for other
functions, so I decided to name the new flag "kill". No other
reason.
Didn't see any point in calculating the error limit. A fixed
number works just as well.
Restarting in usbnet_bh was a simple way to achieve what I
wanted: enabling RX again when we know we can handle it.
Bjørn
drivers/net/usb/usbnet.c | 26 ++++++++++++++++++++++++++
include/linux/usb/usbnet.h | 2 ++
2 files changed, 28 insertions(+), 0 deletions(-)
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index f34b2eb..64657d6 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -380,6 +380,12 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
unsigned long lockflags;
size_t size = dev->rx_urb_size;
+ /* prevent rx skb allocation when error ratio is high */
+ if (test_bit(EVENT_RX_KILL, &dev->flags)) {
+ usb_free_urb(urb);
+ return -ENOLINK;
+ }
+
skb = __netdev_alloc_skb_ip_align(dev->net, size, flags);
if (!skb) {
netif_dbg(dev, rx_err, dev->net, "no rx skb\n");
@@ -539,6 +545,22 @@ block:
break;
}
+ /* stop rx if packet error rate is high */
+ if (++dev->pkt_cnt > 30) {
+ dev->pkt_cnt = 0;
+ dev->pkt_err = 0;
+ } else {
+ if (state == rx_cleanup)
+ dev->pkt_err++;
+ if (dev->pkt_err > 20) {
+ set_bit(EVENT_RX_KILL, &dev->flags);
+ if (net_ratelimit())
+ netif_dbg(dev, rx_err, dev->net,
+ "rx kill: high error rate\n");
+ dev->pkt_err = 0;
+ }
+ }
+
state = defer_bh(dev, skb, &dev->rxq, state);
if (urb) {
@@ -1254,6 +1276,10 @@ static void usbnet_bh (unsigned long param)
}
}
+ /* restart RX again after disabling due to high error rate */
+ if (test_and_clear_bit(EVENT_RX_KILL, &dev->flags) && net_ratelimit())
+ netif_dbg(dev, rx_err, dev->net, "rx kill: restarting\n");
+
// waiting for all pending urbs to complete?
if (dev->wait) {
if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) {
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 5de7a22..0de078d 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -33,6 +33,7 @@ struct usbnet {
wait_queue_head_t *wait;
struct mutex phy_mutex;
unsigned char suspend_count;
+ unsigned char pkt_cnt, pkt_err;
/* i/o info: pipes etc */
unsigned in, out;
@@ -70,6 +71,7 @@ struct usbnet {
# define EVENT_DEV_OPEN 7
# define EVENT_DEVICE_REPORT_IDLE 8
# define EVENT_NO_RUNTIME_PM 9
+# define EVENT_RX_KILL 10
};
static inline struct usb_driver *driver_of(struct usb_interface *intf)
--
1.7.2.5
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [patch net-next] bond: have random dev address by default instead of zeroes
From: Jiri Pirko @ 2013-01-24 19:18 UTC (permalink / raw)
To: Jay Vosburgh; +Cc: netdev, davem, andy, stephen
In-Reply-To: <7112.1359047569@death.nxdomain>
Thu, Jan 24, 2013 at 06:12:49PM CET, fubar@us.ibm.com wrote:
>Jiri Pirko <jiri@resnulli.us> wrote:
>
>>Makes more sense to have randomly generated address by default than to
>>have all zeroes. It also allows user to for example put the bond into
>>bridge without need to have any slaves in it.
>>
>>Also, fix dev_assign_type values on the way.
>>
>>Signed-off-by: Jiri Pirko <jiri@resnulli.us>
>
> This patch appears to provide a random MAC, which is then
>replaced with the first slave's MAC at enslavement time. A more in
>depth commit message would be helpful here; I presumed from the above
>that the bond's MAC was being set to a random MAC and the first slave
>behavior was changing, but inspection of the code suggests otherwise.
Sure, I will make sthi clear in commit message.
>
> Does this have any practical change other than permitting
>enslavement to a bridge prior to the bond having any slaves?
Nothing I can think of.
>
> I also have one code comment, below.
>
>> drivers/net/bonding/bond_main.c | 28 ++++++++++++++++------------
>> 1 file changed, 16 insertions(+), 12 deletions(-)
>>
>>diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
>>index 564cf42..af3a777 100644
>>--- a/drivers/net/bonding/bond_main.c
>>+++ b/drivers/net/bonding/bond_main.c
>>@@ -1320,14 +1320,14 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
>>
>> /*---------------------------------- IOCTL ----------------------------------*/
>>
>>-static int bond_sethwaddr(struct net_device *bond_dev,
>>- struct net_device *slave_dev)
>>+static void bond_set_dev_addr(struct net_device *bond_dev,
>>+ struct net_device *slave_dev)
>> {
>> pr_debug("bond_dev=%p\n", bond_dev);
>> pr_debug("slave_dev=%p\n", slave_dev);
>> pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len);
>> memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
>>- return 0;
>>+ bond_dev->addr_assign_type |= NET_ADDR_SET;
>
> This is a bitwise assignment, but addr_assign_type is not a
>bitmask. If addr_assign_type is not 0 prior to this, this will create
>an illegal value in the field.
You are right. I'll correct this.
>
> -J
>
>> }
>>
>> static netdev_features_t bond_fix_features(struct net_device *dev,
>>@@ -1628,10 +1628,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
>>
>> /* If this is the first slave, then we need to set the master's hardware
>> * address to be the same as the slave's. */
>>- if (is_zero_ether_addr(bond->dev->dev_addr))
>>- memcpy(bond->dev->dev_addr, slave_dev->dev_addr,
>>- slave_dev->addr_len);
>>-
>>+ if (bond->dev->addr_assign_type != NET_ADDR_SET)
>>+ bond_set_dev_addr(bond->dev, slave_dev);
>>
>> new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
>> if (!new_slave) {
>>@@ -2049,11 +2047,11 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
>> if (bond->slave_cnt == 0) {
>> bond_set_carrier(bond);
>>
>>- /* if the last slave was removed, zero the mac address
>>- * of the master so it will be set by the application
>>- * to the mac address of the first slave
>>+ /* If the last slave was removed, set random mac address
>>+ * of the master so it will be set by bond_enslave()
>>+ * to the mac address of the first slave.
>> */
>>- memset(bond_dev->dev_addr, 0, bond_dev->addr_len);
>>+ eth_hw_addr_random(bond_dev);
>>
>> if (bond_vlan_used(bond)) {
>> pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n",
>>@@ -3708,7 +3706,8 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
>> break;
>> case BOND_SETHWADDR_OLD:
>> case SIOCBONDSETHWADDR:
>>- res = bond_sethwaddr(bond_dev, slave_dev);
>>+ bond_set_dev_addr(bond_dev, slave_dev);
>>+ res = 0;
>> break;
>> case BOND_CHANGE_ACTIVE_OLD:
>> case SIOCBONDCHANGEACTIVE:
>>@@ -4858,6 +4857,11 @@ static int bond_init(struct net_device *bond_dev)
>>
>> bond_debug_register(bond);
>>
>>+ /* Ensure valid dev_addr */
>>+ if (is_zero_ether_addr(bond_dev->dev_addr) &&
>>+ bond_dev->addr_assign_type == NET_ADDR_PERM)
>>+ eth_hw_addr_random(bond_dev);
>>+
>> __hw_addr_init(&bond->mc_list);
>> return 0;
>> }
>>--
>>1.8.1
>>
>
>---
> -Jay Vosburgh, IBM Linux Technology Center, fubar@us.ibm.com
>
^ permalink raw reply
* Re: Doubts about listen backlog and tcp_max_syn_backlog
From: Leandro Lucarella @ 2013-01-24 19:21 UTC (permalink / raw)
To: Rick Jones; +Cc: Eric Dumazet, netdev, linux-kernel
In-Reply-To: <51018110.1070403@hp.com>
On Thu, Jan 24, 2013 at 10:44:32AM -0800, Rick Jones wrote:
> On 01/24/2013 04:22 AM, Leandro Lucarella wrote:
> >On Wed, Jan 23, 2013 at 11:28:08AM -0800, Rick Jones wrote:
> >>>Then if syncookies are enabled, the time spent in connect() shouldn't be
> >>>bigger than 3 seconds even if SYNs are being "dropped" by listen, right?
> >>
> >>Do you mean if "ESTABLISHED" connections are dropped because the
> >>listen queue is full? I don't think I would put that as "SYNs being
> >>dropped by listen" - too easy to confuse that with an actual
> >>dropping of a SYN segment.
> >
> >I was just kind of quoting the name given by netstat: "SYNs to LISTEN
> >sockets dropped" (for kernel 3.0, I noticed newer kernels don't have
> >this stat anymore, or the name was changed). I still don't know if we
> >are talking about the same thing.
>
> Are you sure those stats are not present in 3.X kernels? I just
> looked at /proc/net/netstat on a 3.7 system and noticed both the
> ListenMumble stats and the three cookie stats. And I see the code
> for them in the tree:
>
> aj@tardy:~/net-next/net/ipv4$ grep MIB_LISTEN *.c
> proc.c: SNMP_MIB_ITEM("ListenOverflows", LINUX_MIB_LISTENOVERFLOWS),
> proc.c: SNMP_MIB_ITEM("ListenDrops", LINUX_MIB_LISTENDROPS),
> tcp_ipv4.c: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
> tcp_ipv4.c: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
>
> raj@tardy:~/net-next/net/ipv4$ grep MIB_SYN *.c
> proc.c: SNMP_MIB_ITEM("SyncookiesSent", LINUX_MIB_SYNCOOKIESSENT),
> proc.c: SNMP_MIB_ITEM("SyncookiesRecv", LINUX_MIB_SYNCOOKIESRECV),
> proc.c: SNMP_MIB_ITEM("SyncookiesFailed", LINUX_MIB_SYNCOOKIESFAILED),
> syncookies.c: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT);
> syncookies.c: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED);
> syncookies.c: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESRECV);
>
> I will sometimes be tripped-up by netstat's not showing a statistic
> with a zero value...
This is what I'm talking about:
pc1 $ uname -a
Linux labs09 3.5.0-18-generic #29~precise1-Ubuntu SMP Mon Oct 22 16:31:46 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
pc1 $ netstat --version | head -n2
net-tools 1.60
netstat 1.42 (2001-04-15)
pc1 $ netstat -s | grep -i syn
4 invalid SYN cookies received
pc2 $ uname -a
Linux eu-21 3.0.0-19-server #33-Ubuntu SMP Thu Apr 19 20:32:48 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
pc2 $ netstat --version | head -n2
net-tools 1.60
netstat 1.42 (2001-04-15)
pc2 $ netstat -s | grep -i syn
1996450 SYN cookies sent
2899079 SYN cookies received
410573 invalid SYN cookies received
10012473 resets received for embryonic SYN_RECV sockets
5659740 SYNs to LISTEN sockets dropped
1 connections reset due to unexpected SYN
I didn't take a look at the kernel or netstat sources about this, so I
don't know exactly how are they connected.
> >>But yes, I would not expect a connect() call to remain incomplete
> >>for any longer than it took to receive an SYN|ACK from the other
> >>end.
> >
> >So the only reason to experience these high times spent in connect()
> >should be because a SYN or SYN|ACK was actually loss in a lower layer,
> >like an error in the network device or a transmission error?
>
> Modulo the/some other drop-without-stat point such as Vijay
> mentioned yesterday.
So, in this cases a syncookie is not sent back? I had the impression
they were sent always...
> You might consider taking some packet traces. If you can I would
> start with a trace taken on the system(s) on which the long
> connect() calls are happening. I think the tcpdump manpage has an
> example of a tcpdump command with a filter expression that catches
> just SYNchronize and FINished segments which I suppose you could
> extend to include ReSeT segments. Such a filter expression would be
> missing the client's ACK of the SYN|ACK but unless you see
> incrementing stats relating to say checksum failures or other drops
> on the "client" side I suppose you could assume that the client
> ACKed the server's SYN|ACK.
Yes, I already did captures and we are definitely loosing packets
(including SYNs), but it looks like the amount of SYNs I'm loosing is
lower than the amount of long connect() times I observe. This is not
confirmed yet, I'm still investigating.
Thanks!
--
Leandro Lucarella
sociomantic labs GmbH
http://www.sociomantic.com
^ permalink raw reply
* [patch net-next] ipv4: introduce address lifetime
From: Jiri Pirko @ 2013-01-24 19:41 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, stephen, kuznet, jmorris, yoshfuji, kaber,
psimerda, tgraf, nicolas.dichtel, nhorman, dcbw
There are some usecase when lifetime of ipv4 addresses might be helpful.
For example:
1) initramfs networkmanager uses a DHCP daemon to learn network
configuration parameters
2) initramfs networkmanager addresses, routes and DNS configuration
3) initramfs networkmanager is requested to stop
4) initramfs networkmanager stops all daemons including dhclient
5) there are addresses and routes configured but no daemon running. If
the system doesn't start networkmanager for some reason, addresses and
routes will be used forever, which violates RFC 2131.
This patch is essentially a backport of ivp6 address lifetime mechanism
for ipv4 addresses.
Current "ip" tool supports this without any patch (since it does not
distinguish between ipv4 and ipv6 addresses in this perspective.
Also, this should be back-compatible with all current netlink users.
Reported-by: Pavel Šimerda <psimerda@redhat.com>
Signed-off-by: Jiri Pirko <jiri@resnulli.us>
---
include/linux/inetdevice.h | 6 ++
include/net/addrconf.h | 4 +
net/ipv4/devinet.c | 215 +++++++++++++++++++++++++++++++++++++++++++--
net/ipv6/addrconf.c | 4 -
4 files changed, 220 insertions(+), 9 deletions(-)
diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h
index a9d8289..ea1e3b8 100644
--- a/include/linux/inetdevice.h
+++ b/include/linux/inetdevice.h
@@ -166,6 +166,12 @@ struct in_ifaddr {
unsigned char ifa_flags;
unsigned char ifa_prefixlen;
char ifa_label[IFNAMSIZ];
+
+ /* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */
+ __u32 ifa_valid_lft;
+ __u32 ifa_preferred_lft;
+ unsigned long ifa_cstamp; /* created timestamp */
+ unsigned long ifa_tstamp; /* updated timestamp */
};
extern int register_inetaddr_notifier(struct notifier_block *nb);
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 6c58d50..40be2a0 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -15,6 +15,10 @@
#define IPV6_MAX_ADDRESSES 16
+#define ADDRCONF_TIMER_FUZZ_MINUS (HZ > 50 ? HZ / 50 : 1)
+#define ADDRCONF_TIMER_FUZZ (HZ / 4)
+#define ADDRCONF_TIMER_FUZZ_MAX (HZ)
+
#include <linux/in.h>
#include <linux/in6.h>
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index a8e4f26..5281314 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -63,6 +63,7 @@
#include <net/ip_fib.h>
#include <net/rtnetlink.h>
#include <net/net_namespace.h>
+#include <net/addrconf.h>
#include "fib_lookup.h"
@@ -93,6 +94,7 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
[IFA_ADDRESS] = { .type = NLA_U32 },
[IFA_BROADCAST] = { .type = NLA_U32 },
[IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
+ [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) },
};
#define IN4_ADDR_HSIZE_SHIFT 8
@@ -417,6 +419,10 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
}
+static void check_lifetime(struct work_struct *work);
+
+static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
+
static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
u32 portid)
{
@@ -462,6 +468,9 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
inet_hash_insert(dev_net(in_dev->dev), ifa);
+ cancel_delayed_work(&check_lifetime_work);
+ schedule_delayed_work(&check_lifetime_work, 0);
+
/* Send message first, then call notifier.
Notifier will trigger FIB update, so that
listeners of netlink will know about new ifaddr */
@@ -573,7 +582,107 @@ errout:
return err;
}
-static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
+#define INFINITY_LIFE_TIME 0xFFFFFFFF
+
+static void check_lifetime(struct work_struct *work)
+{
+ unsigned long now, next, next_sec, next_sched;
+ struct in_ifaddr *ifa;
+ struct hlist_node *node;
+ int i;
+
+ now = jiffies;
+ next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
+
+ rcu_read_lock();
+ for (i = 0; i < IN4_ADDR_HSIZE; i++) {
+ hlist_for_each_entry_rcu(ifa, node,
+ &inet_addr_lst[i], hash) {
+ unsigned long age;
+
+ if (ifa->ifa_flags & IFA_F_PERMANENT)
+ continue;
+
+ /* We try to batch several events at once. */
+ age = (now - ifa->ifa_tstamp +
+ ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
+
+ if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
+ age >= ifa->ifa_valid_lft) {
+ struct in_ifaddr **ifap ;
+
+ rtnl_lock();
+ for (ifap = &ifa->ifa_dev->ifa_list;
+ *ifap != NULL; ifap = &ifa->ifa_next) {
+ if (*ifap == ifa)
+ inet_del_ifa(ifa->ifa_dev,
+ ifap, 1);
+ }
+ rtnl_unlock();
+ } else if (ifa->ifa_preferred_lft ==
+ INFINITY_LIFE_TIME) {
+ continue;
+ } else if (age >= ifa->ifa_preferred_lft) {
+ if (time_before(ifa->ifa_tstamp +
+ ifa->ifa_valid_lft * HZ, next))
+ next = ifa->ifa_tstamp +
+ ifa->ifa_valid_lft * HZ;
+
+ if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) {
+ ifa->ifa_flags |= IFA_F_DEPRECATED;
+ rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
+ }
+ } else if (time_before(ifa->ifa_tstamp +
+ ifa->ifa_preferred_lft * HZ,
+ next)) {
+ next = ifa->ifa_tstamp +
+ ifa->ifa_preferred_lft * HZ;
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ next_sec = round_jiffies_up(next);
+ next_sched = next;
+
+ /* If rounded timeout is accurate enough, accept it. */
+ if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
+ next_sched = next_sec;
+
+ now = jiffies;
+ /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
+ if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
+ next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
+
+ schedule_delayed_work(&check_lifetime_work, next_sched - now);
+}
+
+static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
+ __u32 prefered_lft)
+{
+ unsigned long timeout;
+
+ ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
+
+ timeout = addrconf_timeout_fixup(valid_lft, HZ);
+ if (addrconf_finite_timeout(timeout))
+ ifa->ifa_valid_lft = timeout;
+ else
+ ifa->ifa_flags |= IFA_F_PERMANENT;
+
+ timeout = addrconf_timeout_fixup(prefered_lft, HZ);
+ if (addrconf_finite_timeout(timeout)) {
+ if (timeout == 0)
+ ifa->ifa_flags |= IFA_F_DEPRECATED;
+ ifa->ifa_preferred_lft = timeout;
+ }
+ ifa->ifa_tstamp = jiffies;
+ if (!ifa->ifa_cstamp)
+ ifa->ifa_cstamp = ifa->ifa_tstamp;
+}
+
+static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
+ __u32 *pvalid_lft, __u32 *pprefered_lft)
{
struct nlattr *tb[IFA_MAX+1];
struct in_ifaddr *ifa;
@@ -633,24 +742,73 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
else
memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+ if (tb[IFA_CACHEINFO]) {
+ struct ifa_cacheinfo *ci;
+
+ ci = nla_data(tb[IFA_CACHEINFO]);
+ if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
+ err = -EINVAL;
+ goto errout;
+ }
+ *pvalid_lft = ci->ifa_valid;
+ *pprefered_lft = ci->ifa_prefered;
+ }
+
return ifa;
errout:
return ERR_PTR(err);
}
+static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
+{
+ struct in_device *in_dev = ifa->ifa_dev;
+ struct in_ifaddr *ifa1, **ifap;
+
+ if (!ifa->ifa_local)
+ return NULL;
+
+ for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
+ ifap = &ifa1->ifa_next) {
+ if (ifa1->ifa_mask == ifa->ifa_mask &&
+ inet_ifa_match(ifa1->ifa_address, ifa) &&
+ ifa1->ifa_local == ifa->ifa_local)
+ return ifa1;
+ }
+ return NULL;
+}
+
static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
struct in_ifaddr *ifa;
+ struct in_ifaddr *ifa_existing;
+ __u32 valid_lft = INFINITY_LIFE_TIME;
+ __u32 prefered_lft = INFINITY_LIFE_TIME;
ASSERT_RTNL();
- ifa = rtm_to_ifaddr(net, nlh);
+ ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft);
if (IS_ERR(ifa))
return PTR_ERR(ifa);
- return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
+ ifa_existing = find_matching_ifa(ifa);
+ if (!ifa_existing) {
+ /* It would be best to check for !NLM_F_CREATE here but
+ * userspace alreay relies on not having to provide this.
+ */
+ set_ifa_lifetime(ifa, valid_lft, prefered_lft);
+ return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
+ } else {
+ inet_free_ifa(ifa);
+
+ if (nlh->nlmsg_flags & NLM_F_EXCL ||
+ !(nlh->nlmsg_flags & NLM_F_REPLACE))
+ return -EEXIST;
+
+ set_ifa_lifetime(ifa_existing, valid_lft, prefered_lft);
+ }
+ return 0;
}
/*
@@ -852,6 +1010,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
ifa->ifa_prefixlen = 32;
ifa->ifa_mask = inet_make_mask(32);
}
+ set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
ret = inet_set_ifa(dev, ifa);
break;
@@ -1190,6 +1349,8 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
ifa->ifa_dev = in_dev;
ifa->ifa_scope = RT_SCOPE_HOST;
memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+ set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
+ INFINITY_LIFE_TIME);
inet_insert_ifa(ifa);
}
}
@@ -1246,11 +1407,30 @@ static size_t inet_nlmsg_size(void)
+ nla_total_size(IFNAMSIZ); /* IFA_LABEL */
}
+static inline u32 cstamp_delta(unsigned long cstamp)
+{
+ return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
+}
+
+static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
+ unsigned long tstamp, u32 preferred, u32 valid)
+{
+ struct ifa_cacheinfo ci;
+
+ ci.cstamp = cstamp_delta(cstamp);
+ ci.tstamp = cstamp_delta(tstamp);
+ ci.ifa_prefered = preferred;
+ ci.ifa_valid = valid;
+
+ return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
+}
+
static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
u32 portid, u32 seq, int event, unsigned int flags)
{
struct ifaddrmsg *ifm;
struct nlmsghdr *nlh;
+ u32 preferred, valid;
nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
if (nlh == NULL)
@@ -1259,10 +1439,31 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
ifm = nlmsg_data(nlh);
ifm->ifa_family = AF_INET;
ifm->ifa_prefixlen = ifa->ifa_prefixlen;
- ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
+ ifm->ifa_flags = ifa->ifa_flags;
ifm->ifa_scope = ifa->ifa_scope;
ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
+ if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
+ preferred = ifa->ifa_preferred_lft;
+ valid = ifa->ifa_valid_lft;
+ if (preferred != INFINITY_LIFE_TIME) {
+ long tval = (jiffies - ifa->ifa_tstamp) / HZ;
+
+ if (preferred > tval)
+ preferred -= tval;
+ else
+ preferred = 0;
+ if (valid != INFINITY_LIFE_TIME) {
+ if (valid > tval)
+ valid -= tval;
+ else
+ valid = 0;
+ }
+ }
+ } else {
+ preferred = INFINITY_LIFE_TIME;
+ valid = INFINITY_LIFE_TIME;
+ }
if ((ifa->ifa_address &&
nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) ||
(ifa->ifa_local &&
@@ -1270,7 +1471,9 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
(ifa->ifa_broadcast &&
nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
(ifa->ifa_label[0] &&
- nla_put_string(skb, IFA_LABEL, ifa->ifa_label)))
+ nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
+ put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
+ preferred, valid))
goto nla_put_failure;
return nlmsg_end(skb, nlh);
@@ -1988,6 +2191,8 @@ void __init devinet_init(void)
register_gifconf(PF_INET, inet_gifconf);
register_netdevice_notifier(&ip_netdev_notifier);
+ schedule_delayed_work(&check_lifetime_work, 0);
+
rtnl_af_register(&inet_af_ops);
rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 80d5980..7f7332b 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -110,10 +110,6 @@ static inline u32 cstamp_delta(unsigned long cstamp)
return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
}
-#define ADDRCONF_TIMER_FUZZ_MINUS (HZ > 50 ? HZ/50 : 1)
-#define ADDRCONF_TIMER_FUZZ (HZ / 4)
-#define ADDRCONF_TIMER_FUZZ_MAX (HZ)
-
#ifdef CONFIG_SYSCTL
static void addrconf_sysctl_register(struct inet6_dev *idev);
static void addrconf_sysctl_unregister(struct inet6_dev *idev);
--
1.8.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox