2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2007-12-31 07:18:29 +00:00
|
|
|
* Ethernet netdevice using ATM AAL5 as underlying carrier
|
|
|
|
* (RFC1483 obsoleted by RFC2684) for Linux
|
|
|
|
*
|
|
|
|
* Authors: Marcell GAL, 2000, XDSL Ltd, Hungary
|
|
|
|
* Eric Kinzie, 2006-2007, US Naval Research Laboratory
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-01-26 11:40:00 +00:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <linux/ip.h>
|
2010-01-26 11:40:04 +00:00
|
|
|
#include <linux/uaccess.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
|
|
|
#include <linux/slab.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <net/arp.h>
|
|
|
|
#include <linux/atm.h>
|
|
|
|
#include <linux/atmdev.h>
|
2006-01-11 20:17:47 +00:00
|
|
|
#include <linux/capability.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/seq_file.h>
|
|
|
|
|
|
|
|
#include <linux/atmbr2684.h>
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
|
|
|
|
static void skb_debug(const struct sk_buff *skb)
|
|
|
|
{
|
2010-01-26 11:40:04 +00:00
|
|
|
#ifdef SKB_DEBUG
|
2005-04-16 22:20:36 +00:00
|
|
|
#define NUM2PRINT 50
|
2010-01-26 11:40:04 +00:00
|
|
|
print_hex_dump(KERN_DEBUG, "br2684: skb: ", DUMP_OFFSET,
|
|
|
|
16, 1, skb->data, min(NUM2PRINT, skb->len), true);
|
2005-04-16 22:20:36 +00:00
|
|
|
#endif
|
2010-01-26 11:40:04 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-12-31 07:17:53 +00:00
|
|
|
#define BR2684_ETHERTYPE_LEN 2
|
|
|
|
#define BR2684_PAD_LEN 2
|
|
|
|
|
|
|
|
#define LLC 0xaa, 0xaa, 0x03
|
|
|
|
#define SNAP_BRIDGED 0x00, 0x80, 0xc2
|
|
|
|
#define SNAP_ROUTED 0x00, 0x00, 0x00
|
|
|
|
#define PID_ETHERNET 0x00, 0x07
|
|
|
|
#define ETHERTYPE_IPV4 0x08, 0x00
|
|
|
|
#define ETHERTYPE_IPV6 0x86, 0xdd
|
|
|
|
#define PAD_BRIDGED 0x00, 0x00
|
|
|
|
|
2008-06-17 23:20:06 +00:00
|
|
|
static const unsigned char ethertype_ipv4[] = { ETHERTYPE_IPV4 };
|
|
|
|
static const unsigned char ethertype_ipv6[] = { ETHERTYPE_IPV6 };
|
|
|
|
static const unsigned char llc_oui_pid_pad[] =
|
2007-12-31 07:18:29 +00:00
|
|
|
{ LLC, SNAP_BRIDGED, PID_ETHERNET, PAD_BRIDGED };
|
2011-08-17 06:37:52 +00:00
|
|
|
static const unsigned char pad[] = { PAD_BRIDGED };
|
2008-06-17 23:20:06 +00:00
|
|
|
static const unsigned char llc_oui_ipv4[] = { LLC, SNAP_ROUTED, ETHERTYPE_IPV4 };
|
|
|
|
static const unsigned char llc_oui_ipv6[] = { LLC, SNAP_ROUTED, ETHERTYPE_IPV6 };
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
enum br2684_encaps {
|
2007-12-31 07:18:29 +00:00
|
|
|
e_vc = BR2684_ENCAPS_VC,
|
2005-04-16 22:20:36 +00:00
|
|
|
e_llc = BR2684_ENCAPS_LLC,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct br2684_vcc {
|
2007-12-31 07:18:29 +00:00
|
|
|
struct atm_vcc *atmvcc;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct net_device *device;
|
2007-12-31 07:18:29 +00:00
|
|
|
/* keep old push, pop functions for chaining */
|
2010-01-26 11:40:04 +00:00
|
|
|
void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb);
|
2009-09-03 06:26:39 +00:00
|
|
|
void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb);
|
2012-11-27 23:28:36 +00:00
|
|
|
void (*old_release_cb)(struct atm_vcc *vcc);
|
2012-11-28 00:46:45 +00:00
|
|
|
struct module *old_owner;
|
2005-04-16 22:20:36 +00:00
|
|
|
enum br2684_encaps encaps;
|
|
|
|
struct list_head brvccs;
|
|
|
|
#ifdef CONFIG_ATM_BR2684_IPFILTER
|
|
|
|
struct br2684_filter filter;
|
|
|
|
#endif /* CONFIG_ATM_BR2684_IPFILTER */
|
2012-04-15 05:58:06 +00:00
|
|
|
unsigned int copies_needed, copies_failed;
|
atm: br2684: Fix excessive queue bloat
There's really no excuse for an additional wmem_default of buffering
between the netdev queue and the ATM device. Two packets (one in-flight,
and one ready to send) ought to be fine. It's not as if it should take
long to get another from the netdev queue when we need it.
If necessary we can make the queue space configurable later, but I don't
think it's likely to be necessary.
cf. commit 9d02daf754238adac48fa075ee79e7edd3d79ed3 (pppoatm: Fix
excessive queue bloat) which did something very similar for PPPoATM.
Note that there is a tremendously unlikely race condition which may
result in qspace temporarily going negative. If a CPU running the
br2684_pop() function goes off into the weeds for a long period of time
after incrementing qspace to 1, but before calling netdev_wake_queue()...
and another CPU ends up calling br2684_start_xmit() and *stopping* the
queue again before the first CPU comes back, the netdev queue could
end up being woken when qspace has already reached zero.
An alternative approach to coping with this race would be to check in
br2684_start_xmit() for qspace==0 and return NETDEV_TX_BUSY, but just
using '> 0' and '< 1' for comparison instead of '== 0' and '!= 0' is
simpler. It just warranted a mention of *why* we do it that way...
Move the call to atmvcc->send() to happen *after* the accounting and
potentially stopping the netdev queue, in br2684_xmit_vcc(). This matters
if the ->send() call suffers an immediate failure, because it'll call
br2684_pop() with the offending skb before returning. We want that to
happen *after* we've done the initial accounting for the packet in
question. Also make it return an appropriate success/failure indication
while we're at it.
Tested by running 'ping -l 1000 bottomless.aaisp.net.uk' from within my
network, with only a single PPPoE-over-BR2684 link running. And after
setting txqueuelen on the nas0 interface to something low (5, in fact).
Before the patch, we'd see about 15 packets being queued and a resulting
latency of ~56ms being reached. After the patch, we see only about 8,
which is fairly much what we expect. And a max latency of ~36ms. On this
OpenWRT box, wmem_default is 163840.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Reviewed-by: Krzysztof Mazur <krzysiek@podlesie.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-11-25 12:06:52 +00:00
|
|
|
atomic_t qspace;
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct br2684_dev {
|
|
|
|
struct net_device *net_dev;
|
|
|
|
struct list_head br2684_devs;
|
|
|
|
int number;
|
2007-12-31 07:18:29 +00:00
|
|
|
struct list_head brvccs; /* one device <=> one vcc (before xmas) */
|
2005-04-16 22:20:36 +00:00
|
|
|
int mac_was_set;
|
2007-12-31 07:17:53 +00:00
|
|
|
enum br2684_payload payload;
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This lock should be held for writing any time the list of devices or
|
|
|
|
* their attached vcc's could be altered. It should be held for reading
|
|
|
|
* any time these are being queried. Note that we sometimes need to
|
|
|
|
* do read-locking under interrupt context, so write locking must block
|
|
|
|
* the current CPU's interrupts
|
|
|
|
*/
|
|
|
|
static DEFINE_RWLOCK(devs_lock);
|
|
|
|
|
|
|
|
static LIST_HEAD(br2684_devs);
|
|
|
|
|
|
|
|
static inline struct br2684_dev *BRPRIV(const struct net_device *net_dev)
|
|
|
|
{
|
2010-11-15 11:12:33 +00:00
|
|
|
return netdev_priv(net_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct net_device *list_entry_brdev(const struct list_head *le)
|
|
|
|
{
|
|
|
|
return list_entry(le, struct br2684_dev, br2684_devs)->net_dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct br2684_vcc *BR2684_VCC(const struct atm_vcc *atmvcc)
|
|
|
|
{
|
2007-12-31 07:18:29 +00:00
|
|
|
return (struct br2684_vcc *)(atmvcc->user_back);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct br2684_vcc *list_entry_brvcc(const struct list_head *le)
|
|
|
|
{
|
|
|
|
return list_entry(le, struct br2684_vcc, brvccs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Caller should hold read_lock(&devs_lock) */
|
|
|
|
static struct net_device *br2684_find_dev(const struct br2684_if_spec *s)
|
|
|
|
{
|
|
|
|
struct list_head *lh;
|
|
|
|
struct net_device *net_dev;
|
|
|
|
switch (s->method) {
|
|
|
|
case BR2684_FIND_BYNUM:
|
|
|
|
list_for_each(lh, &br2684_devs) {
|
|
|
|
net_dev = list_entry_brdev(lh);
|
|
|
|
if (BRPRIV(net_dev)->number == s->spec.devnum)
|
|
|
|
return net_dev;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BR2684_FIND_BYIFNAME:
|
|
|
|
list_for_each(lh, &br2684_devs) {
|
|
|
|
net_dev = list_entry_brdev(lh);
|
|
|
|
if (!strncmp(net_dev->name, s->spec.ifname, IFNAMSIZ))
|
|
|
|
return net_dev;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-07-08 20:55:31 +00:00
|
|
|
static int atm_dev_event(struct notifier_block *this, unsigned long event,
|
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
struct atm_dev *atm_dev = arg;
|
|
|
|
struct list_head *lh;
|
|
|
|
struct net_device *net_dev;
|
|
|
|
struct br2684_vcc *brvcc;
|
|
|
|
struct atm_vcc *atm_vcc;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
pr_debug("event=%ld dev=%p\n", event, atm_dev);
|
|
|
|
|
|
|
|
read_lock_irqsave(&devs_lock, flags);
|
|
|
|
list_for_each(lh, &br2684_devs) {
|
|
|
|
net_dev = list_entry_brdev(lh);
|
|
|
|
|
|
|
|
list_for_each_entry(brvcc, &BRPRIV(net_dev)->brvccs, brvccs) {
|
|
|
|
atm_vcc = brvcc->atmvcc;
|
|
|
|
if (atm_vcc && brvcc->atmvcc->dev == atm_dev) {
|
|
|
|
|
|
|
|
if (atm_vcc->dev->signal == ATM_PHY_SIG_LOST)
|
|
|
|
netif_carrier_off(net_dev);
|
|
|
|
else
|
|
|
|
netif_carrier_on(net_dev);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
read_unlock_irqrestore(&devs_lock, flags);
|
|
|
|
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block atm_dev_notifier = {
|
|
|
|
.notifier_call = atm_dev_event,
|
|
|
|
};
|
|
|
|
|
2009-09-03 06:26:39 +00:00
|
|
|
/* chained vcc->pop function. Check if we should wake the netif_queue */
|
|
|
|
static void br2684_pop(struct atm_vcc *vcc, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct br2684_vcc *brvcc = BR2684_VCC(vcc);
|
|
|
|
|
atm: br2684: Fix excessive queue bloat
There's really no excuse for an additional wmem_default of buffering
between the netdev queue and the ATM device. Two packets (one in-flight,
and one ready to send) ought to be fine. It's not as if it should take
long to get another from the netdev queue when we need it.
If necessary we can make the queue space configurable later, but I don't
think it's likely to be necessary.
cf. commit 9d02daf754238adac48fa075ee79e7edd3d79ed3 (pppoatm: Fix
excessive queue bloat) which did something very similar for PPPoATM.
Note that there is a tremendously unlikely race condition which may
result in qspace temporarily going negative. If a CPU running the
br2684_pop() function goes off into the weeds for a long period of time
after incrementing qspace to 1, but before calling netdev_wake_queue()...
and another CPU ends up calling br2684_start_xmit() and *stopping* the
queue again before the first CPU comes back, the netdev queue could
end up being woken when qspace has already reached zero.
An alternative approach to coping with this race would be to check in
br2684_start_xmit() for qspace==0 and return NETDEV_TX_BUSY, but just
using '> 0' and '< 1' for comparison instead of '== 0' and '!= 0' is
simpler. It just warranted a mention of *why* we do it that way...
Move the call to atmvcc->send() to happen *after* the accounting and
potentially stopping the netdev queue, in br2684_xmit_vcc(). This matters
if the ->send() call suffers an immediate failure, because it'll call
br2684_pop() with the offending skb before returning. We want that to
happen *after* we've done the initial accounting for the packet in
question. Also make it return an appropriate success/failure indication
while we're at it.
Tested by running 'ping -l 1000 bottomless.aaisp.net.uk' from within my
network, with only a single PPPoE-over-BR2684 link running. And after
setting txqueuelen on the nas0 interface to something low (5, in fact).
Before the patch, we'd see about 15 packets being queued and a resulting
latency of ~56ms being reached. After the patch, we see only about 8,
which is fairly much what we expect. And a max latency of ~36ms. On this
OpenWRT box, wmem_default is 163840.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Reviewed-by: Krzysztof Mazur <krzysiek@podlesie.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-11-25 12:06:52 +00:00
|
|
|
pr_debug("(vcc %p ; net_dev %p )\n", vcc, brvcc->device);
|
2009-09-03 06:26:39 +00:00
|
|
|
brvcc->old_pop(vcc, skb);
|
|
|
|
|
atm: br2684: Fix excessive queue bloat
There's really no excuse for an additional wmem_default of buffering
between the netdev queue and the ATM device. Two packets (one in-flight,
and one ready to send) ought to be fine. It's not as if it should take
long to get another from the netdev queue when we need it.
If necessary we can make the queue space configurable later, but I don't
think it's likely to be necessary.
cf. commit 9d02daf754238adac48fa075ee79e7edd3d79ed3 (pppoatm: Fix
excessive queue bloat) which did something very similar for PPPoATM.
Note that there is a tremendously unlikely race condition which may
result in qspace temporarily going negative. If a CPU running the
br2684_pop() function goes off into the weeds for a long period of time
after incrementing qspace to 1, but before calling netdev_wake_queue()...
and another CPU ends up calling br2684_start_xmit() and *stopping* the
queue again before the first CPU comes back, the netdev queue could
end up being woken when qspace has already reached zero.
An alternative approach to coping with this race would be to check in
br2684_start_xmit() for qspace==0 and return NETDEV_TX_BUSY, but just
using '> 0' and '< 1' for comparison instead of '== 0' and '!= 0' is
simpler. It just warranted a mention of *why* we do it that way...
Move the call to atmvcc->send() to happen *after* the accounting and
potentially stopping the netdev queue, in br2684_xmit_vcc(). This matters
if the ->send() call suffers an immediate failure, because it'll call
br2684_pop() with the offending skb before returning. We want that to
happen *after* we've done the initial accounting for the packet in
question. Also make it return an appropriate success/failure indication
while we're at it.
Tested by running 'ping -l 1000 bottomless.aaisp.net.uk' from within my
network, with only a single PPPoE-over-BR2684 link running. And after
setting txqueuelen on the nas0 interface to something low (5, in fact).
Before the patch, we'd see about 15 packets being queued and a resulting
latency of ~56ms being reached. After the patch, we see only about 8,
which is fairly much what we expect. And a max latency of ~36ms. On this
OpenWRT box, wmem_default is 163840.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Reviewed-by: Krzysztof Mazur <krzysiek@podlesie.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-11-25 12:06:52 +00:00
|
|
|
/* If the queue space just went up from zero, wake */
|
|
|
|
if (atomic_inc_return(&brvcc->qspace) == 1)
|
|
|
|
netif_wake_queue(brvcc->device);
|
2009-09-03 06:26:39 +00:00
|
|
|
}
|
atm: br2684: Fix excessive queue bloat
There's really no excuse for an additional wmem_default of buffering
between the netdev queue and the ATM device. Two packets (one in-flight,
and one ready to send) ought to be fine. It's not as if it should take
long to get another from the netdev queue when we need it.
If necessary we can make the queue space configurable later, but I don't
think it's likely to be necessary.
cf. commit 9d02daf754238adac48fa075ee79e7edd3d79ed3 (pppoatm: Fix
excessive queue bloat) which did something very similar for PPPoATM.
Note that there is a tremendously unlikely race condition which may
result in qspace temporarily going negative. If a CPU running the
br2684_pop() function goes off into the weeds for a long period of time
after incrementing qspace to 1, but before calling netdev_wake_queue()...
and another CPU ends up calling br2684_start_xmit() and *stopping* the
queue again before the first CPU comes back, the netdev queue could
end up being woken when qspace has already reached zero.
An alternative approach to coping with this race would be to check in
br2684_start_xmit() for qspace==0 and return NETDEV_TX_BUSY, but just
using '> 0' and '< 1' for comparison instead of '== 0' and '!= 0' is
simpler. It just warranted a mention of *why* we do it that way...
Move the call to atmvcc->send() to happen *after* the accounting and
potentially stopping the netdev queue, in br2684_xmit_vcc(). This matters
if the ->send() call suffers an immediate failure, because it'll call
br2684_pop() with the offending skb before returning. We want that to
happen *after* we've done the initial accounting for the packet in
question. Also make it return an appropriate success/failure indication
while we're at it.
Tested by running 'ping -l 1000 bottomless.aaisp.net.uk' from within my
network, with only a single PPPoE-over-BR2684 link running. And after
setting txqueuelen on the nas0 interface to something low (5, in fact).
Before the patch, we'd see about 15 packets being queued and a resulting
latency of ~56ms being reached. After the patch, we see only about 8,
which is fairly much what we expect. And a max latency of ~36ms. On this
OpenWRT box, wmem_default is 163840.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Reviewed-by: Krzysztof Mazur <krzysiek@podlesie.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-11-25 12:06:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Send a packet out a particular vcc. Not to useful right now, but paves
|
|
|
|
* the way for multiple vcc's per itf. Returns true if we can send,
|
|
|
|
* otherwise false
|
|
|
|
*/
|
2009-01-09 13:00:58 +00:00
|
|
|
static int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev,
|
2007-12-31 07:18:29 +00:00
|
|
|
struct br2684_vcc *brvcc)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2009-01-09 13:00:58 +00:00
|
|
|
struct br2684_dev *brdev = BRPRIV(dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct atm_vcc *atmvcc;
|
2011-08-17 06:37:18 +00:00
|
|
|
int minheadroom = (brvcc->encaps == e_llc) ?
|
|
|
|
((brdev->payload == p_bridged) ?
|
|
|
|
sizeof(llc_oui_pid_pad) : sizeof(llc_oui_ipv4)) :
|
|
|
|
((brdev->payload == p_bridged) ? BR2684_PAD_LEN : 0);
|
2007-12-31 07:17:53 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (skb_headroom(skb) < minheadroom) {
|
|
|
|
struct sk_buff *skb2 = skb_realloc_headroom(skb, minheadroom);
|
|
|
|
brvcc->copies_needed++;
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
if (skb2 == NULL) {
|
|
|
|
brvcc->copies_failed++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
skb = skb2;
|
|
|
|
}
|
2007-12-31 07:17:53 +00:00
|
|
|
|
|
|
|
if (brvcc->encaps == e_llc) {
|
|
|
|
if (brdev->payload == p_bridged) {
|
|
|
|
skb_push(skb, sizeof(llc_oui_pid_pad));
|
2007-12-31 07:18:29 +00:00
|
|
|
skb_copy_to_linear_data(skb, llc_oui_pid_pad,
|
|
|
|
sizeof(llc_oui_pid_pad));
|
2007-12-31 07:17:53 +00:00
|
|
|
} else if (brdev->payload == p_routed) {
|
|
|
|
unsigned short prot = ntohs(skb->protocol);
|
|
|
|
|
|
|
|
skb_push(skb, sizeof(llc_oui_ipv4));
|
|
|
|
switch (prot) {
|
2007-12-31 07:18:29 +00:00
|
|
|
case ETH_P_IP:
|
|
|
|
skb_copy_to_linear_data(skb, llc_oui_ipv4,
|
|
|
|
sizeof(llc_oui_ipv4));
|
|
|
|
break;
|
|
|
|
case ETH_P_IPV6:
|
|
|
|
skb_copy_to_linear_data(skb, llc_oui_ipv6,
|
|
|
|
sizeof(llc_oui_ipv6));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return 0;
|
2007-12-31 07:17:53 +00:00
|
|
|
}
|
|
|
|
}
|
2008-06-17 00:18:18 +00:00
|
|
|
} else { /* e_vc */
|
|
|
|
if (brdev->payload == p_bridged) {
|
|
|
|
skb_push(skb, 2);
|
2007-12-31 07:17:53 +00:00
|
|
|
memset(skb->data, 0, 2);
|
2008-06-17 00:18:18 +00:00
|
|
|
}
|
2007-12-31 07:17:53 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
skb_debug(skb);
|
|
|
|
|
|
|
|
ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc;
|
2007-08-28 22:22:09 +00:00
|
|
|
pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, atmvcc, atmvcc->dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
atomic_add(skb->truesize, &sk_atm(atmvcc)->sk_wmem_alloc);
|
|
|
|
ATM_SKB(skb)->atm_options = atmvcc->atm_options;
|
2009-01-09 13:00:58 +00:00
|
|
|
dev->stats.tx_packets++;
|
|
|
|
dev->stats.tx_bytes += skb->len;
|
2009-09-03 06:26:39 +00:00
|
|
|
|
atm: br2684: Fix excessive queue bloat
There's really no excuse for an additional wmem_default of buffering
between the netdev queue and the ATM device. Two packets (one in-flight,
and one ready to send) ought to be fine. It's not as if it should take
long to get another from the netdev queue when we need it.
If necessary we can make the queue space configurable later, but I don't
think it's likely to be necessary.
cf. commit 9d02daf754238adac48fa075ee79e7edd3d79ed3 (pppoatm: Fix
excessive queue bloat) which did something very similar for PPPoATM.
Note that there is a tremendously unlikely race condition which may
result in qspace temporarily going negative. If a CPU running the
br2684_pop() function goes off into the weeds for a long period of time
after incrementing qspace to 1, but before calling netdev_wake_queue()...
and another CPU ends up calling br2684_start_xmit() and *stopping* the
queue again before the first CPU comes back, the netdev queue could
end up being woken when qspace has already reached zero.
An alternative approach to coping with this race would be to check in
br2684_start_xmit() for qspace==0 and return NETDEV_TX_BUSY, but just
using '> 0' and '< 1' for comparison instead of '== 0' and '!= 0' is
simpler. It just warranted a mention of *why* we do it that way...
Move the call to atmvcc->send() to happen *after* the accounting and
potentially stopping the netdev queue, in br2684_xmit_vcc(). This matters
if the ->send() call suffers an immediate failure, because it'll call
br2684_pop() with the offending skb before returning. We want that to
happen *after* we've done the initial accounting for the packet in
question. Also make it return an appropriate success/failure indication
while we're at it.
Tested by running 'ping -l 1000 bottomless.aaisp.net.uk' from within my
network, with only a single PPPoE-over-BR2684 link running. And after
setting txqueuelen on the nas0 interface to something low (5, in fact).
Before the patch, we'd see about 15 packets being queued and a resulting
latency of ~56ms being reached. After the patch, we see only about 8,
which is fairly much what we expect. And a max latency of ~36ms. On this
OpenWRT box, wmem_default is 163840.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Reviewed-by: Krzysztof Mazur <krzysiek@podlesie.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-11-25 12:06:52 +00:00
|
|
|
if (atomic_dec_return(&brvcc->qspace) < 1) {
|
|
|
|
/* No more please! */
|
2009-09-03 06:26:39 +00:00
|
|
|
netif_stop_queue(brvcc->device);
|
atm: br2684: Fix excessive queue bloat
There's really no excuse for an additional wmem_default of buffering
between the netdev queue and the ATM device. Two packets (one in-flight,
and one ready to send) ought to be fine. It's not as if it should take
long to get another from the netdev queue when we need it.
If necessary we can make the queue space configurable later, but I don't
think it's likely to be necessary.
cf. commit 9d02daf754238adac48fa075ee79e7edd3d79ed3 (pppoatm: Fix
excessive queue bloat) which did something very similar for PPPoATM.
Note that there is a tremendously unlikely race condition which may
result in qspace temporarily going negative. If a CPU running the
br2684_pop() function goes off into the weeds for a long period of time
after incrementing qspace to 1, but before calling netdev_wake_queue()...
and another CPU ends up calling br2684_start_xmit() and *stopping* the
queue again before the first CPU comes back, the netdev queue could
end up being woken when qspace has already reached zero.
An alternative approach to coping with this race would be to check in
br2684_start_xmit() for qspace==0 and return NETDEV_TX_BUSY, but just
using '> 0' and '< 1' for comparison instead of '== 0' and '!= 0' is
simpler. It just warranted a mention of *why* we do it that way...
Move the call to atmvcc->send() to happen *after* the accounting and
potentially stopping the netdev queue, in br2684_xmit_vcc(). This matters
if the ->send() call suffers an immediate failure, because it'll call
br2684_pop() with the offending skb before returning. We want that to
happen *after* we've done the initial accounting for the packet in
question. Also make it return an appropriate success/failure indication
while we're at it.
Tested by running 'ping -l 1000 bottomless.aaisp.net.uk' from within my
network, with only a single PPPoE-over-BR2684 link running. And after
setting txqueuelen on the nas0 interface to something low (5, in fact).
Before the patch, we'd see about 15 packets being queued and a resulting
latency of ~56ms being reached. After the patch, we see only about 8,
which is fairly much what we expect. And a max latency of ~36ms. On this
OpenWRT box, wmem_default is 163840.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Reviewed-by: Krzysztof Mazur <krzysiek@podlesie.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-11-25 12:06:52 +00:00
|
|
|
/* We might have raced with br2684_pop() */
|
|
|
|
if (unlikely(atomic_read(&brvcc->qspace) > 0))
|
|
|
|
netif_wake_queue(brvcc->device);
|
2009-09-03 06:26:39 +00:00
|
|
|
}
|
|
|
|
|
atm: br2684: Fix excessive queue bloat
There's really no excuse for an additional wmem_default of buffering
between the netdev queue and the ATM device. Two packets (one in-flight,
and one ready to send) ought to be fine. It's not as if it should take
long to get another from the netdev queue when we need it.
If necessary we can make the queue space configurable later, but I don't
think it's likely to be necessary.
cf. commit 9d02daf754238adac48fa075ee79e7edd3d79ed3 (pppoatm: Fix
excessive queue bloat) which did something very similar for PPPoATM.
Note that there is a tremendously unlikely race condition which may
result in qspace temporarily going negative. If a CPU running the
br2684_pop() function goes off into the weeds for a long period of time
after incrementing qspace to 1, but before calling netdev_wake_queue()...
and another CPU ends up calling br2684_start_xmit() and *stopping* the
queue again before the first CPU comes back, the netdev queue could
end up being woken when qspace has already reached zero.
An alternative approach to coping with this race would be to check in
br2684_start_xmit() for qspace==0 and return NETDEV_TX_BUSY, but just
using '> 0' and '< 1' for comparison instead of '== 0' and '!= 0' is
simpler. It just warranted a mention of *why* we do it that way...
Move the call to atmvcc->send() to happen *after* the accounting and
potentially stopping the netdev queue, in br2684_xmit_vcc(). This matters
if the ->send() call suffers an immediate failure, because it'll call
br2684_pop() with the offending skb before returning. We want that to
happen *after* we've done the initial accounting for the packet in
question. Also make it return an appropriate success/failure indication
while we're at it.
Tested by running 'ping -l 1000 bottomless.aaisp.net.uk' from within my
network, with only a single PPPoE-over-BR2684 link running. And after
setting txqueuelen on the nas0 interface to something low (5, in fact).
Before the patch, we'd see about 15 packets being queued and a resulting
latency of ~56ms being reached. After the patch, we see only about 8,
which is fairly much what we expect. And a max latency of ~36ms. On this
OpenWRT box, wmem_default is 163840.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Reviewed-by: Krzysztof Mazur <krzysiek@podlesie.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-11-25 12:06:52 +00:00
|
|
|
/* If this fails immediately, the skb will be freed and br2684_pop()
|
|
|
|
will wake the queue if appropriate. Just return an error so that
|
|
|
|
the stats are updated correctly */
|
|
|
|
return !atmvcc->send(atmvcc, skb);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2012-11-27 23:28:36 +00:00
|
|
|
static void br2684_release_cb(struct atm_vcc *atmvcc)
|
|
|
|
{
|
|
|
|
struct br2684_vcc *brvcc = BR2684_VCC(atmvcc);
|
|
|
|
|
|
|
|
if (atomic_read(&brvcc->qspace) > 0)
|
|
|
|
netif_wake_queue(brvcc->device);
|
|
|
|
|
|
|
|
if (brvcc->old_release_cb)
|
|
|
|
brvcc->old_release_cb(atmvcc);
|
|
|
|
}
|
|
|
|
|
2008-06-17 23:20:06 +00:00
|
|
|
static inline struct br2684_vcc *pick_outgoing_vcc(const struct sk_buff *skb,
|
|
|
|
const struct br2684_dev *brdev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-12-31 07:18:29 +00:00
|
|
|
return list_empty(&brdev->brvccs) ? NULL : list_entry_brvcc(brdev->brvccs.next); /* 1 vcc/dev right now */
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2009-08-31 19:50:42 +00:00
|
|
|
static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,
|
|
|
|
struct net_device *dev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct br2684_dev *brdev = BRPRIV(dev);
|
|
|
|
struct br2684_vcc *brvcc;
|
2012-11-27 23:28:36 +00:00
|
|
|
struct atm_vcc *atmvcc;
|
|
|
|
netdev_tx_t ret = NETDEV_TX_OK;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-01-26 11:40:00 +00:00
|
|
|
pr_debug("skb_dst(skb)=%p\n", skb_dst(skb));
|
2005-04-16 22:20:36 +00:00
|
|
|
read_lock(&devs_lock);
|
|
|
|
brvcc = pick_outgoing_vcc(skb, brdev);
|
|
|
|
if (brvcc == NULL) {
|
2007-08-28 22:22:09 +00:00
|
|
|
pr_debug("no vcc attached to dev %s\n", dev->name);
|
2009-01-09 13:00:58 +00:00
|
|
|
dev->stats.tx_errors++;
|
|
|
|
dev->stats.tx_carrier_errors++;
|
2005-04-16 22:20:36 +00:00
|
|
|
/* netif_stop_queue(dev); */
|
|
|
|
dev_kfree_skb(skb);
|
2012-11-27 23:28:36 +00:00
|
|
|
goto out_devs;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2012-11-27 23:28:36 +00:00
|
|
|
atmvcc = brvcc->atmvcc;
|
|
|
|
|
|
|
|
bh_lock_sock(sk_atm(atmvcc));
|
|
|
|
|
|
|
|
if (test_bit(ATM_VF_RELEASED, &atmvcc->flags) ||
|
|
|
|
test_bit(ATM_VF_CLOSE, &atmvcc->flags) ||
|
|
|
|
!test_bit(ATM_VF_READY, &atmvcc->flags)) {
|
|
|
|
dev->stats.tx_dropped++;
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sock_owned_by_user(sk_atm(atmvcc))) {
|
|
|
|
netif_stop_queue(brvcc->device);
|
|
|
|
ret = NETDEV_TX_BUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-01-09 13:00:58 +00:00
|
|
|
if (!br2684_xmit_vcc(skb, dev, brvcc)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* We should probably use netif_*_queue() here, but that
|
|
|
|
* involves added complication. We need to walk before
|
2007-12-31 07:18:29 +00:00
|
|
|
* we can run.
|
|
|
|
*
|
|
|
|
* Don't free here! this pointer might be no longer valid!
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2009-01-09 13:00:58 +00:00
|
|
|
dev->stats.tx_errors++;
|
|
|
|
dev->stats.tx_fifo_errors++;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2012-11-27 23:28:36 +00:00
|
|
|
out:
|
|
|
|
bh_unlock_sock(sk_atm(atmvcc));
|
|
|
|
out_devs:
|
2005-04-16 22:20:36 +00:00
|
|
|
read_unlock(&devs_lock);
|
2012-11-27 23:28:36 +00:00
|
|
|
return ret;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We remember when the MAC gets set, so we don't override it later with
|
|
|
|
* the ESI of the ATM card of the first VC
|
|
|
|
*/
|
|
|
|
static int br2684_mac_addr(struct net_device *dev, void *p)
|
|
|
|
{
|
2009-01-09 13:00:59 +00:00
|
|
|
int err = eth_mac_addr(dev, p);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!err)
|
|
|
|
BRPRIV(dev)->mac_was_set = 1;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_ATM_BR2684_IPFILTER
|
|
|
|
/* this IOCTL is experimental. */
|
2007-12-31 07:18:29 +00:00
|
|
|
static int br2684_setfilt(struct atm_vcc *atmvcc, void __user * arg)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct br2684_vcc *brvcc;
|
|
|
|
struct br2684_filter_set fs;
|
|
|
|
|
|
|
|
if (copy_from_user(&fs, arg, sizeof fs))
|
|
|
|
return -EFAULT;
|
|
|
|
if (fs.ifspec.method != BR2684_FIND_BYNOTHING) {
|
|
|
|
/*
|
|
|
|
* This is really a per-vcc thing, but we can also search
|
2007-12-31 07:18:29 +00:00
|
|
|
* by device.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
struct br2684_dev *brdev;
|
|
|
|
read_lock(&devs_lock);
|
|
|
|
brdev = BRPRIV(br2684_find_dev(&fs.ifspec));
|
2010-01-26 11:40:04 +00:00
|
|
|
if (brdev == NULL || list_empty(&brdev->brvccs) ||
|
|
|
|
brdev->brvccs.next != brdev->brvccs.prev) /* >1 VCC */
|
2005-04-16 22:20:36 +00:00
|
|
|
brvcc = NULL;
|
|
|
|
else
|
|
|
|
brvcc = list_entry_brvcc(brdev->brvccs.next);
|
|
|
|
read_unlock(&devs_lock);
|
|
|
|
if (brvcc == NULL)
|
|
|
|
return -ESRCH;
|
|
|
|
} else
|
|
|
|
brvcc = BR2684_VCC(atmvcc);
|
|
|
|
memcpy(&brvcc->filter, &fs.filter, sizeof(brvcc->filter));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns 1 if packet should be dropped */
|
|
|
|
static inline int
|
2006-11-15 05:11:29 +00:00
|
|
|
packet_fails_filter(__be16 type, struct br2684_vcc *brvcc, struct sk_buff *skb)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
if (brvcc->filter.netmask == 0)
|
2007-12-31 07:18:29 +00:00
|
|
|
return 0; /* no filter in place */
|
2007-03-26 03:12:32 +00:00
|
|
|
if (type == htons(ETH_P_IP) &&
|
2007-12-31 07:18:29 +00:00
|
|
|
(((struct iphdr *)(skb->data))->daddr & brvcc->filter.
|
2005-04-16 22:20:36 +00:00
|
|
|
netmask) == brvcc->filter.prefix)
|
|
|
|
return 0;
|
2007-03-26 03:12:32 +00:00
|
|
|
if (type == htons(ETH_P_ARP))
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
2007-12-31 07:18:29 +00:00
|
|
|
/*
|
|
|
|
* TODO: we should probably filter ARPs too.. don't want to have
|
|
|
|
* them returning values that don't make sense, or is that ok?
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
return 1; /* drop */
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_ATM_BR2684_IPFILTER */
|
|
|
|
|
|
|
|
static void br2684_close_vcc(struct br2684_vcc *brvcc)
|
|
|
|
{
|
2007-08-28 22:22:09 +00:00
|
|
|
pr_debug("removing VCC %p from dev %p\n", brvcc, brvcc->device);
|
2005-04-16 22:20:36 +00:00
|
|
|
write_lock_irq(&devs_lock);
|
|
|
|
list_del(&brvcc->brvccs);
|
|
|
|
write_unlock_irq(&devs_lock);
|
|
|
|
brvcc->atmvcc->user_back = NULL; /* what about vcc->recvq ??? */
|
2012-11-27 23:28:36 +00:00
|
|
|
brvcc->atmvcc->release_cb = brvcc->old_release_cb;
|
2005-04-16 22:20:36 +00:00
|
|
|
brvcc->old_push(brvcc->atmvcc, NULL); /* pass on the bad news */
|
2012-11-28 00:46:45 +00:00
|
|
|
module_put(brvcc->old_owner);
|
2005-04-16 22:20:36 +00:00
|
|
|
kfree(brvcc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* when AAL5 PDU comes in: */
|
|
|
|
static void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct br2684_vcc *brvcc = BR2684_VCC(atmvcc);
|
|
|
|
struct net_device *net_dev = brvcc->device;
|
|
|
|
struct br2684_dev *brdev = BRPRIV(net_dev);
|
|
|
|
|
2010-01-26 11:40:00 +00:00
|
|
|
pr_debug("\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (unlikely(skb == NULL)) {
|
|
|
|
/* skb==NULL means VCC is being destroyed */
|
|
|
|
br2684_close_vcc(brvcc);
|
|
|
|
if (list_empty(&brdev->brvccs)) {
|
2008-05-05 01:00:36 +00:00
|
|
|
write_lock_irq(&devs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
list_del(&brdev->br2684_devs);
|
2008-05-05 01:00:36 +00:00
|
|
|
write_unlock_irq(&devs_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
unregister_netdev(net_dev);
|
2008-05-06 07:00:16 +00:00
|
|
|
free_netdev(net_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb_debug(skb);
|
|
|
|
atm_return(atmvcc, skb->truesize);
|
2007-08-28 22:22:09 +00:00
|
|
|
pr_debug("skb from brdev %p\n", brdev);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (brvcc->encaps == e_llc) {
|
2007-12-31 07:17:53 +00:00
|
|
|
|
|
|
|
if (skb->len > 7 && skb->data[7] == 0x01)
|
|
|
|
__skb_trim(skb, skb->len - 4);
|
|
|
|
|
|
|
|
/* accept packets that have "ipv[46]" in the snap header */
|
2010-01-26 11:40:04 +00:00
|
|
|
if ((skb->len >= (sizeof(llc_oui_ipv4))) &&
|
|
|
|
(memcmp(skb->data, llc_oui_ipv4,
|
|
|
|
sizeof(llc_oui_ipv4) - BR2684_ETHERTYPE_LEN) == 0)) {
|
|
|
|
if (memcmp(skb->data + 6, ethertype_ipv6,
|
|
|
|
sizeof(ethertype_ipv6)) == 0)
|
2008-09-21 05:20:49 +00:00
|
|
|
skb->protocol = htons(ETH_P_IPV6);
|
2010-01-26 11:40:04 +00:00
|
|
|
else if (memcmp(skb->data + 6, ethertype_ipv4,
|
|
|
|
sizeof(ethertype_ipv4)) == 0)
|
2008-09-21 05:20:49 +00:00
|
|
|
skb->protocol = htons(ETH_P_IP);
|
2008-06-17 00:18:18 +00:00
|
|
|
else
|
|
|
|
goto error;
|
2007-12-31 07:17:53 +00:00
|
|
|
skb_pull(skb, sizeof(llc_oui_ipv4));
|
|
|
|
skb_reset_network_header(skb);
|
|
|
|
skb->pkt_type = PACKET_HOST;
|
2010-01-26 11:40:04 +00:00
|
|
|
/*
|
|
|
|
* Let us waste some time for checking the encapsulation.
|
|
|
|
* Note, that only 7 char is checked so frames with a valid FCS
|
|
|
|
* are also accepted (but FCS is not checked of course).
|
|
|
|
*/
|
2007-12-31 07:17:53 +00:00
|
|
|
} else if ((skb->len >= sizeof(llc_oui_pid_pad)) &&
|
2007-12-31 07:18:29 +00:00
|
|
|
(memcmp(skb->data, llc_oui_pid_pad, 7) == 0)) {
|
2007-12-31 07:17:53 +00:00
|
|
|
skb_pull(skb, sizeof(llc_oui_pid_pad));
|
|
|
|
skb->protocol = eth_type_trans(skb, net_dev);
|
2008-06-17 00:18:18 +00:00
|
|
|
} else
|
|
|
|
goto error;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-06-17 00:18:18 +00:00
|
|
|
} else { /* e_vc */
|
|
|
|
if (brdev->payload == p_routed) {
|
|
|
|
struct iphdr *iph;
|
|
|
|
|
|
|
|
skb_reset_network_header(skb);
|
|
|
|
iph = ip_hdr(skb);
|
|
|
|
if (iph->version == 4)
|
2008-09-21 05:20:49 +00:00
|
|
|
skb->protocol = htons(ETH_P_IP);
|
2008-06-17 00:18:18 +00:00
|
|
|
else if (iph->version == 6)
|
2008-09-21 05:20:49 +00:00
|
|
|
skb->protocol = htons(ETH_P_IPV6);
|
2008-06-17 00:18:18 +00:00
|
|
|
else
|
|
|
|
goto error;
|
|
|
|
skb->pkt_type = PACKET_HOST;
|
|
|
|
} else { /* p_bridged */
|
|
|
|
/* first 2 chars should be 0 */
|
2011-08-17 06:37:52 +00:00
|
|
|
if (memcmp(skb->data, pad, BR2684_PAD_LEN) != 0)
|
2008-06-17 00:18:18 +00:00
|
|
|
goto error;
|
|
|
|
skb_pull(skb, BR2684_PAD_LEN);
|
|
|
|
skb->protocol = eth_type_trans(skb, net_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_ATM_BR2684_IPFILTER
|
2008-06-17 00:18:18 +00:00
|
|
|
if (unlikely(packet_fails_filter(skb->protocol, brvcc, skb)))
|
|
|
|
goto dropped;
|
2005-04-16 22:20:36 +00:00
|
|
|
#endif /* CONFIG_ATM_BR2684_IPFILTER */
|
|
|
|
skb->dev = net_dev;
|
|
|
|
ATM_SKB(skb)->vcc = atmvcc; /* needed ? */
|
2007-08-28 22:22:09 +00:00
|
|
|
pr_debug("received packet's protocol: %x\n", ntohs(skb->protocol));
|
2005-04-16 22:20:36 +00:00
|
|
|
skb_debug(skb);
|
2008-06-17 00:18:18 +00:00
|
|
|
/* sigh, interface is down? */
|
|
|
|
if (unlikely(!(net_dev->flags & IFF_UP)))
|
|
|
|
goto dropped;
|
2009-01-09 13:00:58 +00:00
|
|
|
net_dev->stats.rx_packets++;
|
|
|
|
net_dev->stats.rx_bytes += skb->len;
|
2005-04-16 22:20:36 +00:00
|
|
|
memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data));
|
|
|
|
netif_rx(skb);
|
2008-06-17 00:18:18 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
dropped:
|
2009-01-09 13:00:58 +00:00
|
|
|
net_dev->stats.rx_dropped++;
|
2008-06-17 00:18:18 +00:00
|
|
|
goto free_skb;
|
|
|
|
error:
|
2009-01-09 13:00:58 +00:00
|
|
|
net_dev->stats.rx_errors++;
|
2008-06-17 00:18:18 +00:00
|
|
|
free_skb:
|
|
|
|
dev_kfree_skb(skb);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-12-31 07:18:29 +00:00
|
|
|
/*
|
|
|
|
* Assign a vcc to a dev
|
|
|
|
* Note: we do not have explicit unassign, but look at _push()
|
|
|
|
*/
|
|
|
|
static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct br2684_vcc *brvcc;
|
|
|
|
struct br2684_dev *brdev;
|
|
|
|
struct net_device *net_dev;
|
|
|
|
struct atm_backend_br2684 be;
|
2011-11-21 10:25:57 +00:00
|
|
|
int err;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (copy_from_user(&be, arg, sizeof be))
|
|
|
|
return -EFAULT;
|
2006-07-21 21:51:30 +00:00
|
|
|
brvcc = kzalloc(sizeof(struct br2684_vcc), GFP_KERNEL);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!brvcc)
|
|
|
|
return -ENOMEM;
|
atm: br2684: Fix excessive queue bloat
There's really no excuse for an additional wmem_default of buffering
between the netdev queue and the ATM device. Two packets (one in-flight,
and one ready to send) ought to be fine. It's not as if it should take
long to get another from the netdev queue when we need it.
If necessary we can make the queue space configurable later, but I don't
think it's likely to be necessary.
cf. commit 9d02daf754238adac48fa075ee79e7edd3d79ed3 (pppoatm: Fix
excessive queue bloat) which did something very similar for PPPoATM.
Note that there is a tremendously unlikely race condition which may
result in qspace temporarily going negative. If a CPU running the
br2684_pop() function goes off into the weeds for a long period of time
after incrementing qspace to 1, but before calling netdev_wake_queue()...
and another CPU ends up calling br2684_start_xmit() and *stopping* the
queue again before the first CPU comes back, the netdev queue could
end up being woken when qspace has already reached zero.
An alternative approach to coping with this race would be to check in
br2684_start_xmit() for qspace==0 and return NETDEV_TX_BUSY, but just
using '> 0' and '< 1' for comparison instead of '== 0' and '!= 0' is
simpler. It just warranted a mention of *why* we do it that way...
Move the call to atmvcc->send() to happen *after* the accounting and
potentially stopping the netdev queue, in br2684_xmit_vcc(). This matters
if the ->send() call suffers an immediate failure, because it'll call
br2684_pop() with the offending skb before returning. We want that to
happen *after* we've done the initial accounting for the packet in
question. Also make it return an appropriate success/failure indication
while we're at it.
Tested by running 'ping -l 1000 bottomless.aaisp.net.uk' from within my
network, with only a single PPPoE-over-BR2684 link running. And after
setting txqueuelen on the nas0 interface to something low (5, in fact).
Before the patch, we'd see about 15 packets being queued and a resulting
latency of ~56ms being reached. After the patch, we see only about 8,
which is fairly much what we expect. And a max latency of ~36ms. On this
OpenWRT box, wmem_default is 163840.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Reviewed-by: Krzysztof Mazur <krzysiek@podlesie.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-11-25 12:06:52 +00:00
|
|
|
/*
|
|
|
|
* Allow two packets in the ATM queue. One actually being sent, and one
|
|
|
|
* for the ATM 'TX done' handler to send. It shouldn't take long to get
|
|
|
|
* the next one from the netdev queue, when we need it. More than that
|
|
|
|
* would be bufferbloat.
|
|
|
|
*/
|
|
|
|
atomic_set(&brvcc->qspace, 2);
|
2005-04-16 22:20:36 +00:00
|
|
|
write_lock_irq(&devs_lock);
|
|
|
|
net_dev = br2684_find_dev(&be.ifspec);
|
|
|
|
if (net_dev == NULL) {
|
2011-03-31 01:57:33 +00:00
|
|
|
pr_err("tried to attach to non-existent device\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
err = -ENXIO;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
brdev = BRPRIV(net_dev);
|
|
|
|
if (atmvcc->push == NULL) {
|
|
|
|
err = -EBADFD;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (!list_empty(&brdev->brvccs)) {
|
|
|
|
/* Only 1 VCC/dev right now */
|
|
|
|
err = -EEXIST;
|
|
|
|
goto error;
|
|
|
|
}
|
2010-01-26 11:40:04 +00:00
|
|
|
if (be.fcs_in != BR2684_FCSIN_NO ||
|
|
|
|
be.fcs_out != BR2684_FCSOUT_NO ||
|
|
|
|
be.fcs_auto || be.has_vpiid || be.send_padding ||
|
|
|
|
(be.encaps != BR2684_ENCAPS_VC &&
|
|
|
|
be.encaps != BR2684_ENCAPS_LLC) ||
|
|
|
|
be.min_size != 0) {
|
2005-04-16 22:20:36 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto error;
|
|
|
|
}
|
2010-01-26 11:40:00 +00:00
|
|
|
pr_debug("vcc=%p, encaps=%d, brvcc=%p\n", atmvcc, be.encaps, brvcc);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (list_empty(&brdev->brvccs) && !brdev->mac_was_set) {
|
|
|
|
unsigned char *esi = atmvcc->dev->esi;
|
|
|
|
if (esi[0] | esi[1] | esi[2] | esi[3] | esi[4] | esi[5])
|
|
|
|
memcpy(net_dev->dev_addr, esi, net_dev->addr_len);
|
|
|
|
else
|
|
|
|
net_dev->dev_addr[2] = 1;
|
|
|
|
}
|
|
|
|
list_add(&brvcc->brvccs, &brdev->brvccs);
|
|
|
|
write_unlock_irq(&devs_lock);
|
|
|
|
brvcc->device = net_dev;
|
|
|
|
brvcc->atmvcc = atmvcc;
|
|
|
|
atmvcc->user_back = brvcc;
|
2007-12-31 07:18:29 +00:00
|
|
|
brvcc->encaps = (enum br2684_encaps)be.encaps;
|
2005-04-16 22:20:36 +00:00
|
|
|
brvcc->old_push = atmvcc->push;
|
2009-09-03 06:26:39 +00:00
|
|
|
brvcc->old_pop = atmvcc->pop;
|
2012-11-27 23:28:36 +00:00
|
|
|
brvcc->old_release_cb = atmvcc->release_cb;
|
2012-11-28 00:46:45 +00:00
|
|
|
brvcc->old_owner = atmvcc->owner;
|
2005-04-16 22:20:36 +00:00
|
|
|
barrier();
|
|
|
|
atmvcc->push = br2684_push;
|
2009-09-03 06:26:39 +00:00
|
|
|
atmvcc->pop = br2684_pop;
|
2012-11-27 23:28:36 +00:00
|
|
|
atmvcc->release_cb = br2684_release_cb;
|
2012-11-28 00:46:45 +00:00
|
|
|
atmvcc->owner = THIS_MODULE;
|
2006-12-01 05:05:23 +00:00
|
|
|
|
2010-07-08 20:55:31 +00:00
|
|
|
/* initialize netdev carrier state */
|
|
|
|
if (atmvcc->dev->signal == ATM_PHY_SIG_LOST)
|
|
|
|
netif_carrier_off(net_dev);
|
|
|
|
else
|
|
|
|
netif_carrier_on(net_dev);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
__module_get(THIS_MODULE);
|
2011-11-21 10:25:57 +00:00
|
|
|
|
|
|
|
/* re-process everything received between connection setup and
|
|
|
|
backend setup */
|
|
|
|
vcc_process_recv_queue(atmvcc);
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
2010-01-26 11:40:04 +00:00
|
|
|
|
|
|
|
error:
|
2005-04-16 22:20:36 +00:00
|
|
|
write_unlock_irq(&devs_lock);
|
|
|
|
kfree(brvcc);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-01-09 13:00:59 +00:00
|
|
|
static const struct net_device_ops br2684_netdev_ops = {
|
|
|
|
.ndo_start_xmit = br2684_start_xmit,
|
|
|
|
.ndo_set_mac_address = br2684_mac_addr,
|
|
|
|
.ndo_change_mtu = eth_change_mtu,
|
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
|
|
};
|
|
|
|
|
2009-12-04 11:06:32 +00:00
|
|
|
static const struct net_device_ops br2684_netdev_ops_routed = {
|
|
|
|
.ndo_start_xmit = br2684_start_xmit,
|
|
|
|
.ndo_set_mac_address = br2684_mac_addr,
|
|
|
|
.ndo_change_mtu = eth_change_mtu
|
|
|
|
};
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static void br2684_setup(struct net_device *netdev)
|
|
|
|
{
|
|
|
|
struct br2684_dev *brdev = BRPRIV(netdev);
|
|
|
|
|
|
|
|
ether_setup(netdev);
|
2011-08-17 06:37:18 +00:00
|
|
|
netdev->hard_header_len += sizeof(llc_oui_pid_pad); /* worst case */
|
2009-05-02 20:49:36 +00:00
|
|
|
brdev->net_dev = netdev;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-01-09 13:00:59 +00:00
|
|
|
netdev->netdev_ops = &br2684_netdev_ops;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
INIT_LIST_HEAD(&brdev->brvccs);
|
|
|
|
}
|
|
|
|
|
2007-12-31 07:17:53 +00:00
|
|
|
static void br2684_setup_routed(struct net_device *netdev)
|
|
|
|
{
|
|
|
|
struct br2684_dev *brdev = BRPRIV(netdev);
|
|
|
|
|
2009-12-04 11:06:32 +00:00
|
|
|
brdev->net_dev = netdev;
|
2011-08-17 06:37:18 +00:00
|
|
|
netdev->hard_header_len = sizeof(llc_oui_ipv4); /* worst case */
|
2009-12-04 11:06:32 +00:00
|
|
|
netdev->netdev_ops = &br2684_netdev_ops_routed;
|
2007-12-31 07:17:53 +00:00
|
|
|
netdev->addr_len = 0;
|
|
|
|
netdev->mtu = 1500;
|
|
|
|
netdev->type = ARPHRD_PPP;
|
|
|
|
netdev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
|
|
|
|
netdev->tx_queue_len = 100;
|
|
|
|
INIT_LIST_HEAD(&brdev->brvccs);
|
|
|
|
}
|
|
|
|
|
2010-01-26 11:40:04 +00:00
|
|
|
static int br2684_create(void __user *arg)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct net_device *netdev;
|
|
|
|
struct br2684_dev *brdev;
|
|
|
|
struct atm_newif_br2684 ni;
|
2007-12-31 07:17:53 +00:00
|
|
|
enum br2684_payload payload;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-01-26 11:40:00 +00:00
|
|
|
pr_debug("\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-01-26 11:40:04 +00:00
|
|
|
if (copy_from_user(&ni, arg, sizeof ni))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EFAULT;
|
2007-12-31 07:17:53 +00:00
|
|
|
|
|
|
|
if (ni.media & BR2684_FLAG_ROUTED)
|
|
|
|
payload = p_routed;
|
|
|
|
else
|
|
|
|
payload = p_bridged;
|
2007-12-31 07:18:29 +00:00
|
|
|
ni.media &= 0xffff; /* strip flags */
|
2007-12-31 07:17:53 +00:00
|
|
|
|
2010-01-26 11:40:04 +00:00
|
|
|
if (ni.media != BR2684_MEDIA_ETHERNET || ni.mtu != 1500)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
netdev = alloc_netdev(sizeof(struct br2684_dev),
|
|
|
|
ni.ifname[0] ? ni.ifname : "nas%d",
|
2007-12-31 07:17:53 +00:00
|
|
|
(payload == p_routed) ?
|
2007-12-31 07:18:29 +00:00
|
|
|
br2684_setup_routed : br2684_setup);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!netdev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
brdev = BRPRIV(netdev);
|
|
|
|
|
2007-08-28 22:22:09 +00:00
|
|
|
pr_debug("registered netdev %s\n", netdev->name);
|
2005-04-16 22:20:36 +00:00
|
|
|
/* open, stop, do_ioctl ? */
|
|
|
|
err = register_netdev(netdev);
|
|
|
|
if (err < 0) {
|
2010-01-26 11:40:00 +00:00
|
|
|
pr_err("register_netdev failed\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
free_netdev(netdev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
write_lock_irq(&devs_lock);
|
2010-07-08 20:55:31 +00:00
|
|
|
|
2007-12-31 07:17:53 +00:00
|
|
|
brdev->payload = payload;
|
2010-07-08 20:55:31 +00:00
|
|
|
|
|
|
|
if (list_empty(&br2684_devs)) {
|
|
|
|
/* 1st br2684 device */
|
|
|
|
brdev->number = 1;
|
|
|
|
} else
|
|
|
|
brdev->number = BRPRIV(list_entry_brdev(br2684_devs.prev))->number + 1;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
list_add_tail(&brdev->br2684_devs, &br2684_devs);
|
|
|
|
write_unlock_irq(&devs_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This handles ioctls actually performed on our vcc - we must return
|
|
|
|
* -ENOIOCTLCMD for any unrecognized ioctl
|
|
|
|
*/
|
|
|
|
static int br2684_ioctl(struct socket *sock, unsigned int cmd,
|
2007-12-31 07:18:29 +00:00
|
|
|
unsigned long arg)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct atm_vcc *atmvcc = ATM_SD(sock);
|
|
|
|
void __user *argp = (void __user *)arg;
|
2007-12-31 07:18:29 +00:00
|
|
|
atm_backend_t b;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
int err;
|
2007-12-31 07:18:29 +00:00
|
|
|
switch (cmd) {
|
2005-04-16 22:20:36 +00:00
|
|
|
case ATM_SETBACKEND:
|
2007-12-31 07:18:29 +00:00
|
|
|
case ATM_NEWBACKENDIF:
|
2005-04-16 22:20:36 +00:00
|
|
|
err = get_user(b, (atm_backend_t __user *) argp);
|
|
|
|
if (err)
|
|
|
|
return -EFAULT;
|
|
|
|
if (b != ATM_BACKEND_BR2684)
|
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
if (!capable(CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
2012-11-28 08:08:04 +00:00
|
|
|
if (cmd == ATM_SETBACKEND) {
|
|
|
|
if (sock->state != SS_CONNECTED)
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
return br2684_regvcc(atmvcc, argp);
|
2012-11-28 08:08:04 +00:00
|
|
|
} else {
|
2005-04-16 22:20:36 +00:00
|
|
|
return br2684_create(argp);
|
2012-11-28 08:08:04 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
#ifdef CONFIG_ATM_BR2684_IPFILTER
|
|
|
|
case BR2684_SETFILT:
|
|
|
|
if (atmvcc->push != br2684_push)
|
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
if (!capable(CAP_NET_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
err = br2684_setfilt(atmvcc, argp);
|
2007-12-31 07:18:29 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
|
|
|
#endif /* CONFIG_ATM_BR2684_IPFILTER */
|
|
|
|
}
|
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct atm_ioctl br2684_ioctl_ops = {
|
2007-12-31 07:18:29 +00:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.ioctl = br2684_ioctl,
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
2007-12-31 07:18:29 +00:00
|
|
|
static void *br2684_seq_start(struct seq_file *seq, loff_t * pos)
|
2008-01-15 11:29:50 +00:00
|
|
|
__acquires(devs_lock)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
read_lock(&devs_lock);
|
2007-07-09 20:12:24 +00:00
|
|
|
return seq_list_start(&br2684_devs, *pos);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-12-31 07:18:29 +00:00
|
|
|
static void *br2684_seq_next(struct seq_file *seq, void *v, loff_t * pos)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-07-09 20:12:24 +00:00
|
|
|
return seq_list_next(v, &br2684_devs, pos);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void br2684_seq_stop(struct seq_file *seq, void *v)
|
2008-01-15 11:29:50 +00:00
|
|
|
__releases(devs_lock)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
read_unlock(&devs_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int br2684_seq_show(struct seq_file *seq, void *v)
|
|
|
|
{
|
2007-07-09 20:12:24 +00:00
|
|
|
const struct br2684_dev *brdev = list_entry(v, struct br2684_dev,
|
2007-12-31 07:18:29 +00:00
|
|
|
br2684_devs);
|
2005-04-16 22:20:36 +00:00
|
|
|
const struct net_device *net_dev = brdev->net_dev;
|
|
|
|
const struct br2684_vcc *brvcc;
|
|
|
|
|
2008-10-27 22:59:26 +00:00
|
|
|
seq_printf(seq, "dev %.16s: num=%d, mac=%pM (%s)\n",
|
2007-10-04 00:59:30 +00:00
|
|
|
net_dev->name,
|
|
|
|
brdev->number,
|
2008-10-27 22:59:26 +00:00
|
|
|
net_dev->dev_addr,
|
2007-10-04 00:59:30 +00:00
|
|
|
brdev->mac_was_set ? "set" : "auto");
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
list_for_each_entry(brvcc, &brdev->brvccs, brvccs) {
|
2007-12-31 07:17:53 +00:00
|
|
|
seq_printf(seq, " vcc %d.%d.%d: encaps=%s payload=%s"
|
2007-12-31 07:18:29 +00:00
|
|
|
", failed copies %u/%u"
|
|
|
|
"\n", brvcc->atmvcc->dev->number,
|
|
|
|
brvcc->atmvcc->vpi, brvcc->atmvcc->vci,
|
|
|
|
(brvcc->encaps == e_llc) ? "LLC" : "VC",
|
|
|
|
(brdev->payload == p_bridged) ? "bridged" : "routed",
|
|
|
|
brvcc->copies_failed, brvcc->copies_needed);
|
2005-04-16 22:20:36 +00:00
|
|
|
#ifdef CONFIG_ATM_BR2684_IPFILTER
|
|
|
|
#define b1(var, byte) ((u8 *) &brvcc->filter.var)[byte]
|
|
|
|
#define bs(var) b1(var, 0), b1(var, 1), b1(var, 2), b1(var, 3)
|
2007-12-31 07:18:29 +00:00
|
|
|
if (brvcc->filter.netmask != 0)
|
|
|
|
seq_printf(seq, " filter=%d.%d.%d.%d/"
|
|
|
|
"%d.%d.%d.%d\n", bs(prefix), bs(netmask));
|
2005-04-16 22:20:36 +00:00
|
|
|
#undef bs
|
|
|
|
#undef b1
|
|
|
|
#endif /* CONFIG_ATM_BR2684_IPFILTER */
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-11 06:07:31 +00:00
|
|
|
static const struct seq_operations br2684_seq_ops = {
|
2005-04-16 22:20:36 +00:00
|
|
|
.start = br2684_seq_start,
|
2007-12-31 07:18:29 +00:00
|
|
|
.next = br2684_seq_next,
|
|
|
|
.stop = br2684_seq_stop,
|
|
|
|
.show = br2684_seq_show,
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int br2684_proc_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return seq_open(file, &br2684_seq_ops);
|
|
|
|
}
|
|
|
|
|
2007-02-12 08:55:35 +00:00
|
|
|
static const struct file_operations br2684_proc_ops = {
|
2007-12-31 07:18:29 +00:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = br2684_proc_open,
|
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
2005-04-16 22:20:36 +00:00
|
|
|
.release = seq_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
extern struct proc_dir_entry *atm_proc_root; /* from proc.c */
|
2007-12-31 07:18:29 +00:00
|
|
|
#endif /* CONFIG_PROC_FS */
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
static int __init br2684_init(void)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
struct proc_dir_entry *p;
|
2008-02-28 21:55:45 +00:00
|
|
|
p = proc_create("br2684", 0, atm_proc_root, &br2684_proc_ops);
|
|
|
|
if (p == NULL)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
#endif
|
|
|
|
register_atm_ioctl(&br2684_ioctl_ops);
|
2010-09-23 01:50:54 +00:00
|
|
|
register_atmdevice_notifier(&atm_dev_notifier);
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit br2684_exit(void)
|
|
|
|
{
|
|
|
|
struct net_device *net_dev;
|
|
|
|
struct br2684_dev *brdev;
|
|
|
|
struct br2684_vcc *brvcc;
|
|
|
|
deregister_atm_ioctl(&br2684_ioctl_ops);
|
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
remove_proc_entry("br2684", atm_proc_root);
|
|
|
|
#endif
|
|
|
|
|
2010-07-08 20:55:31 +00:00
|
|
|
|
2010-09-23 01:50:54 +00:00
|
|
|
unregister_atmdevice_notifier(&atm_dev_notifier);
|
2010-07-08 20:55:31 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
while (!list_empty(&br2684_devs)) {
|
|
|
|
net_dev = list_entry_brdev(br2684_devs.next);
|
|
|
|
brdev = BRPRIV(net_dev);
|
|
|
|
while (!list_empty(&brdev->brvccs)) {
|
|
|
|
brvcc = list_entry_brvcc(brdev->brvccs.next);
|
|
|
|
br2684_close_vcc(brvcc);
|
|
|
|
}
|
|
|
|
|
|
|
|
list_del(&brdev->br2684_devs);
|
|
|
|
unregister_netdev(net_dev);
|
2008-05-06 07:00:16 +00:00
|
|
|
free_netdev(net_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(br2684_init);
|
|
|
|
module_exit(br2684_exit);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Marcell GAL");
|
|
|
|
MODULE_DESCRIPTION("RFC2684 bridged protocols over ATM/AAL5");
|
|
|
|
MODULE_LICENSE("GPL");
|