Commit fc53e6ec authored by Linus Torvalds's avatar Linus Torvalds

Merge http://linux-scsi.bkbits.net/scsi-for-linus-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 0c482202 a537cfc1
README file for the dc395x SCSI driver
==========================================
Status
------
The driver has been tested with CD-R and CD-R/W drives. These should
be safe to use. Testing with hard disks has not been done to any
great degree and caution should be exercised if you want to attempt
to use this driver with hard disks.
This is a 2.5 only driver. For a 2.4 driver please see the original
driver (which this driver started from) at
http://www.garloff.de/kurt/linux/dc395/
Problems, questions and patches should be submitted to the mailing
list. Details on the list, including archives, are available at
http://lists.twibble.org/mailman/listinfo/dc395x/
Parameters
----------
The driver uses the settings from the EEPROM set in the SCSI BIOS
setup. If there is no EEPROM, the driver uses default values.
Both can be overriden by command line parameters (module or kernel
parameters).
The syntax is as follows:
dc395x = AdapterID, SpeedIdx, DevMode, AdaptMode, Tags, DelayReset
AdapterID : Host Adapter SCSI ID
SpeedIdx : 0,1,...7 = 20,13.3,10,8,6.7,5.8,5,4 MHz [ 7]
DevMode : Bitmap for Dev Cfg [63]
AdaptMode : Bitmap for Adapter Cfg [47]
Tags : The number of tags is 1<<x, if x has been specified [ 4]
DelayReset: The seconds to not accept commands after a SCSI Reset [ 1]
DevMode bit definition:
Bit Val(hex) Val(dec) Meaning
*0 0x01 1 Parity check
*1 0x02 2 Synchronous Negotiation
*2 0x04 4 Disconnection
*3 0x08 8 Send Start command on startup. (Not used)
*4 0x10 16 Tagged Command Queueing
*5 0x20 32 Wide Negotiation
AdaptMode bit definition
Bit Val(hex) Val(dec) Meaning
*0 0x01 1 Support more than two drives. (Not used)
*1 0x02 2 Use DOS compatible mapping for HDs greater than 1GB.
*2 0x04 4 Reset SCSI Bus on startup.
*3 0x08 8 Active Negation: Improves SCSI Bus noise immunity.
4 0x10 16 Immediate return on BIOS seek command. (Not used)
(*)5 0x20 32 Check for LUNs >= 1.
If you set AdapterID to -1, the adapter will use conservative
("safe") default settings instead; more precisely, dc395x=-1 is a
shortcut for dc395x=7,4,9,15,2,10
If you specify -2 for a value, it will be ignored. You don't need to
specify all six parameters.
Copyright
---------
The driver is free software. It is protected by the GNU General Public
License (GPL). Please read it, before using this driver. It should be
included in your kernel sources and with your distribution. It carries the
filename COPYING. If you don't have it, please ask me to send you one by
email.
Note: The GNU GPL says also something about warranty and liability.
Please be aware the following: While we do my best to provide a working and
reliable driver, there is a chance, that it will kill your valuable data.
We refuse to take any responsibility for that. The driver is provided as-is
and YOU USE IT AT YOUR OWN RESPONSIBILITY.
......@@ -505,6 +505,17 @@ W: http://www.qsl.net/dl1bke/
L: linux-hams@vger.kernel.org
S: Maintained
DC395x SCSI driver
P: Oliver Neukum
M: oliver@neukum.name
P: Ali Akcaagac
M: aliakc@web.de
P: Jamie Lenehan
M: lenehan@twibble.org
W: http://twibble.org/dist/dc395x/
L: http://lists.twibble.org/mailman/listinfo/dc395x/
S: Maintained
DC390/AM53C974 SCSI driver
P: Kurt Garloff
M: garloff@suse.de
......
......@@ -414,7 +414,7 @@ static int i810_dma_initialize(drm_device_t *dev,
return -ENOMEM;
}
memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page);
DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);
I810_WRITE(0x02080, dev_priv->dma_status_page);
DRM_DEBUG("Enabled hardware status page\n");
......
......@@ -40,6 +40,7 @@
#include <linux/string.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/oplib.h>
#include <asm/system.h>
#include <asm/uaccess.h>
......
......@@ -1390,6 +1390,23 @@ config SCSI_SYM53C416
read <file:Documentation/modules.txt>. The module will be called
sym53c416.
config SCSI_DC395x
tristate "Tekram DC395(U/UW/F) and DC315(U) SCSI support (EXPERIMENTAL)"
depends on EXPERIMENTAL && PCI && SCSI
---help---
This driver supports PCI SCSI host adapters based on the ASIC
TRM-S1040 chip, e.g Tekram DC395(U/UW/F) and DC315(U) variants.
This driver works, but is still in experimental status. So better
have a bootable disk and a backup in case of emergency.
Documentation can be found in <file:Documentation/scsi/dc395x.txt>.
If you want to compile this driver as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
say M here and read <file:Documentation/modules.txt>. The module
will be called dc395x.
config SCSI_DC390T
tristate "Tekram DC390(T) and Am53/79C974 SCSI support"
depends on PCI && SCSI
......
......@@ -88,6 +88,7 @@ obj-$(CONFIG_SCSI_7000FASST) += wd7000.o
obj-$(CONFIG_SCSI_MCA_53C9X) += NCR53C9x.o mca_53c9x.o
obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o
obj-$(CONFIG_SCSI_EATA) += eata.o
obj-$(CONFIG_SCSI_DC395x) += dc395x.o
obj-$(CONFIG_SCSI_DC390T) += tmscsim.o
obj-$(CONFIG_SCSI_AM53C974) += AM53C974.o
obj-$(CONFIG_SCSI_MEGARAID) += megaraid.o
......
This diff is collapsed.
This diff is collapsed.
......@@ -320,6 +320,21 @@ static inline void list_splice_init(struct list_head *list,
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
/**
* list_for_each_entry_rcu - iterate over rcu list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_rcu(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
prefetch(pos->member.next); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member), \
({ smp_read_barrier_depends(); 0;}), \
prefetch(pos->member.next))
/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
......
......@@ -15,6 +15,8 @@
#ifndef _NET_IF_INET6_H
#define _NET_IF_INET6_H
#include <net/snmp.h>
#define IF_RA_RCVD 0x20
#define IF_RS_SENT 0x10
......@@ -152,6 +154,11 @@ struct ipv6_devconf
void *sysctl;
};
struct ipv6_devstat {
struct proc_dir_entry *proc_dir_entry;
DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6);
};
struct inet6_dev
{
struct net_device *dev;
......@@ -185,6 +192,7 @@ struct inet6_dev
struct neigh_parms *nd_parms;
struct inet6_dev *next;
struct ipv6_devconf cnf;
struct ipv6_devstat stats;
};
extern struct ipv6_devconf ipv6_devconf;
......
......@@ -106,24 +106,48 @@ struct frag_hdr {
/* sysctls */
extern int sysctl_ipv6_bindv6only;
/* MIBs */
DECLARE_SNMP_STAT(struct ipv6_mib, ipv6_statistics);
#define IP6_INC_STATS(field) SNMP_INC_STATS(ipv6_statistics, field)
#define IP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(ipv6_statistics, field)
#define IP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(ipv6_statistics, field)
DECLARE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics);
#define ICMP6_INC_STATS(field) SNMP_INC_STATS(icmpv6_statistics, field)
#define ICMP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(icmpv6_statistics, field)
#define ICMP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(icmpv6_statistics, field)
#define ICMP6_STATS_PTR_BH(field) \
(& \
((per_cpu_ptr(icmpv6_statistics[0], smp_processor_id()))-> \
field))
#define ICMP6_INC_STATS(idev, field) ({ \
struct inet6_dev *_idev = (idev); \
if (likely(_idev != NULL)) \
SNMP_INC_STATS(idev->stats.icmpv6, field); \
SNMP_INC_STATS(icmpv6_statistics, field); \
})
#define ICMP6_INC_STATS_BH(idev, field) ({ \
struct inet6_dev *_idev = (idev); \
if (likely(_idev != NULL)) \
SNMP_INC_STATS_BH((_idev)->stats.icmpv6, field); \
SNMP_INC_STATS_BH(icmpv6_statistics, field); \
})
#define ICMP6_INC_STATS_USER(idev, field) ({ \
struct inet6_dev *_idev = (idev); \
if (likely(_idev != NULL)) \
SNMP_INC_STATS_USER(_idev->stats.icmpv6, field); \
SNMP_INC_STATS_USER(icmpv6_statistics, field); \
})
#define ICMP6_INC_STATS_OFFSET_BH(idev, field, offset) ({ \
struct inet6_dev *_idev = idev; \
__typeof__(offset) _offset = (offset); \
if (likely(_idev != NULL)) \
SNMP_INC_STATS_OFFSET_BH(_idev->stats.icmpv6, field, _offset); \
SNMP_INC_STATS_OFFSET_BH(icmpv6_statistics, field, _offset); \
})
DECLARE_SNMP_STAT(struct udp_mib, udp_stats_in6);
#define UDP6_INC_STATS(field) SNMP_INC_STATS(udp_stats_in6, field)
#define UDP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(udp_stats_in6, field)
#define UDP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(udp_stats_in6, field)
extern atomic_t inet6_sock_nr;
int snmp6_register_dev(struct inet6_dev *idev);
int snmp6_unregister_dev(struct inet6_dev *idev);
int snmp6_mib_init(void *ptr[2], size_t mibsize);
void snmp6_mib_free(void *ptr[2]);
struct ip6_ra_chain
{
struct ip6_ra_chain *next;
......
......@@ -304,6 +304,8 @@ struct linux_mib
#define SNMP_INC_STATS_BH(mib, field) \
(per_cpu_ptr(mib[0], smp_processor_id())->field++)
#define SNMP_INC_STATS_OFFSET_BH(mib, field, offset) \
((*((&per_cpu_ptr(mib[0], smp_processor_id())->field) + (offset)))++)
#define SNMP_INC_STATS_USER(mib, field) \
(per_cpu_ptr(mib[1], smp_processor_id())->field++)
#define SNMP_INC_STATS(mib, field) \
......
......@@ -6,6 +6,7 @@ obj-y := main.o version.o mounts.o initramfs.o
mounts-y := do_mounts.o
mounts-$(CONFIG_DEVFS_FS) += do_mounts_devfs.o
mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o
mounts-$(CONFIG_BLK_DEV_INITRD) += do_mounts_initrd.o
mounts-$(CONFIG_BLK_DEV_MD) += do_mounts_md.o
# files to be removed upon make clean
......
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/minix_fs.h>
#include <linux/ext2_fs.h>
#include <linux/romfs_fs.h>
#include <linux/initrd.h>
#include "do_mounts.h"
unsigned int real_root_dev; /* do_proc_dointvec cannot handle kdev_t */
static int __initdata old_fd, root_fd;
static int __initdata mount_initrd = 1;
static int __init no_initrd(char *str)
{
mount_initrd = 0;
return 1;
}
__setup("noinitrd", no_initrd);
static int __init do_linuxrc(void * shell)
{
static char *argv[] = { "linuxrc", NULL, };
extern char * envp_init[];
close(old_fd);close(root_fd);
close(0);close(1);close(2);
setsid();
(void) open("/dev/console",O_RDWR,0);
(void) dup(0);
(void) dup(0);
return execve(shell, argv, envp_init);
}
static void __init handle_initrd(void)
{
int error;
int i, pid;
real_root_dev = ROOT_DEV;
create_dev("/dev/root.old", Root_RAM0, NULL);
/* mount initrd on rootfs' /root */
mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
sys_mkdir("/old", 0700);
root_fd = open("/", 0, 0);
old_fd = open("/old", 0, 0);
/* move initrd over / and chdir/chroot in initrd root */
sys_chdir("/root");
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
mount_devfs_fs ();
pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
if (pid > 0) {
while (pid != waitpid(-1, &i, 0))
yield();
}
/* move initrd to rootfs' /old */
sys_fchdir(old_fd);
sys_mount("/", ".", NULL, MS_MOVE, NULL);
/* switch root and cwd back to / of rootfs */
sys_fchdir(root_fd);
sys_chroot(".");
close(old_fd);
close(root_fd);
umount_devfs("/old/dev");
if (real_root_dev == Root_RAM0) {
sys_chdir("/old");
return;
}
ROOT_DEV = real_root_dev;
mount_root();
printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
if (!error)
printk("okay\n");
else {
int fd = open("/dev/root.old", O_RDWR, 0);
printk("failed\n");
printk(KERN_NOTICE "Unmounting old root\n");
sys_umount("/old", MNT_DETACH);
printk(KERN_NOTICE "Trying to free ramdisk memory ... ");
if (fd < 0) {
error = fd;
} else {
error = sys_ioctl(fd, BLKFLSBUF, 0);
close(fd);
}
printk(!error ? "okay\n" : "failed\n");
}
}
int __init initrd_load(void)
{
if (!mount_initrd)
return 0;
create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, 0), NULL);
create_dev("/dev/initrd", MKDEV(RAMDISK_MAJOR, INITRD_MINOR), NULL);
/* Load the initrd data into /dev/ram0. Execute it as initrd unless
* /dev/ram0 is supposed to be our actual root device, in
* that case the ram disk is just set up here, and gets
* mounted in the normal path. */
if (rd_load_image("/dev/initrd") && ROOT_DEV != Root_RAM0) {
handle_initrd();
return 1;
}
return 0;
}
......@@ -248,115 +248,6 @@ int __init rd_load_disk(int n)
return rd_load_image("/dev/root");
}
#ifdef CONFIG_BLK_DEV_INITRD
unsigned int real_root_dev; /* do_proc_dointvec cannot handle kdev_t */
static int __initdata old_fd, root_fd;
static int __initdata mount_initrd = 1;
static int __init no_initrd(char *str)
{
mount_initrd = 0;
return 1;
}
__setup("noinitrd", no_initrd);
static int __init do_linuxrc(void * shell)
{
static char *argv[] = { "linuxrc", NULL, };
extern char * envp_init[];
close(old_fd);close(root_fd);
close(0);close(1);close(2);
setsid();
(void) open("/dev/console",O_RDWR,0);
(void) dup(0);
(void) dup(0);
return execve(shell, argv, envp_init);
}
static void __init handle_initrd(void)
{
int error;
int i, pid;
real_root_dev = ROOT_DEV;
create_dev("/dev/root.old", Root_RAM0, NULL);
/* mount initrd on rootfs' /root */
mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);
sys_mkdir("/old", 0700);
root_fd = open("/", 0, 0);
old_fd = open("/old", 0, 0);
/* move initrd over / and chdir/chroot in initrd root */
sys_chdir("/root");
sys_mount(".", "/", NULL, MS_MOVE, NULL);
sys_chroot(".");
mount_devfs_fs ();
pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
if (pid > 0) {
while (pid != waitpid(-1, &i, 0))
yield();
}
/* move initrd to rootfs' /old */
sys_fchdir(old_fd);
sys_mount("/", ".", NULL, MS_MOVE, NULL);
/* switch root and cwd back to / of rootfs */
sys_fchdir(root_fd);
sys_chroot(".");
close(old_fd);
close(root_fd);
umount_devfs("/old/dev");
if (real_root_dev == Root_RAM0) {
sys_chdir("/old");
return;
}
ROOT_DEV = real_root_dev;
mount_root();
printk(KERN_NOTICE "Trying to move old root to /initrd ... ");
error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
if (!error)
printk("okay\n");
else {
int fd = open("/dev/root.old", O_RDWR, 0);
printk("failed\n");
printk(KERN_NOTICE "Unmounting old root\n");
sys_umount("/old", MNT_DETACH);
printk(KERN_NOTICE "Trying to free ramdisk memory ... ");
if (fd < 0) {
error = fd;
} else {
error = sys_ioctl(fd, BLKFLSBUF, 0);
close(fd);
}
printk(!error ? "okay\n" : "failed\n");
}
}
int __init initrd_load(void)
{
if (!mount_initrd)
return 0;
create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, 0), NULL);
create_dev("/dev/initrd", MKDEV(RAMDISK_MAJOR, INITRD_MINOR), NULL);
/* Load the initrd data into /dev/ram0. Execute it as initrd unless
* /dev/ram0 is supposed to be our actual root device, in
* that case the ram disk is just set up here, and gets
* mounted in the normal path. */
if (rd_load_image("/dev/initrd") && ROOT_DEV != Root_RAM0) {
handle_initrd();
return 1;
}
return 0;
}
#endif
#ifdef BUILD_CRAMDISK
/*
......
......@@ -74,27 +74,20 @@ static int __br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct net_bridge *br;
int ret;
br = dev->priv;
read_lock(&br->lock);
rcu_read_lock();
ret = __br_dev_xmit(skb, dev);
read_unlock(&br->lock);
rcu_read_unlock();
return ret;
}
static int br_dev_open(struct net_device *dev)
{
struct net_bridge *br;
netif_start_queue(dev);
br = dev->priv;
write_lock(&br->lock);
br_stp_enable_bridge(br);
write_unlock(&br->lock);
br_stp_enable_bridge(dev->priv);
return 0;
}
......@@ -105,12 +98,7 @@ static void br_dev_set_multicast_list(struct net_device *dev)
static int br_dev_stop(struct net_device *dev)
{
struct net_bridge *br;
br = dev->priv;
write_lock(&br->lock);
br_stp_disable_bridge(br);
write_unlock(&br->lock);
br_stp_disable_bridge(dev->priv);
netif_stop_queue(dev);
......
......@@ -21,7 +21,8 @@
#include <linux/netfilter_bridge.h>
#include "br_private.h"
static inline int should_deliver(struct net_bridge_port *p, struct sk_buff *skb)
static inline int should_deliver(const struct net_bridge_port *p,
const struct sk_buff *skb)
{
if (skb->dev == p->dev ||
p->state != BR_STATE_FORWARDING)
......@@ -52,7 +53,7 @@ int br_forward_finish(struct sk_buff *skb)
return 0;
}
static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
{
skb->dev = to->dev;
#ifdef CONFIG_NETFILTER_DEBUG
......@@ -62,7 +63,7 @@ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
br_forward_finish);
}
static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
{
struct net_device *indev;
......@@ -73,8 +74,8 @@ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
br_forward_finish);
}
/* called under bridge lock */
void br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
/* called with rcu_read_lock */
void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
{
if (should_deliver(to, skb)) {
__br_deliver(to, skb);
......@@ -84,8 +85,8 @@ void br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
kfree_skb(skb);
}
/* called under bridge lock */
void br_forward(struct net_bridge_port *to, struct sk_buff *skb)
/* called with rcu_read_lock */
void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
{
if (should_deliver(to, skb)) {
__br_forward(to, skb);
......@@ -97,7 +98,8 @@ void br_forward(struct net_bridge_port *to, struct sk_buff *skb)
/* called under bridge lock */
static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
void (*__packet_hook)(struct net_bridge_port *p, struct sk_buff *skb))
void (*__packet_hook)(const struct net_bridge_port *p,
struct sk_buff *skb))
{
struct net_bridge_port *p;
struct net_bridge_port *prev;
......@@ -115,8 +117,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
prev = NULL;
p = br->port_list;
while (p != NULL) {
list_for_each_entry_rcu(p, &br->port_list, list) {
if (should_deliver(p, skb)) {
if (prev != NULL) {
struct sk_buff *skb2;
......@@ -132,8 +133,6 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
prev = p;
}
p = p->next;
}
if (prev != NULL) {
......@@ -144,7 +143,8 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
kfree_skb(skb);
}
/* called under bridge lock */
/* called with rcu_read_lock */
void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
{
br_flood(br, skb, clone, __br_deliver);
......
......@@ -18,8 +18,8 @@
#include <linux/if_bridge.h>
#include <linux/inetdevice.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rtnetlink.h>
#include <linux/brlock.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include "br_private.h"
......@@ -38,45 +38,39 @@ static int br_initial_port_cost(struct net_device *dev)
return 100;
}
/* called under BR_NETPROTO_LOCK and bridge lock */
static int __br_del_if(struct net_bridge *br, struct net_device *dev)
static void destroy_nbp(void *arg)
{
struct net_bridge_port *p;
struct net_bridge_port **pptr;
struct net_bridge_port *p = arg;
dev_put(p->dev);
kfree(p);
}
if ((p = dev->br_port) == NULL)
return -EINVAL;
/* called under bridge lock */
static void del_nbp(struct net_bridge_port *p)
{
struct net_device *dev = p->dev;
br_stp_disable_port(p);
dev_set_promiscuity(dev, -1);
dev->br_port = NULL;
pptr = &br->port_list;
while (*pptr != NULL) {
if (*pptr == p) {
*pptr = p->next;
break;
}
list_del_rcu(&p->list);
pptr = &((*pptr)->next);
}
br_fdb_delete_by_port(p->br, p);
br_fdb_delete_by_port(br, p);
kfree(p);
dev_put(dev);
return 0;
call_rcu(&p->rcu, destroy_nbp, p);
}
static void del_ifs(struct net_bridge *br)
{
br_write_lock_bh(BR_NETPROTO_LOCK);
write_lock(&br->lock);
while (br->port_list != NULL)
__br_del_if(br, br->port_list->dev);
write_unlock(&br->lock);
br_write_unlock_bh(BR_NETPROTO_LOCK);
struct list_head *p, *n;
spin_lock_bh(&br->lock);
list_for_each_safe(p, n, &br->port_list) {
del_nbp(list_entry(p, struct net_bridge_port, list));
}
spin_unlock_bh(&br->lock);
}
static struct net_bridge *new_nb(const char *name)
......@@ -98,7 +92,8 @@ static struct net_bridge *new_nb(const char *name)
ether_setup(dev);
br_dev_setup(dev);
br->lock = RW_LOCK_UNLOCKED;
br->lock = SPIN_LOCK_UNLOCKED;
INIT_LIST_HEAD(&br->port_list);
br->hash_lock = RW_LOCK_UNLOCKED;
br->bridge_id.prio[0] = 0x80;
......@@ -155,8 +150,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device
br_init_port(p);
p->state = BR_STATE_DISABLED;
p->next = br->port_list;
br->port_list = p;
list_add_rcu(&p->list, &br->port_list);
return p;
}
......@@ -218,9 +212,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return -ELOOP;
dev_hold(dev);
write_lock_bh(&br->lock);
spin_lock_bh(&br->lock);
if ((p = new_nbp(br, dev)) == NULL) {
write_unlock_bh(&br->lock);
spin_unlock_bh(&br->lock);
dev_put(dev);
return -EXFULL;
}
......@@ -231,21 +225,24 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
br_fdb_insert(br, p, dev->dev_addr, 1);
if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP))
br_stp_enable_port(p);
write_unlock_bh(&br->lock);
spin_unlock_bh(&br->lock);
return 0;
}
int br_del_if(struct net_bridge *br, struct net_device *dev)
{
int retval;
struct net_bridge_port *p;
int retval = 0;
br_write_lock_bh(BR_NETPROTO_LOCK);
write_lock(&br->lock);
retval = __br_del_if(br, dev);
br_stp_recalculate_bridge_id(br);
write_unlock(&br->lock);
br_write_unlock_bh(BR_NETPROTO_LOCK);
spin_lock_bh(&br->lock);
if ((p = dev->br_port) == NULL || p->br != br)
retval = -EINVAL;
else {
del_nbp(p);
br_stp_recalculate_bridge_id(br);
}
spin_unlock_bh(&br->lock);
return retval;
}
......@@ -269,13 +266,11 @@ void br_get_port_ifindices(struct net_bridge *br, int *ifindices)
{
struct net_bridge_port *p;
read_lock(&br->lock);
p = br->port_list;
while (p != NULL) {
rcu_read_lock();
list_for_each_entry_rcu(p, &br->port_list, list) {
ifindices[p->port_no] = p->dev->ifindex;
p = p->next;
}
read_unlock(&br->lock);
rcu_read_unlock();
}
......
......@@ -59,15 +59,16 @@ int br_handle_frame_finish(struct sk_buff *skb)
dest = skb->mac.ethernet->h_dest;
rcu_read_lock();
p = skb->dev->br_port;
if (p == NULL)
goto err_nolock;
smp_read_barrier_depends();
br = p->br;
read_lock(&br->lock);
if (skb->dev->br_port == NULL)
goto err;
if (p == NULL || p->state == BR_STATE_DISABLED) {
kfree(skb);
goto out;
}
br = p->br;
passedup = 0;
if (br->dev.flags & IFF_PROMISC) {
struct sk_buff *skb2;
......@@ -105,35 +106,20 @@ int br_handle_frame_finish(struct sk_buff *skb)
br_flood_forward(br, skb, 0);
out:
read_unlock(&br->lock);
return 0;
err:
read_unlock(&br->lock);
err_nolock:
kfree_skb(skb);
rcu_read_unlock();
return 0;
}
int br_handle_frame(struct sk_buff *skb)
{
struct net_bridge *br;
unsigned char *dest;
struct net_bridge_port *p;
dest = skb->mac.ethernet->h_dest;
rcu_read_lock();
p = skb->dev->br_port;
if (p == NULL)
goto err_nolock;
br = p->br;
read_lock(&br->lock);
if (skb->dev->br_port == NULL)
goto err;
if (!(br->dev.flags & IFF_UP) ||
p->state == BR_STATE_DISABLED)
if (p == NULL || p->state == BR_STATE_DISABLED)
goto err;
if (skb->mac.ethernet->h_source[0] & 1)
......@@ -141,39 +127,30 @@ int br_handle_frame(struct sk_buff *skb)
if (p->state == BR_STATE_LEARNING ||
p->state == BR_STATE_FORWARDING)
br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
br_fdb_insert(p->br, p, skb->mac.ethernet->h_source, 0);
if (br->stp_enabled &&
if (p->br->stp_enabled &&
!memcmp(dest, bridge_ula, 5) &&
!(dest[5] & 0xF0))
goto handle_special_frame;
!(dest[5] & 0xF0)) {
if (!dest[5])
br_stp_handle_bpdu(skb);
goto err;
}
if (p->state == BR_STATE_FORWARDING) {
if (br_should_route_hook && br_should_route_hook(&skb)) {
read_unlock(&br->lock);
rcu_read_unlock();
return -1;
}
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);
read_unlock(&br->lock);
rcu_read_unlock();
return 0;
}
err:
read_unlock(&br->lock);
err_nolock:
kfree_skb(skb);
return 0;
handle_special_frame:
if (!dest[5]) {
br_stp_handle_bpdu(skb);
read_unlock(&br->lock);
return 0;
}
rcu_read_unlock();
kfree_skb(skb);
read_unlock(&br->lock);
return 0;
}
......@@ -68,8 +68,8 @@ static int br_ioctl_device(struct net_bridge *br,
{
struct __bridge_info b;
read_lock(&br->lock);
memset(&b, 0, sizeof(struct __bridge_info));
rcu_read_lock();
memcpy(&b.designated_root, &br->designated_root, 8);
memcpy(&b.bridge_id, &br->bridge_id, 8);
b.root_path_cost = br->root_path_cost;
......@@ -89,7 +89,7 @@ static int br_ioctl_device(struct net_bridge *br,
b.tcn_timer_value = timer_residue(&br->tcn_timer);
b.topology_change_timer_value = timer_residue(&br->topology_change_timer);
b.gc_timer_value = timer_residue(&br->gc_timer);
read_unlock(&br->lock);
rcu_read_unlock();
if (copy_to_user((void *)arg0, &b, sizeof(b)))
return -EFAULT;
......@@ -116,27 +116,27 @@ static int br_ioctl_device(struct net_bridge *br,
}
case BRCTL_SET_BRIDGE_FORWARD_DELAY:
write_lock(&br->lock);
spin_lock_bh(&br->lock);
br->bridge_forward_delay = user_to_ticks(arg0);
if (br_is_root_bridge(br))
br->forward_delay = br->bridge_forward_delay;
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return 0;
case BRCTL_SET_BRIDGE_HELLO_TIME:
write_lock(&br->lock);
spin_lock_bh(&br->lock);
br->bridge_hello_time = user_to_ticks(arg0);
if (br_is_root_bridge(br))
br->hello_time = br->bridge_hello_time;
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return 0;
case BRCTL_SET_BRIDGE_MAX_AGE:
write_lock(&br->lock);
spin_lock_bh(&br->lock);
br->bridge_max_age = user_to_ticks(arg0);
if (br_is_root_bridge(br))
br->max_age = br->bridge_max_age;
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return 0;
case BRCTL_SET_AGEING_TIME:
......@@ -152,9 +152,9 @@ static int br_ioctl_device(struct net_bridge *br,
struct __port_info p;
struct net_bridge_port *pt;
read_lock(&br->lock);
rcu_read_lock();
if ((pt = br_get_port(br, arg1)) == NULL) {
read_unlock(&br->lock);
rcu_read_unlock();
return -EINVAL;
}
......@@ -172,7 +172,7 @@ static int br_ioctl_device(struct net_bridge *br,
p.forward_delay_timer_value = timer_residue(&pt->forward_delay_timer);
p.hold_timer_value = timer_residue(&pt->hold_timer);
read_unlock(&br->lock);
rcu_read_unlock();
if (copy_to_user((void *)arg0, &p, sizeof(p)))
return -EFAULT;
......@@ -185,9 +185,9 @@ static int br_ioctl_device(struct net_bridge *br,
return 0;
case BRCTL_SET_BRIDGE_PRIORITY:
write_lock(&br->lock);
spin_lock_bh(&br->lock);
br_stp_set_bridge_priority(br, arg0);
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return 0;
case BRCTL_SET_PORT_PRIORITY:
......@@ -195,12 +195,12 @@ static int br_ioctl_device(struct net_bridge *br,
struct net_bridge_port *p;
int ret = 0;
write_lock(&br->lock);
spin_lock_bh(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL)
ret = -EINVAL;
else
br_stp_set_port_priority(p, arg1);
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return ret;
}
......@@ -209,12 +209,12 @@ static int br_ioctl_device(struct net_bridge *br,
struct net_bridge_port *p;
int ret = 0;
write_lock(&br->lock);
spin_lock_bh(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL)
ret = -EINVAL;
else
br_stp_set_path_cost(p, arg1);
write_unlock(&br->lock);
spin_unlock_bh(&br->lock);
return ret;
}
......
......@@ -41,10 +41,10 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
switch (event)
{
case NETDEV_CHANGEADDR:
write_lock_bh(&br->lock);
spin_lock_bh(&br->lock);
br_fdb_changeaddr(p, dev->dev_addr);
br_stp_recalculate_bridge_id(br);
write_unlock_bh(&br->lock);
spin_unlock_bh(&br->lock);
break;
case NETDEV_GOING_DOWN:
......@@ -53,17 +53,17 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
case NETDEV_DOWN:
if (br->dev.flags & IFF_UP) {
write_lock_bh(&br->lock);
spin_lock_bh(&br->lock);
br_stp_disable_port(p);
write_unlock_bh(&br->lock);
spin_unlock_bh(&br->lock);
}
break;
case NETDEV_UP:
if (!(br->dev.flags & IFF_UP)) {
write_lock_bh(&br->lock);
spin_lock_bh(&br->lock);
br_stp_enable_port(p);
write_unlock_bh(&br->lock);
spin_unlock_bh(&br->lock);
}
break;
......
......@@ -55,9 +55,9 @@ struct net_bridge_fdb_entry
struct net_bridge_port
{
struct net_bridge_port *next;
struct net_bridge *br;
struct net_device *dev;
struct list_head list;
int port_no;
/* STP */
......@@ -75,12 +75,14 @@ struct net_bridge_port
struct br_timer forward_delay_timer;
struct br_timer hold_timer;
struct br_timer message_age_timer;
struct rcu_head rcu;
};
struct net_bridge
{
rwlock_t lock;
struct net_bridge_port *port_list;
spinlock_t lock;
struct list_head port_list;
struct net_device dev;
struct net_device_stats statistics;
rwlock_t hash_lock;
......@@ -137,10 +139,10 @@ extern void br_fdb_insert(struct net_bridge *br,
int is_local);
/* br_forward.c */
extern void br_deliver(struct net_bridge_port *to,
extern void br_deliver(const struct net_bridge_port *to,
struct sk_buff *skb);
extern int br_dev_queue_push_xmit(struct sk_buff *skb);
extern void br_forward(struct net_bridge_port *to,
extern void br_forward(const struct net_bridge_port *to,
struct sk_buff *skb);
extern int br_forward_finish(struct sk_buff *skb);
extern void br_flood_deliver(struct net_bridge *br,
......
......@@ -22,7 +22,7 @@
/* called under ioctl_lock or bridge lock */
/* called under bridge lock */
int br_is_root_bridge(struct net_bridge *br)
{
return !memcmp(&br->bridge_id, &br->designated_root, 8);
......@@ -35,17 +35,14 @@ int br_is_designated_port(struct net_bridge_port *p)
(p->designated_port == p->port_id);
}
/* called under ioctl_lock or bridge lock */
/* called under bridge lock */
struct net_bridge_port *br_get_port(struct net_bridge *br, int port_no)
{
struct net_bridge_port *p;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->port_no == port_no)
return p;
p = p->next;
}
return NULL;
......@@ -109,12 +106,10 @@ static void br_root_selection(struct net_bridge *br)
root_port = 0;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (br_should_become_root_port(p, root_port))
root_port = p->port_no;
p = p->next;
}
br->root_port = root_port;
......@@ -241,13 +236,11 @@ static void br_designated_port_selection(struct net_bridge *br)
{
struct net_bridge_port *p;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED &&
br_should_become_designated_port(p))
br_become_designated_port(p);
p = p->next;
}
}
......@@ -313,13 +306,10 @@ void br_config_bpdu_generation(struct net_bridge *br)
{
struct net_bridge_port *p;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p))
br_transmit_config(p);
p = p->next;
}
}
......@@ -391,8 +381,7 @@ void br_port_state_selection(struct net_bridge *br)
{
struct net_bridge_port *p;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED) {
if (p->port_no == br->root_port) {
p->config_pending = 0;
......@@ -407,8 +396,6 @@ void br_port_state_selection(struct net_bridge *br)
br_make_blocking(p);
}
}
p = p->next;
}
}
......@@ -419,18 +406,13 @@ static void br_topology_change_acknowledge(struct net_bridge_port *p)
br_transmit_config(p);
}
/* lock-safe */
/* called under bridge lock */
void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
{
struct net_bridge *br;
int was_root;
if (p->state == BR_STATE_DISABLED)
return;
br = p->br;
read_lock(&br->lock);
was_root = br_is_root_bridge(br);
if (br_supersedes_port_info(p, bpdu)) {
br_record_config_information(p, bpdu);
......@@ -455,21 +437,16 @@ void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *b
} else if (br_is_designated_port(p)) {
br_reply(p);
}
read_unlock(&br->lock);
}
/* lock-safe */
/* called under bridge lock */
void br_received_tcn_bpdu(struct net_bridge_port *p)
{
read_lock(&p->br->lock);
if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p)) {
if (br_is_designated_port(p)) {
printk(KERN_INFO "%s: received tcn bpdu on port %i(%s)\n",
p->br->dev.name, p->port_no, p->dev->name);
br_topology_change_detection(p->br);
br_topology_change_acknowledge(p);
}
read_unlock(&p->br->lock);
}
......@@ -132,18 +132,23 @@ void br_send_tcn_bpdu(struct net_bridge_port *p)
static unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
/* called under bridge lock */
/* NO locks */
void br_stp_handle_bpdu(struct sk_buff *skb)
{
unsigned char *buf;
struct net_bridge_port *p;
struct net_bridge *br;
buf = skb->mac.raw + 14;
p = skb->dev->br_port;
if (!p->br->stp_enabled || memcmp(buf, header, 6)) {
kfree_skb(skb);
return;
}
br = p->br;
spin_lock_bh(&br->lock);
if (p->state == BR_STATE_DISABLED
|| !(br->dev.flags & IFF_UP)
|| !br->stp_enabled
|| memcmp(buf, header, 6))
goto out;
if (buf[6] == BPDU_TYPE_CONFIG) {
struct br_config_bpdu bpdu;
......@@ -178,16 +183,14 @@ void br_stp_handle_bpdu(struct sk_buff *skb)
bpdu.hello_time = br_get_ticks(buf+34);
bpdu.forward_delay = br_get_ticks(buf+36);
kfree_skb(skb);
br_received_config_bpdu(p, &bpdu);
return;
goto out;
}
if (buf[6] == BPDU_TYPE_TCN) {
br_received_tcn_bpdu(p);
kfree_skb(skb);
return;
goto out;
}
kfree_skb(skb);
out:
spin_unlock_bh(&br->lock);
}
......@@ -44,6 +44,7 @@ void br_stp_enable_bridge(struct net_bridge *br)
struct net_bridge_port *p;
struct timer_list *timer = &br->tick;
spin_lock_bh(&br->lock);
init_timer(timer);
timer->data = (unsigned long) br;
timer->function = br_tick;
......@@ -53,22 +54,21 @@ void br_stp_enable_bridge(struct net_bridge *br)
br_timer_set(&br->hello_timer, jiffies);
br_config_bpdu_generation(br);
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->dev->flags & IFF_UP)
br_stp_enable_port(p);
p = p->next;
}
br_timer_set(&br->gc_timer, jiffies);
spin_unlock_bh(&br->lock);
}
/* called under bridge lock */
/* NO locks held */
void br_stp_disable_bridge(struct net_bridge *br)
{
struct net_bridge_port *p;
spin_lock_bh(&br->lock);
br->topology_change = 0;
br->topology_change_detected = 0;
br_timer_clear(&br->hello_timer);
......@@ -77,13 +77,11 @@ void br_stp_disable_bridge(struct net_bridge *br)
br_timer_clear(&br->gc_timer);
br_fdb_cleanup(br);
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED)
br_stp_disable_port(p);
p = p->next;
}
spin_unlock_bh(&br->lock);
del_timer_sync(&br->tick);
}
......@@ -133,15 +131,13 @@ static void br_stp_change_bridge_id(struct net_bridge *br, unsigned char *addr)
memcpy(br->bridge_id.addr, addr, ETH_ALEN);
memcpy(br->dev.dev_addr, addr, ETH_ALEN);
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN))
memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN))
memcpy(p->designated_root.addr, addr, ETH_ALEN);
p = p->next;
}
br_configuration_update(br);
......@@ -160,13 +156,11 @@ void br_stp_recalculate_bridge_id(struct net_bridge *br)
addr = br_mac_zero;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (addr == br_mac_zero ||
memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
addr = p->dev->dev_addr;
p = p->next;
}
if (memcmp(br->bridge_id.addr, addr, ETH_ALEN))
......@@ -181,15 +175,13 @@ void br_stp_set_bridge_priority(struct net_bridge *br, int newprio)
wasroot = br_is_root_bridge(br);
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p)) {
p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF;
p->designated_bridge.prio[1] = newprio & 0xFF;
}
p = p->next;
}
br->bridge_id.prio[0] = (newprio >> 8) & 0xFF;
......
......@@ -32,13 +32,10 @@ static int br_is_designated_for_some_port(struct net_bridge *br)
{
struct net_bridge_port *p;
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED &&
!memcmp(&p->designated_bridge, &br->bridge_id, 8))
return 1;
p = p->next;
}
return 0;
......@@ -162,12 +159,9 @@ static void br_check_timers(struct net_bridge *br)
br_topology_change_timer_expired(br);
}
p = br->port_list;
while (p != NULL) {
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED)
br_check_port_timers(p);
p = p->next;
}
}
......@@ -175,10 +169,10 @@ void br_tick(unsigned long __data)
{
struct net_bridge *br = (struct net_bridge *)__data;
read_lock(&br->lock);
br_check_timers(br);
read_unlock(&br->lock);
if (spin_trylock_bh(&br->lock)) {
br_check_timers(br);
spin_unlock_bh(&br->lock);
}
br->tick.expires = jiffies + 1;
add_timer(&br->tick);
}
......@@ -300,6 +300,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
printk("Freeing alive inet6 device %p\n", idev);
return;
}
snmp6_unregister_dev(idev);
inet6_dev_count--;
kfree(idev);
}
......@@ -332,6 +333,15 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
/* We refer to the device */
dev_hold(dev);
if (snmp6_register_dev(ndev) < 0) {
ADBG((KERN_WARNING
"%s(): cannot create /proc/net/dev_snmp6/%s\n",
__FUNCTION__, dev->name));
neigh_parms_release(&nd_tbl, ndev->nd_parms);
in6_dev_finish_destroy(ndev);
return NULL;
}
#ifdef CONFIG_IPV6_PRIVACY
get_random_bytes(ndev->rndid, sizeof(ndev->rndid));
get_random_bytes(ndev->entropy, sizeof(ndev->entropy));
......
......@@ -631,79 +631,72 @@ inet6_unregister_protosw(struct inet_protosw *p)
inet_unregister_protosw(p);
}
static int __init init_ipv6_mibs(void)
int
snmp6_mib_init(void *ptr[2], size_t mibsize)
{
int i;
ipv6_statistics[0] = kmalloc_percpu(sizeof (struct ipv6_mib),
GFP_KERNEL);
if (!ipv6_statistics[0])
goto err_ip_mib0;
ipv6_statistics[1] = kmalloc_percpu(sizeof (struct ipv6_mib),
GFP_KERNEL);
if (!ipv6_statistics[1])
goto err_ip_mib1;
icmpv6_statistics[0] = kmalloc_percpu(sizeof (struct icmpv6_mib),
GFP_KERNEL);
if (!icmpv6_statistics[0])
goto err_icmp_mib0;
icmpv6_statistics[1] = kmalloc_percpu(sizeof (struct icmpv6_mib),
GFP_KERNEL);
if (!icmpv6_statistics[1])
goto err_icmp_mib1;
udp_stats_in6[0] = kmalloc_percpu(sizeof (struct udp_mib),
GFP_KERNEL);
if (!udp_stats_in6[0])
goto err_udp_mib0;
udp_stats_in6[1] = kmalloc_percpu(sizeof (struct udp_mib),
GFP_KERNEL);
if (!udp_stats_in6[1])
goto err_udp_mib1;
/* Zero all percpu versions of the mibs */
if (ptr == NULL)
return -EINVAL;
ptr[0] = kmalloc_percpu(mibsize, GFP_KERNEL);
if (!ptr[0])
goto err0;
ptr[1] = kmalloc_percpu(mibsize, GFP_KERNEL);
if (!ptr[1])
goto err1;
/* Zero percpu version of the mibs */
for (i = 0; i < NR_CPUS; i++) {
if (cpu_possible(i)) {
memset(per_cpu_ptr(ipv6_statistics[0], i), 0,
sizeof (struct ipv6_mib));
memset(per_cpu_ptr(ipv6_statistics[1], i), 0,
sizeof (struct ipv6_mib));
memset(per_cpu_ptr(icmpv6_statistics[0], i), 0,
sizeof (struct icmpv6_mib));
memset(per_cpu_ptr(icmpv6_statistics[1], i), 0,
sizeof (struct icmpv6_mib));
memset(per_cpu_ptr(udp_stats_in6[0], i), 0,
sizeof (struct udp_mib));
memset(per_cpu_ptr(udp_stats_in6[1], i), 0,
sizeof (struct udp_mib));
memset(per_cpu_ptr(ptr[0], i), 0, mibsize);
memset(per_cpu_ptr(ptr[1], i), 0, mibsize);
}
}
return 0;
err_udp_mib1:
kfree_percpu(udp_stats_in6[0]);
err_udp_mib0:
kfree_percpu(icmpv6_statistics[1]);
err_icmp_mib1:
kfree_percpu(icmpv6_statistics[0]);
err_icmp_mib0:
kfree_percpu(ipv6_statistics[1]);
err_ip_mib1:
kfree_percpu(ipv6_statistics[0]);
err_ip_mib0:
err1:
kfree_percpu(ptr[0]);
ptr[0] = NULL;
err0:
return -ENOMEM;
}
void
snmp6_mib_free(void *ptr[2])
{
if (ptr == NULL)
return;
kfree_percpu(ptr[0]);
kfree_percpu(ptr[1]);
ptr[0] = ptr[1] = NULL;
}
static int __init init_ipv6_mibs(void)
{
if (snmp6_mib_init((void **)ipv6_statistics, sizeof (struct ipv6_mib)) < 0)
goto err_ip_mib;
if (snmp6_mib_init((void **)icmpv6_statistics, sizeof (struct icmpv6_mib)) < 0)
goto err_icmp_mib;
if (snmp6_mib_init((void **)udp_stats_in6, sizeof (struct udp_mib)) < 0)
goto err_udp_mib;
return 0;
err_udp_mib:
snmp6_mib_free((void **)icmpv6_statistics);
err_icmp_mib:
snmp6_mib_free((void **)ipv6_statistics);
err_ip_mib:
return -ENOMEM;
}
static void cleanup_ipv6_mibs(void)
{
kfree_percpu(ipv6_statistics[0]);
kfree_percpu(ipv6_statistics[1]);
kfree_percpu(icmpv6_statistics[0]);
kfree_percpu(icmpv6_statistics[1]);
kfree_percpu(udp_stats_in6[0]);
kfree_percpu(udp_stats_in6[1]);
snmp6_mib_free((void **)ipv6_statistics);
snmp6_mib_free((void **)icmpv6_statistics);
snmp6_mib_free((void **)udp_stats_in6);
}
extern int ipv6_misc_proc_init(void);
......@@ -819,6 +812,7 @@ static int __init inet6_init(void)
#ifdef CONFIG_PROC_FS
proc_anycast6_fail:
proc_net_remove("snmp6");
proc_net_remove("dev_snmp6");
proc_net_remove("sockstat6");
proc_misc6_fail:
proc_net_remove("udp6");
......@@ -854,6 +848,7 @@ static void inet6_exit(void)
proc_net_remove("tcp6");
proc_net_remove("udp6");
proc_net_remove("sockstat6");
proc_net_remove("dev_snmp6");
proc_net_remove("snmp6");
proc_net_remove("anycast6");
#endif
......
......@@ -26,6 +26,8 @@
* yoshfuji : ensure to sent parameter problem for
* fragments.
* YOSHIFUJI Hideaki @USAGI: added sysctl for icmp rate limit.
* Randy Dunlap and
* YOSHIFUJI Hideaki @USAGI: Per-interface statistics support
*/
#include <linux/module.h>
......@@ -247,6 +249,7 @@ static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
struct net_device *dev)
{
struct inet6_dev *idev;
struct ipv6hdr *hdr = skb->nh.ipv6h;
struct sock *sk = icmpv6_socket->sk;
struct in6_addr *saddr = NULL;
......@@ -351,11 +354,16 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
msg.len = len;
idev = in6_dev_get(skb->dev);
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
MSG_DONTWAIT);
if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
ICMP6_STATS_PTR_BH(Icmp6OutDestUnreachs) [type-ICMPV6_DEST_UNREACH]++;
ICMP6_INC_STATS_BH(Icmp6OutMsgs);
ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6OutDestUnreachs, type - ICMPV6_DEST_UNREACH);
ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
out:
icmpv6_xmit_unlock();
}
......@@ -363,6 +371,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
static void icmpv6_echo_reply(struct sk_buff *skb)
{
struct sock *sk = icmpv6_socket->sk;
struct inet6_dev *idev;
struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
struct in6_addr *saddr;
struct icmpv6_msg msg;
......@@ -394,14 +403,19 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
fl.fl_icmp_type = ICMPV6_ECHO_REPLY;
fl.fl_icmp_code = 0;
idev = in6_dev_get(skb->dev);
icmpv6_xmit_lock();
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1,
MSG_DONTWAIT);
ICMP6_INC_STATS_BH(Icmp6OutEchoReplies);
ICMP6_INC_STATS_BH(Icmp6OutMsgs);
ICMP6_INC_STATS_BH(idev, Icmp6OutEchoReplies);
ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs);
icmpv6_xmit_unlock();
if (likely(idev != NULL))
in6_dev_put(idev);
}
static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info)
......@@ -464,12 +478,13 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
{
struct sk_buff *skb = *pskb;
struct net_device *dev = skb->dev;
struct inet6_dev *idev = __in6_dev_get(dev);
struct in6_addr *saddr, *daddr;
struct ipv6hdr *orig_hdr;
struct icmp6hdr *hdr;
int type;
ICMP6_INC_STATS_BH(Icmp6InMsgs);
ICMP6_INC_STATS_BH(idev, Icmp6InMsgs);
saddr = &skb->nh.ipv6h->saddr;
daddr = &skb->nh.ipv6h->daddr;
......@@ -517,9 +532,9 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
type = hdr->icmp6_type;
if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
ICMP6_STATS_PTR_BH(Icmp6InDestUnreachs)[type-ICMPV6_DEST_UNREACH]++;
ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6InDestUnreachs, type - ICMPV6_DEST_UNREACH);
else if (type >= ICMPV6_ECHO_REQUEST && type <= NDISC_REDIRECT)
ICMP6_STATS_PTR_BH(Icmp6InEchos)[type-ICMPV6_ECHO_REQUEST]++;
ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6InEchos, type - ICMPV6_ECHO_REQUEST);
switch (type) {
case ICMPV6_ECHO_REQUEST:
......@@ -597,7 +612,7 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
return 0;
discard_it:
ICMP6_INC_STATS_BH(Icmp6InErrors);
ICMP6_INC_STATS_BH(idev, Icmp6InErrors);
kfree_skb(skb);
return 0;
}
......
......@@ -34,5 +34,6 @@ EXPORT_SYMBOL(ipv6_get_saddr);
EXPORT_SYMBOL(ipv6_chk_addr);
EXPORT_SYMBOL(in6addr_any);
EXPORT_SYMBOL(in6addr_loopback);
EXPORT_SYMBOL(in6_dev_finish_destroy);
EXPORT_SYMBOL(xfrm6_rcv);
EXPORT_SYMBOL(xfrm6_clear_mutable_options);
......@@ -1253,6 +1253,7 @@ static void mld_sendpack(struct sk_buff *skb)
struct ipv6hdr *pip6 = skb->nh.ipv6h;
struct mld2_report *pmr = (struct mld2_report *)skb->h.raw;
int payload_len, mldlen;
struct inet6_dev *idev = in6_dev_get(skb->dev);
payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h -
sizeof(struct ipv6hdr);
......@@ -1262,7 +1263,9 @@ static void mld_sendpack(struct sk_buff *skb)
pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,
IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0));
dev_queue_xmit(skb);
ICMP6_INC_STATS(Icmp6OutMsgs);
ICMP6_INC_STATS(idev,Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
}
static int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel)
......@@ -1520,6 +1523,7 @@ static void mld_send_cr(struct inet6_dev *idev)
static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
{
struct sock *sk = igmp6_socket->sk;
struct inet6_dev *idev;
struct sk_buff *skb;
struct icmp6hdr *hdr;
struct in6_addr *snd_addr;
......@@ -1577,12 +1581,17 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
IPPROTO_ICMPV6,
csum_partial((__u8 *) hdr, len, 0));
idev = in6_dev_get(skb->dev);
dev_queue_xmit(skb);
if (type == ICMPV6_MGM_REDUCTION)
ICMP6_INC_STATS(Icmp6OutGroupMembReductions);
ICMP6_INC_STATS(idev, Icmp6OutGroupMembReductions);
else
ICMP6_INC_STATS(Icmp6OutGroupMembResponses);
ICMP6_INC_STATS(Icmp6OutMsgs);
ICMP6_INC_STATS(idev, Icmp6OutGroupMembResponses);
ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
return;
out:
......
......@@ -415,6 +415,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
{
static struct in6_addr tmpaddr;
struct inet6_ifaddr *ifp;
struct inet6_dev *idev;
struct flowi fl;
struct rt6_info *rt = NULL;
struct dst_entry* dst;
......@@ -497,10 +498,14 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
dst_clone(dst);
skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb);
ICMP6_INC_STATS(Icmp6OutNeighborAdvertisements);
ICMP6_INC_STATS(Icmp6OutMsgs);
ICMP6_INC_STATS(idev, Icmp6OutNeighborAdvertisements);
ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
}
void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
......@@ -510,6 +515,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
struct flowi fl;
struct rt6_info *rt = NULL;
struct dst_entry* dst;
struct inet6_dev *idev;
struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct nd_msg *msg;
......@@ -576,10 +582,14 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
/* send it! */
dst_clone(dst);
skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb);
ICMP6_INC_STATS(Icmp6OutNeighborSolicits);
ICMP6_INC_STATS(Icmp6OutMsgs);
ICMP6_INC_STATS(idev, Icmp6OutNeighborSolicits);
ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
}
void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
......@@ -588,6 +598,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
struct flowi fl;
struct rt6_info *rt = NULL;
struct dst_entry* dst;
struct inet6_dev *idev;
struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct icmp6hdr *hdr;
......@@ -644,10 +655,14 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
/* send it! */
dst_clone(dst);
skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb);
ICMP6_INC_STATS(Icmp6OutRouterSolicits);
ICMP6_INC_STATS(Icmp6OutMsgs);
ICMP6_INC_STATS(idev, Icmp6OutRouterSolicits);
ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
}
......@@ -1271,6 +1286,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
struct net_device *dev;
struct rt6_info *rt;
struct dst_entry *dst;
struct inet6_dev *idev;
struct flowi fl;
u8 *opt;
int rd_len;
......@@ -1379,10 +1395,14 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
csum_partial((u8 *) icmph, len, 0));
skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb);
ICMP6_INC_STATS(Icmp6OutRedirects);
ICMP6_INC_STATS(Icmp6OutMsgs);
ICMP6_INC_STATS(idev, Icmp6OutRedirects);
ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
}
static void pndisc_redo(struct sk_buff *skb)
......
......@@ -10,12 +10,14 @@
* Version: $Id: proc.c,v 1.17 2002/02/01 22:01:04 davem Exp $
*
* Authors: David S. Miller (davem@caip.rutgers.edu)
* YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/socket.h>
#include <linux/net.h>
......@@ -28,6 +30,10 @@
#include <net/transp_v6.h>
#include <net/ipv6.h>
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_net_devsnmp6;
#endif
static int fold_prot_inuse(struct proto *proto)
{
int res = 0;
......@@ -53,14 +59,16 @@ static int sockstat6_seq_show(struct seq_file *seq, void *v)
}
static struct snmp6_item
struct snmp6_item
{
char *name;
void **mib;
int offset;
} snmp6_list[] = {
};
#define SNMP6_SENTINEL { .name = NULL, .offset = 0 }
static struct snmp6_item snmp6_ipv6_list[] = {
/* ipv6 mib according to draft-ietf-ipngwg-ipv6-mib-04 */
#define SNMP6_GEN(x) { #x , (void **)ipv6_statistics, offsetof(struct ipv6_mib, x) }
#define SNMP6_GEN(x) { .name = #x , .offset = offsetof(struct ipv6_mib, x) }
SNMP6_GEN(Ip6InReceives),
SNMP6_GEN(Ip6InHdrErrors),
SNMP6_GEN(Ip6InTooBigErrors),
......@@ -84,6 +92,10 @@ static struct snmp6_item
SNMP6_GEN(Ip6InMcastPkts),
SNMP6_GEN(Ip6OutMcastPkts),
#undef SNMP6_GEN
SNMP6_SENTINEL
};
static struct snmp6_item snmp6_icmp6_list[] = {
/* icmpv6 mib according to draft-ietf-ipngwg-ipv6-icmp-mib-02
Exceptions: {In|Out}AdminProhibs are removed, because I see
......@@ -94,7 +106,7 @@ static struct snmp6_item
OutRouterAdvertisements too.
OutGroupMembQueries too.
*/
#define SNMP6_GEN(x) { #x , (void **)icmpv6_statistics, offsetof(struct icmpv6_mib, x) }
#define SNMP6_GEN(x) { .name = #x , .offset = offsetof(struct icmpv6_mib, x) }
SNMP6_GEN(Icmp6InMsgs),
SNMP6_GEN(Icmp6InErrors),
SNMP6_GEN(Icmp6InDestUnreachs),
......@@ -124,12 +136,17 @@ static struct snmp6_item
SNMP6_GEN(Icmp6OutGroupMembResponses),
SNMP6_GEN(Icmp6OutGroupMembReductions),
#undef SNMP6_GEN
#define SNMP6_GEN(x) { "Udp6" #x , (void **)udp_stats_in6, offsetof(struct udp_mib, Udp##x) }
SNMP6_SENTINEL
};
static struct snmp6_item snmp6_udp6_list[] = {
#define SNMP6_GEN(x) { .name = "Udp6" #x , .offset = offsetof(struct udp_mib, Udp##x) }
SNMP6_GEN(InDatagrams),
SNMP6_GEN(NoPorts),
SNMP6_GEN(InErrors),
SNMP6_GEN(OutDatagrams)
SNMP6_GEN(OutDatagrams),
#undef SNMP6_GEN
SNMP6_SENTINEL
};
static unsigned long
......@@ -151,18 +168,30 @@ fold_field(void *mib[], int offt)
return res;
}
static int snmp6_seq_show(struct seq_file *seq, void *v)
static inline void
snmp6_seq_show_item(struct seq_file *seq, void **mib, struct snmp6_item *itemlist)
{
int i;
for (i=0; itemlist[i].name; i++)
seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name,
fold_field(mib, itemlist[i].offset));
}
for (i=0; i<sizeof(snmp6_list)/sizeof(snmp6_list[0]); i++)
seq_printf(seq, "%-32s\t%lu\n", snmp6_list[i].name,
fold_field(snmp6_list[i].mib, snmp6_list[i].offset));
static int snmp6_seq_show(struct seq_file *seq, void *v)
{
struct inet6_dev *idev = (struct inet6_dev *)seq->private;
if (idev) {
seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex);
snmp6_seq_show_item(seq, (void **)idev->stats.icmpv6, snmp6_icmp6_list);
} else {
snmp6_seq_show_item(seq, (void **)ipv6_statistics, snmp6_ipv6_list);
snmp6_seq_show_item(seq, (void **)icmpv6_statistics, snmp6_icmp6_list);
snmp6_seq_show_item(seq, (void **)udp_stats_in6, snmp6_udp6_list);
}
return 0;
}
static int sockstat6_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, sockstat6_seq_show, NULL);
......@@ -177,7 +206,7 @@ static struct file_operations sockstat6_seq_fops = {
static int snmp6_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, snmp6_seq_show, NULL);
return single_open(file, snmp6_seq_show, PDE(inode)->data);
}
static struct file_operations snmp6_seq_fops = {
......@@ -187,6 +216,57 @@ static struct file_operations snmp6_seq_fops = {
.release = single_release,
};
int snmp6_register_dev(struct inet6_dev *idev)
{
int err = -ENOMEM;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *p;
#endif
if (!idev || !idev->dev)
return -EINVAL;
if (snmp6_mib_init((void **)idev->stats.icmpv6, sizeof(struct icmpv6_mib)) < 0)
goto err_icmp;
#ifdef CONFIG_PROC_FS
if (!proc_net_devsnmp6) {
err = -ENOENT;
goto err_proc;
}
p = create_proc_entry(idev->dev->name, S_IRUGO, proc_net_devsnmp6);
if (!p)
goto err_proc;
p->data = idev;
p->proc_fops = &snmp6_seq_fops;
idev->stats.proc_dir_entry = p;
#endif
return 0;
#ifdef CONFIG_PROC_FS
err_proc:
snmp6_mib_free((void **)idev->stats.icmpv6);
#endif
err_icmp:
return err;
}
int snmp6_unregister_dev(struct inet6_dev *idev)
{
#ifdef CONFIG_PROC_FS
if (!proc_net_devsnmp6)
return -ENOENT;
if (!idev || !idev->stats.proc_dir_entry)
return -EINVAL;
remove_proc_entry(idev->stats.proc_dir_entry->name,
proc_net_devsnmp6);
#endif
snmp6_mib_free((void **)idev->stats.icmpv6);
return 0;
}
int __init ipv6_misc_proc_init(void)
{
int rc = 0;
......@@ -197,6 +277,9 @@ int __init ipv6_misc_proc_init(void)
goto proc_snmp6_fail;
else
p->proc_fops = &snmp6_seq_fops;
proc_net_devsnmp6 = proc_mkdir("dev_snmp6", proc_net);
if (!proc_net_devsnmp6)
goto proc_dev_snmp6_fail;
p = create_proc_entry("sockstat6", S_IRUGO, proc_net);
if (!p)
goto proc_sockstat6_fail;
......@@ -206,6 +289,8 @@ int __init ipv6_misc_proc_init(void)
return rc;
proc_sockstat6_fail:
remove_proc_entry("dev_snmp6", proc_net);
proc_dev_snmp6_fail:
remove_proc_entry("snmp6", proc_net);
proc_snmp6_fail:
rc = -ENOMEM;
......
......@@ -751,7 +751,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
sk = tcp_v6_lookup(&hdr->daddr, th->dest, &hdr->saddr, th->source, skb->dev->ifindex);
if (sk == NULL) {
ICMP6_INC_STATS_BH(Icmp6InErrors);
ICMP6_INC_STATS_BH(__in6_dev_get(skb->dev), Icmp6InErrors);
return;
}
......
......@@ -92,6 +92,7 @@ extern struct notifier_block sctp_inetaddr_notifier;
void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int type, int code, int offset, __u32 info)
{
struct inet6_dev *idev;
struct ipv6hdr *iph = (struct ipv6hdr *)skb->data;
struct sctphdr *sh = (struct sctphdr *)(skb->data + offset);
struct sock *sk;
......@@ -102,6 +103,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
char *saveip, *savesctp;
int err;
idev = in6_dev_get(skb->dev);
/* Fix up skb to look at the embedded net header. */
saveip = skb->nh.raw;
savesctp = skb->h.raw;
......@@ -112,8 +115,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
skb->nh.raw = saveip;
skb->h.raw = savesctp;
if (!sk) {
ICMP6_INC_STATS_BH(Icmp6InErrors);
return;
ICMP6_INC_STATS_BH(idev, Icmp6InErrors);
goto out;
}
/* Warning: The sock lock is held. Remember to call
......@@ -139,6 +142,9 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
out_unlock:
sctp_err_finish(sk, ep, asoc);
out:
if (likely(idev != NULL))
in6_dev_put(idev);
}
/* Based on tcp_v6_xmit() in tcp_ipv6.c. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment