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/ ...@@ -505,6 +505,17 @@ W: http://www.qsl.net/dl1bke/
L: linux-hams@vger.kernel.org L: linux-hams@vger.kernel.org
S: Maintained 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 DC390/AM53C974 SCSI driver
P: Kurt Garloff P: Kurt Garloff
M: garloff@suse.de M: garloff@suse.de
......
...@@ -414,7 +414,7 @@ static int i810_dma_initialize(drm_device_t *dev, ...@@ -414,7 +414,7 @@ static int i810_dma_initialize(drm_device_t *dev,
return -ENOMEM; return -ENOMEM;
} }
memset(dev_priv->hw_status_page, 0, PAGE_SIZE); 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); I810_WRITE(0x02080, dev_priv->dma_status_page);
DRM_DEBUG("Enabled hardware status page\n"); DRM_DEBUG("Enabled hardware status page\n");
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/fs.h>
#include <asm/oplib.h> #include <asm/oplib.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
......
...@@ -1390,6 +1390,23 @@ config SCSI_SYM53C416 ...@@ -1390,6 +1390,23 @@ config SCSI_SYM53C416
read <file:Documentation/modules.txt>. The module will be called read <file:Documentation/modules.txt>. The module will be called
sym53c416. 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 config SCSI_DC390T
tristate "Tekram DC390(T) and Am53/79C974 SCSI support" tristate "Tekram DC390(T) and Am53/79C974 SCSI support"
depends on PCI && SCSI depends on PCI && SCSI
......
...@@ -88,6 +88,7 @@ obj-$(CONFIG_SCSI_7000FASST) += wd7000.o ...@@ -88,6 +88,7 @@ obj-$(CONFIG_SCSI_7000FASST) += wd7000.o
obj-$(CONFIG_SCSI_MCA_53C9X) += NCR53C9x.o mca_53c9x.o obj-$(CONFIG_SCSI_MCA_53C9X) += NCR53C9x.o mca_53c9x.o
obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o
obj-$(CONFIG_SCSI_EATA) += eata.o obj-$(CONFIG_SCSI_EATA) += eata.o
obj-$(CONFIG_SCSI_DC395x) += dc395x.o
obj-$(CONFIG_SCSI_DC390T) += tmscsim.o obj-$(CONFIG_SCSI_DC390T) += tmscsim.o
obj-$(CONFIG_SCSI_AM53C974) += AM53C974.o obj-$(CONFIG_SCSI_AM53C974) += AM53C974.o
obj-$(CONFIG_SCSI_MEGARAID) += megaraid.o obj-$(CONFIG_SCSI_MEGARAID) += megaraid.o
......
This source diff could not be displayed because it is too large. You can view the blob instead.
/************************************************************************/
/* */
/* dc395x.h */
/* */
/* Device Driver for Tekram DC395(U/UW/F), DC315(U) */
/* PCI SCSI Bus Master Host Adapter */
/* (SCSI chip set used Tekram ASIC TRM-S1040) */
/* */
/************************************************************************/
#ifndef DC395x_H
#define DC395x_H
/************************************************************************/
/* */
/* Name, Banner and Version */
/* */
/************************************************************************/
#define DC395X_NAME "dc395x"
#define DC395X_BANNER "Tekram DC395(U/UW/F), DC315(U) - ASIC TRM-S1040"
#define DC395X_VERSION "v2.02, 2003/04/20"
/************************************************************************/
/* */
/* Initial values */
/* */
/************************************************************************/
#define DC395x_MAX_CMD_QUEUE 32
/* #define DC395x_MAX_QTAGS 32 */
#define DC395x_MAX_QTAGS 16
#define DC395x_MAX_ADAPTER_NUM 4
#define DC395x_MAX_SCSI_ID 16
#define DC395x_MAX_CMD_PER_LUN DC395x_MAX_QTAGS
#define DC395x_MAX_SG_TABLESIZE 64 /* HW limitation */
#define DC395x_MAX_SG_LISTENTRY 64 /* Must be equal or lower to previous */
/* item */
#define DC395x_MAX_SRB_CNT 63
/* #define DC395x_MAX_CAN_QUEUE 7 * DC395x_MAX_QTAGS */
#define DC395x_MAX_CAN_QUEUE DC395x_MAX_SRB_CNT
#define DC395x_END_SCAN 2
#define DC395x_SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */
#define DC395x_MAX_RETRIES 3
#if 0
#define SYNC_FIRST
#endif
#define NORM_REC_LVL 0
/************************************************************************/
/* */
/* Various definitions */
/* */
/************************************************************************/
#define BIT31 0x80000000
#define BIT30 0x40000000
#define BIT29 0x20000000
#define BIT28 0x10000000
#define BIT27 0x08000000
#define BIT26 0x04000000
#define BIT25 0x02000000
#define BIT24 0x01000000
#define BIT23 0x00800000
#define BIT22 0x00400000
#define BIT21 0x00200000
#define BIT20 0x00100000
#define BIT19 0x00080000
#define BIT18 0x00040000
#define BIT17 0x00020000
#define BIT16 0x00010000
#define BIT15 0x00008000
#define BIT14 0x00004000
#define BIT13 0x00002000
#define BIT12 0x00001000
#define BIT11 0x00000800
#define BIT10 0x00000400
#define BIT9 0x00000200
#define BIT8 0x00000100
#define BIT7 0x00000080
#define BIT6 0x00000040
#define BIT5 0x00000020
#define BIT4 0x00000010
#define BIT3 0x00000008
#define BIT2 0x00000004
#define BIT1 0x00000002
#define BIT0 0x00000001
/* UnitCtrlFlag */
#define UNIT_ALLOCATED BIT0
#define UNIT_INFO_CHANGED BIT1
#define FORMATING_MEDIA BIT2
#define UNIT_RETRY BIT3
/* UnitFlags */
#define DASD_SUPPORT BIT0
#define SCSI_SUPPORT BIT1
#define ASPI_SUPPORT BIT2
/* SRBState machine definition */
#define SRB_FREE 0x0000
#define SRB_WAIT 0x0001
#define SRB_READY 0x0002
#define SRB_MSGOUT 0x0004 /* arbitration+msg_out 1st byte */
#define SRB_MSGIN 0x0008
#define SRB_EXTEND_MSGIN 0x0010
#define SRB_COMMAND 0x0020
#define SRB_START_ 0x0040 /* arbitration+msg_out+command_out */
#define SRB_DISCONNECT 0x0080
#define SRB_DATA_XFER 0x0100
#define SRB_XFERPAD 0x0200
#define SRB_STATUS 0x0400
#define SRB_COMPLETED 0x0800
#define SRB_ABORT_SENT 0x1000
#define SRB_DO_SYNC_NEGO 0x2000
#define SRB_DO_WIDE_NEGO 0x4000
#define SRB_UNEXPECT_RESEL 0x8000
/************************************************************************/
/* */
/* ACB Config */
/* */
/************************************************************************/
#define HCC_WIDE_CARD 0x20
#define HCC_SCSI_RESET 0x10
#define HCC_PARITY 0x08
#define HCC_AUTOTERM 0x04
#define HCC_LOW8TERM 0x02
#define HCC_UP8TERM 0x01
/* ACBFlag */
#define RESET_DEV BIT0
#define RESET_DETECT BIT1
#define RESET_DONE BIT2
/* DCBFlag */
#define ABORT_DEV_ BIT0
/* SRBstatus */
#define SRB_OK BIT0
#define ABORTION BIT1
#define OVER_RUN BIT2
#define UNDER_RUN BIT3
#define PARITY_ERROR BIT4
#define SRB_ERROR BIT5
/* SRBFlag */
#define DATAOUT BIT7
#define DATAIN BIT6
#define RESIDUAL_VALID BIT5
#define ENABLE_TIMER BIT4
#define RESET_DEV0 BIT2
#define ABORT_DEV BIT1
#define AUTO_REQSENSE BIT0
/* Adapter status */
#define H_STATUS_GOOD 0
#define H_SEL_TIMEOUT 0x11
#define H_OVER_UNDER_RUN 0x12
#define H_UNEXP_BUS_FREE 0x13
#define H_TARGET_PHASE_F 0x14
#define H_INVALID_CCB_OP 0x16
#define H_LINK_CCB_BAD 0x17
#define H_BAD_TARGET_DIR 0x18
#define H_DUPLICATE_CCB 0x19
#define H_BAD_CCB_OR_SG 0x1A
#define H_ABORT 0x0FF
/* SCSI BUS Status byte codes */
#define SCSI_STAT_GOOD 0x0 /* Good status */
#define SCSI_STAT_CHECKCOND 0x02 /* SCSI Check Condition */
#define SCSI_STAT_CONDMET 0x04 /* Condition Met */
#define SCSI_STAT_BUSY 0x08 /* Target busy status */
#define SCSI_STAT_INTER 0x10 /* Intermediate status */
#define SCSI_STAT_INTERCONDMET 0x14 /* Intermediate condition met */
#define SCSI_STAT_RESCONFLICT 0x18 /* Reservation conflict */
#define SCSI_STAT_CMDTERM 0x22 /* Command Terminated */
#define SCSI_STAT_QUEUEFULL 0x28 /* Queue Full */
#define SCSI_STAT_UNEXP_BUS_F 0xFD /* Unexpect Bus Free */
#define SCSI_STAT_BUS_RST_DETECT 0xFE /* Scsi Bus Reset detected */
#define SCSI_STAT_SEL_TIMEOUT 0xFF /* Selection Time out */
/* Sync_Mode */
#define SYNC_WIDE_TAG_ATNT_DISABLE 0
#define SYNC_NEGO_ENABLE BIT0
#define SYNC_NEGO_DONE BIT1
#define WIDE_NEGO_ENABLE BIT2
#define WIDE_NEGO_DONE BIT3
#define WIDE_NEGO_STATE BIT4
#define EN_TAG_QUEUEING BIT5
#define EN_ATN_STOP BIT6
#define SYNC_NEGO_OFFSET 15
/* SCSI MSG BYTE */
#define MSG_COMPLETE 0x00
#define MSG_EXTENDED 0x01
#define MSG_SAVE_PTR 0x02
#define MSG_RESTORE_PTR 0x03
#define MSG_DISCONNECT 0x04
#define MSG_INITIATOR_ERROR 0x05
#define MSG_ABORT 0x06
#define MSG_REJECT_ 0x07
#define MSG_NOP 0x08
#define MSG_PARITY_ERROR 0x09
#define MSG_LINK_CMD_COMPL 0x0A
#define MSG_LINK_CMD_COMPL_FLG 0x0B
#define MSG_BUS_RESET 0x0C
#define MSG_ABORT_TAG 0x0D
#define MSG_SIMPLE_QTAG 0x20
#define MSG_HEAD_QTAG 0x21
#define MSG_ORDER_QTAG 0x22
#define MSG_IGNOREWIDE 0x23
#define MSG_IDENTIFY 0x80
#define MSG_HOST_ID 0xC0
/* SCSI STATUS BYTE */
#define STATUS_GOOD 0x00
#define CHECK_CONDITION_ 0x02
#define STATUS_BUSY 0x08
#define STATUS_INTERMEDIATE 0x10
#define RESERVE_CONFLICT 0x18
/* cmd->result */
#define STATUS_MASK_ 0xFF
#define MSG_MASK 0xFF00
#define RETURN_MASK 0xFF0000
/************************************************************************/
/* */
/* Inquiry Data format */
/* */
/************************************************************************/
struct ScsiInqData
{ /* INQ */
u8 DevType; /* Periph Qualifier & Periph Dev Type */
u8 RMB_TypeMod; /* rem media bit & Dev Type Modifier */
u8 Vers; /* ISO, ECMA, & ANSI versions */
u8 RDF; /* AEN, TRMIOP, & response data format */
u8 AddLen; /* length of additional data */
u8 Res1; /* reserved */
u8 Res2; /* reserved */
u8 Flags; /* RelADr, Wbus32, Wbus16, Sync, etc. */
u8 VendorID[8]; /* Vendor Identification */
u8 ProductID[16]; /* Product Identification */
u8 ProductRev[4]; /* Product Revision */
};
/* Inquiry byte 0 masks */
#define SCSI_DEVTYPE 0x1F /* Peripheral Device Type */
#define SCSI_PERIPHQUAL 0xE0 /* Peripheral Qualifier */
/* Inquiry byte 1 mask */
#define SCSI_REMOVABLE_MEDIA 0x80 /* Removable Media bit (1=removable) */
/* Peripheral Device Type definitions */
/* See include/scsi/scsi.h */
#define TYPE_NODEV SCSI_DEVTYPE /* Unknown or no device type */
#ifndef TYPE_PRINTER /* */
# define TYPE_PRINTER 0x02 /* Printer device */
#endif /* */
#ifndef TYPE_COMM /* */
# define TYPE_COMM 0x09 /* Communications device */
#endif
/************************************************************************/
/* */
/* Inquiry flag definitions (Inq data byte 7) */
/* */
/************************************************************************/
#define SCSI_INQ_RELADR 0x80 /* device supports relative addressing */
#define SCSI_INQ_WBUS32 0x40 /* device supports 32 bit data xfers */
#define SCSI_INQ_WBUS16 0x20 /* device supports 16 bit data xfers */
#define SCSI_INQ_SYNC 0x10 /* device supports synchronous xfer */
#define SCSI_INQ_LINKED 0x08 /* device supports linked commands */
#define SCSI_INQ_CMDQUEUE 0x02 /* device supports command queueing */
#define SCSI_INQ_SFTRE 0x01 /* device supports soft resets */
#define ENABLE_CE 1
#define DISABLE_CE 0
#define EEPROM_READ 0x80
/************************************************************************/
/* */
/* The PCI configuration register offset for TRM_S1040 */
/* */
/************************************************************************/
#define TRM_S1040_ID 0x00 /* Vendor and Device ID */
#define TRM_S1040_COMMAND 0x04 /* PCI command register */
#define TRM_S1040_IOBASE 0x10 /* I/O Space base address */
#define TRM_S1040_ROMBASE 0x30 /* Expansion ROM Base Address */
#define TRM_S1040_INTLINE 0x3C /* Interrupt line */
/************************************************************************/
/* */
/* The SCSI register offset for TRM_S1040 */
/* */
/************************************************************************/
#define TRM_S1040_SCSI_STATUS 0x80 /* SCSI Status (R) */
#define COMMANDPHASEDONE 0x2000 /* SCSI command phase done */
#define SCSIXFERDONE 0x0800 /* SCSI SCSI transfer done */
#define SCSIXFERCNT_2_ZERO 0x0100 /* SCSI SCSI transfer count to zero */
#define SCSIINTERRUPT 0x0080 /* SCSI interrupt pending */
#define COMMANDABORT 0x0040 /* SCSI command abort */
#define SEQUENCERACTIVE 0x0020 /* SCSI sequencer active */
#define PHASEMISMATCH 0x0010 /* SCSI phase mismatch */
#define PARITYERROR 0x0008 /* SCSI parity error */
#define PHASEMASK 0x0007 /* Phase MSG/CD/IO */
#define PH_DATA_OUT 0x00 /* Data out phase */
#define PH_DATA_IN 0x01 /* Data in phase */
#define PH_COMMAND 0x02 /* Command phase */
#define PH_STATUS 0x03 /* Status phase */
#define PH_BUS_FREE 0x05 /* Invalid phase used as bus free */
#define PH_MSG_OUT 0x06 /* Message out phase */
#define PH_MSG_IN 0x07 /* Message in phase */
#define TRM_S1040_SCSI_CONTROL 0x80 /* SCSI Control (W) */
#define DO_CLRATN 0x0400 /* Clear ATN */
#define DO_SETATN 0x0200 /* Set ATN */
#define DO_CMDABORT 0x0100 /* Abort SCSI command */
#define DO_RSTMODULE 0x0010 /* Reset SCSI chip */
#define DO_RSTSCSI 0x0008 /* Reset SCSI bus */
#define DO_CLRFIFO 0x0004 /* Clear SCSI transfer FIFO */
#define DO_DATALATCH 0x0002 /* Enable SCSI bus data input (latched) */
/* #define DO_DATALATCH 0x0000 */ /* KG: DISable SCSI bus data latch */
#define DO_HWRESELECT 0x0001 /* Enable hardware reselection */
#define TRM_S1040_SCSI_FIFOCNT 0x82 /* SCSI FIFO Counter 5bits(R) */
#define TRM_S1040_SCSI_SIGNAL 0x83 /* SCSI low level signal (R/W) */
#define TRM_S1040_SCSI_INTSTATUS 0x84 /* SCSI Interrupt Status (R) */
#define INT_SCAM 0x80 /* SCAM selection interrupt */
#define INT_SELECT 0x40 /* Selection interrupt */
#define INT_SELTIMEOUT 0x20 /* Selection timeout interrupt */
#define INT_DISCONNECT 0x10 /* Bus disconnected interrupt */
#define INT_RESELECTED 0x08 /* Reselected interrupt */
#define INT_SCSIRESET 0x04 /* SCSI reset detected interrupt */
#define INT_BUSSERVICE 0x02 /* Bus service interrupt */
#define INT_CMDDONE 0x01 /* SCSI command done interrupt */
#define TRM_S1040_SCSI_OFFSET 0x84 /* SCSI Offset Count (W) */
/************************************************************************/
/* */
/* Bit Name Definition */
/* --------- ------------- ---------------------------- */
/* 07-05 0 RSVD Reversed. Always 0. */
/* 04 0 OFFSET4 Reversed for LVDS. Always 0. */
/* 03-00 0 OFFSET[03:00] Offset number from 0 to 15 */
/* */
/************************************************************************/
#define TRM_S1040_SCSI_SYNC 0x85 /* SCSI Synchronous Control (R/W) */
#define LVDS_SYNC 0x20 /* Enable LVDS synchronous */
#define WIDE_SYNC 0x10 /* Enable WIDE synchronous */
#define ALT_SYNC 0x08 /* Enable Fast-20 alternate synchronous */
/************************************************************************/
/* */
/* SYNCM 7 6 5 4 3 2 1 0 */
/* Name RSVD RSVD LVDS WIDE ALTPERD PERIOD2 PERIOD1 PERIOD0 */
/* Default 0 0 0 0 0 0 0 0 */
/* */
/* Bit Name Definition */
/* --------- ------------- --------------------------- */
/* 07-06 0 RSVD Reversed. Always read 0 */
/* 05 0 LVDS Reversed. Always read 0 */
/* 04 0 WIDE/WSCSI Enable wide (16-bits) SCSI */
/* transfer. */
/* 03 0 ALTPERD/ALTPD Alternate (Sync./Period) mode. */
/* */
/* @@ When this bit is set, */
/* the synchronous period bits 2:0 */
/* in the Synchronous Mode register */
/* are used to transfer data */
/* at the Fast-20 rate. */
/* @@ When this bit is unset, */
/* the synchronous period bits 2:0 */
/* in the Synchronous Mode Register */
/* are used to transfer data */
/* at the Fast-10 rate (or Fast-40 w/ LVDS). */
/* */
/* 02-00 0 PERIOD[2:0]/ Synchronous SCSI Transfer Rate. */
/* SXPD[02:00] These 3 bits specify */
/* the Synchronous SCSI Transfer */
/* Rate for Fast-20 and Fast-10. */
/* These bits are also reset */
/* by a SCSI Bus reset. */
/* */
/* For Fast-10 bit ALTPD = 0 and LVDS = 0 */
/* and bit2,bit1,bit0 is defined as follows : */
/* */
/* 000 100ns, 10.0 MHz */
/* 001 150ns, 6.6 MHz */
/* 010 200ns, 5.0 MHz */
/* 011 250ns, 4.0 MHz */
/* 100 300ns, 3.3 MHz */
/* 101 350ns, 2.8 MHz */
/* 110 400ns, 2.5 MHz */
/* 111 450ns, 2.2 MHz */
/* */
/* For Fast-20 bit ALTPD = 1 and LVDS = 0 */
/* and bit2,bit1,bit0 is defined as follows : */
/* */
/* 000 50ns, 20.0 MHz */
/* 001 75ns, 13.3 MHz */
/* 010 100ns, 10.0 MHz */
/* 011 125ns, 8.0 MHz */
/* 100 150ns, 6.6 MHz */
/* 101 175ns, 5.7 MHz */
/* 110 200ns, 5.0 MHz */
/* 111 250ns, 4.0 MHz KG: Maybe 225ns, 4.4 MHz */
/* */
/* For Fast-40 bit ALTPD = 0 and LVDS = 1 */
/* and bit2,bit1,bit0 is defined as follows : */
/* */
/* 000 25ns, 40.0 MHz */
/* 001 50ns, 20.0 MHz */
/* 010 75ns, 13.3 MHz */
/* 011 100ns, 10.0 MHz */
/* 100 125ns, 8.0 MHz */
/* 101 150ns, 6.6 MHz */
/* 110 175ns, 5.7 MHz */
/* 111 200ns, 5.0 MHz */
/* */
/************************************************************************/
#define TRM_S1040_SCSI_TARGETID 0x86 /* SCSI Target ID (R/W) */
#define TRM_S1040_SCSI_IDMSG 0x87 /* SCSI Identify Message (R) */
#define TRM_S1040_SCSI_HOSTID 0x87 /* SCSI Host ID (W) */
#define TRM_S1040_SCSI_COUNTER 0x88 /* SCSI Transfer Counter 24bits(R/W) */
#define TRM_S1040_SCSI_INTEN 0x8C /* SCSI Interrupt Enable (R/W) */
#define EN_SCAM 0x80 /* Enable SCAM selection interrupt */
#define EN_SELECT 0x40 /* Enable selection interrupt */
#define EN_SELTIMEOUT 0x20 /* Enable selection timeout interrupt */
#define EN_DISCONNECT 0x10 /* Enable bus disconnected interrupt */
#define EN_RESELECTED 0x08 /* Enable reselected interrupt */
#define EN_SCSIRESET 0x04 /* Enable SCSI reset detected interrupt */
#define EN_BUSSERVICE 0x02 /* Enable bus service interrupt */
#define EN_CMDDONE 0x01 /* Enable SCSI command done interrupt */
#define TRM_S1040_SCSI_CONFIG0 0x8D /* SCSI Configuration 0 (R/W) */
#define PHASELATCH 0x40 /* Enable phase latch */
#define INITIATOR 0x20 /* Enable initiator mode */
#define PARITYCHECK 0x10 /* Enable parity check */
#define BLOCKRST 0x01 /* Disable SCSI reset1 */
#define TRM_S1040_SCSI_CONFIG1 0x8E /* SCSI Configuration 1 (R/W) */
#define ACTIVE_NEGPLUS 0x10 /* Enhance active negation */
#define FILTER_DISABLE 0x08 /* Disable SCSI data filter */
#define FAST_FILTER 0x04 /* ? */
#define ACTIVE_NEG 0x02 /* Enable active negation */
#define TRM_S1040_SCSI_CONFIG2 0x8F /* SCSI Configuration 2 (R/W) */
#define CFG2_WIDEFIFO 0x02 /* */
#define TRM_S1040_SCSI_COMMAND 0x90 /* SCSI Command (R/W) */
#define SCMD_COMP 0x12 /* Command complete */
#define SCMD_SEL_ATN 0x60 /* Selection with ATN */
#define SCMD_SEL_ATN3 0x64 /* Selection with ATN3 */
#define SCMD_SEL_ATNSTOP 0xB8 /* Selection with ATN and Stop */
#define SCMD_FIFO_OUT 0xC0 /* SCSI FIFO transfer out */
#define SCMD_DMA_OUT 0xC1 /* SCSI DMA transfer out */
#define SCMD_FIFO_IN 0xC2 /* SCSI FIFO transfer in */
#define SCMD_DMA_IN 0xC3 /* SCSI DMA transfer in */
#define SCMD_MSGACCEPT 0xD8 /* Message accept */
/************************************************************************/
/* */
/* Code Command Description */
/* ---- ---------------------------------------- */
/* 02 Enable reselection with FIFO */
/* 40 Select without ATN with FIFO */
/* 60 Select with ATN with FIFO */
/* 64 Select with ATN3 with FIFO */
/* A0 Select with ATN and stop with FIFO */
/* C0 Transfer information out with FIFO */
/* C1 Transfer information out with DMA */
/* C2 Transfer information in with FIFO */
/* C3 Transfer information in with DMA */
/* 12 Initiator command complete with FIFO */
/* 50 Initiator transfer information out sequence without ATN */
/* with FIFO */
/* 70 Initiator transfer information out sequence with ATN */
/* with FIFO */
/* 74 Initiator transfer information out sequence with ATN3 */
/* with FIFO */
/* 52 Initiator transfer information in sequence without ATN */
/* with FIFO */
/* 72 Initiator transfer information in sequence with ATN */
/* with FIFO */
/* 76 Initiator transfer information in sequence with ATN3 */
/* with FIFO */
/* 90 Initiator transfer information out command complete */
/* with FIFO */
/* 92 Initiator transfer information in command complete */
/* with FIFO */
/* D2 Enable selection */
/* 08 Reselection */
/* 48 Disconnect command with FIFO */
/* 88 Terminate command with FIFO */
/* C8 Target command complete with FIFO */
/* 18 SCAM Arbitration/ Selection */
/* 5A Enable reselection */
/* 98 Select without ATN with FIFO */
/* B8 Select with ATN with FIFO */
/* D8 Message Accepted */
/* 58 NOP */
/* */
/************************************************************************/
#define TRM_S1040_SCSI_TIMEOUT 0x91 /* SCSI Time Out Value (R/W) */
#define TRM_S1040_SCSI_FIFO 0x98 /* SCSI FIFO (R/W) */
#define TRM_S1040_SCSI_TCR0 0x9C /* SCSI Target Control 0 (R/W) */
#define TCR0_WIDE_NEGO_DONE 0x8000 /* Wide nego done */
#define TCR0_SYNC_NEGO_DONE 0x4000 /* Synchronous nego done */
#define TCR0_ENABLE_LVDS 0x2000 /* Enable LVDS synchronous */
#define TCR0_ENABLE_WIDE 0x1000 /* Enable WIDE synchronous */
#define TCR0_ENABLE_ALT 0x0800 /* Enable alternate synchronous */
#define TCR0_PERIOD_MASK 0x0700 /* Transfer rate */
#define TCR0_DO_WIDE_NEGO 0x0080 /* Do wide NEGO */
#define TCR0_DO_SYNC_NEGO 0x0040 /* Do sync NEGO */
#define TCR0_DISCONNECT_EN 0x0020 /* Disconnection enable */
#define TCR0_OFFSET_MASK 0x001F /* Offset number */
#define TRM_S1040_SCSI_TCR1 0x9E /* SCSI Target Control 1 (R/W) */
#define MAXTAG_MASK 0x7F00 /* Maximum tags (127) */
#define NON_TAG_BUSY 0x0080 /* Non tag command active */
#define ACTTAG_MASK 0x007F /* Active tags */
/************************************************************************/
/* */
/* The DMA register offset for TRM_S1040 */
/* */
/************************************************************************/
#define TRM_S1040_DMA_COMMAND 0xA0 /* DMA Command (R/W) */
#define DMACMD_SG 0x02 /* Enable HW S/G support */
#define DMACMD_DIR 0x01 /* 1 = read from SCSI write to Host */
#define XFERDATAIN_SG 0x0103 /* Transfer data in w/ SG */
#define XFERDATAOUT_SG 0x0102 /* Transfer data out w/ SG */
#define XFERDATAIN 0x0101 /* Transfer data in w/o SG */
#define XFERDATAOUT 0x0100 /* Transfer data out w/o SG */
#define TRM_S1040_DMA_FIFOCNT 0xA1 /* DMA FIFO Counter (R) */
#define TRM_S1040_DMA_CONTROL 0xA1 /* DMA Control (W) */
#define DMARESETMODULE 0x10 /* Reset PCI/DMA module */
#define STOPDMAXFER 0x08 /* Stop DMA transfer */
#define ABORTXFER 0x04 /* Abort DMA transfer */
#define CLRXFIFO 0x02 /* Clear DMA transfer FIFO */
#define STARTDMAXFER 0x01 /* Start DMA transfer */
#define TRM_S1040_DMA_FIFOSTAT 0xA2 /* DMA FIFO Status (R) */
#define TRM_S1040_DMA_STATUS 0xA3 /* DMA Interrupt Status (R/W) */
#define XFERPENDING 0x80 /* Transfer pending */
#define SCSIBUSY 0x40 /* SCSI busy */
#define GLOBALINT 0x20 /* DMA_INTEN bit 0-4 set */
#define FORCEDMACOMP 0x10 /* Force DMA transfer complete */
#define DMAXFERERROR 0x08 /* DMA transfer error */
#define DMAXFERABORT 0x04 /* DMA transfer abort */
#define DMAXFERCOMP 0x02 /* Bus Master XFER Complete status */
#define SCSICOMP 0x01 /* SCSI complete interrupt */
#define TRM_S1040_DMA_INTEN 0xA4 /* DMA Interrupt Enable (R/W) */
#define EN_FORCEDMACOMP 0x10 /* Force DMA transfer complete */
#define EN_DMAXFERERROR 0x08 /* DMA transfer error */
#define EN_DMAXFERABORT 0x04 /* DMA transfer abort */
#define EN_DMAXFERCOMP 0x02 /* Bus Master XFER Complete status */
#define EN_SCSIINTR 0x01 /* Enable SCSI complete interrupt */
#define TRM_S1040_DMA_CONFIG 0xA6 /* DMA Configuration (R/W) */
#define DMA_ENHANCE 0x8000 /* Enable DMA enhance feature (SG?) */
#define DMA_PCI_DUAL_ADDR 0x4000 /* */
#define DMA_CFG_RES 0x2000 /* Always 1 */
#define DMA_AUTO_CLR_FIFO 0x1000 /* DISable DMA auto clear FIFO */
#define DMA_MEM_MULTI_READ 0x0800 /* */
#define DMA_MEM_WRITE_INVAL 0x0400 /* Memory write and invalidate */
#define DMA_FIFO_CTRL 0x0300 /* Control FIFO operation with DMA */
#define DMA_FIFO_HALF_HALF 0x0200 /* Keep half filled on both read/write */
#define TRM_S1040_DMA_XCNT 0xA8 /* DMA Transfer Counter (R/W), 24bits */
#define TRM_S1040_DMA_CXCNT 0xAC /* DMA Current Transfer Counter (R) */
#define TRM_S1040_DMA_XLOWADDR 0xB0 /* DMA Transfer Physical Low Address */
#define TRM_S1040_DMA_XHIGHADDR 0xB4 /* DMA Transfer Physical High Address */
/************************************************************************/
/* */
/* The general register offset for TRM_S1040 */
/* */
/************************************************************************/
#define TRM_S1040_GEN_CONTROL 0xD4 /* Global Control */
#define CTRL_LED 0x80 /* Control onboard LED */
#define EN_EEPROM 0x10 /* Enable EEPROM programming */
#define DIS_TERM 0x08 /* Disable onboard termination */
#define AUTOTERM 0x04 /* Enable Auto SCSI terminator */
#define LOW8TERM 0x02 /* Enable Lower 8 bit SCSI terminator */
#define UP8TERM 0x01 /* Enable Upper 8 bit SCSI terminator */
#define TRM_S1040_GEN_STATUS 0xD5 /* Global Status */
#define GTIMEOUT 0x80 /* Global timer reach 0 */
#define EXT68HIGH 0x40 /* Higher 8 bit connected externally */
#define INT68HIGH 0x20 /* Higher 8 bit connected internally */
#define CON5068 0x10 /* External 50/68 pin connected (low) */
#define CON68 0x08 /* Internal 68 pin connected (low) */
#define CON50 0x04 /* Internal 50 pin connected (low!) */
#define WIDESCSI 0x02 /* Wide SCSI card */
#define STATUS_LOAD_DEFAULT 0x01 /* */
#define TRM_S1040_GEN_NVRAM 0xD6 /* Serial NON-VOLATILE RAM port */
#define NVR_BITOUT 0x08 /* Serial data out */
#define NVR_BITIN 0x04 /* Serial data in */
#define NVR_CLOCK 0x02 /* Serial clock */
#define NVR_SELECT 0x01 /* Serial select */
#define TRM_S1040_GEN_EDATA 0xD7 /* Parallel EEPROM data port */
#define TRM_S1040_GEN_EADDRESS 0xD8 /* Parallel EEPROM address */
#define TRM_S1040_GEN_TIMER 0xDB /* Global timer */
/************************************************************************/
/* */
/* NvmTarCfg0: Target configuration byte 0 :..pDCB->DevMode */
/* */
/************************************************************************/
#define NTC_DO_WIDE_NEGO 0x20 /* Wide negotiate */
#define NTC_DO_TAG_QUEUEING 0x10 /* Enable SCSI tag queuing */
#define NTC_DO_SEND_START 0x08 /* Send start command SPINUP */
#define NTC_DO_DISCONNECT 0x04 /* Enable SCSI disconnect */
#define NTC_DO_SYNC_NEGO 0x02 /* Sync negotiation */
#define NTC_DO_PARITY_CHK 0x01 /* (it sould define at NAC) */
/* Parity check enable */
/************************************************************************/
/* */
/* Nvram Initiater bits definition */
/* */
/************************************************************************/
#if 0
#define MORE2_DRV BIT0
#define GREATER_1G BIT1
#define RST_SCSI_BUS BIT2
#define ACTIVE_NEGATION BIT3
#define NO_SEEK BIT4
#define LUN_CHECK BIT5
#endif
/************************************************************************/
/* */
/* Nvram Adapter Cfg bits definition */
/* */
/************************************************************************/
#define NAC_SCANLUN 0x20 /* Include LUN as BIOS device */
#define NAC_POWERON_SCSI_RESET 0x04 /* Power on reset enable */
#define NAC_GREATER_1G 0x02 /* > 1G support enable */
#define NAC_GT2DRIVES 0x01 /* Support more than 2 drives */
/* #define NAC_DO_PARITY_CHK 0x08 */ /* Parity check enable */
#endif
...@@ -320,6 +320,21 @@ static inline void list_splice_init(struct list_head *list, ...@@ -320,6 +320,21 @@ static inline void list_splice_init(struct list_head *list,
for (pos = (head)->next, n = pos->next; pos != (head); \ for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next) 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. * Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is * Mostly useful for hash tables where the two pointer list head is
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#ifndef _NET_IF_INET6_H #ifndef _NET_IF_INET6_H
#define _NET_IF_INET6_H #define _NET_IF_INET6_H
#include <net/snmp.h>
#define IF_RA_RCVD 0x20 #define IF_RA_RCVD 0x20
#define IF_RS_SENT 0x10 #define IF_RS_SENT 0x10
...@@ -152,6 +154,11 @@ struct ipv6_devconf ...@@ -152,6 +154,11 @@ struct ipv6_devconf
void *sysctl; void *sysctl;
}; };
struct ipv6_devstat {
struct proc_dir_entry *proc_dir_entry;
DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6);
};
struct inet6_dev struct inet6_dev
{ {
struct net_device *dev; struct net_device *dev;
...@@ -185,6 +192,7 @@ struct inet6_dev ...@@ -185,6 +192,7 @@ struct inet6_dev
struct neigh_parms *nd_parms; struct neigh_parms *nd_parms;
struct inet6_dev *next; struct inet6_dev *next;
struct ipv6_devconf cnf; struct ipv6_devconf cnf;
struct ipv6_devstat stats;
}; };
extern struct ipv6_devconf ipv6_devconf; extern struct ipv6_devconf ipv6_devconf;
......
...@@ -106,24 +106,48 @@ struct frag_hdr { ...@@ -106,24 +106,48 @@ struct frag_hdr {
/* sysctls */ /* sysctls */
extern int sysctl_ipv6_bindv6only; extern int sysctl_ipv6_bindv6only;
/* MIBs */
DECLARE_SNMP_STAT(struct ipv6_mib, ipv6_statistics); DECLARE_SNMP_STAT(struct ipv6_mib, ipv6_statistics);
#define IP6_INC_STATS(field) SNMP_INC_STATS(ipv6_statistics, field) #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_BH(field) SNMP_INC_STATS_BH(ipv6_statistics, field)
#define IP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(ipv6_statistics, field) #define IP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(ipv6_statistics, field)
DECLARE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics); DECLARE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics);
#define ICMP6_INC_STATS(field) SNMP_INC_STATS(icmpv6_statistics, field) #define ICMP6_INC_STATS(idev, field) ({ \
#define ICMP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(icmpv6_statistics, field) struct inet6_dev *_idev = (idev); \
#define ICMP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(icmpv6_statistics, field) if (likely(_idev != NULL)) \
#define ICMP6_STATS_PTR_BH(field) \ SNMP_INC_STATS(idev->stats.icmpv6, field); \
(& \ SNMP_INC_STATS(icmpv6_statistics, field); \
((per_cpu_ptr(icmpv6_statistics[0], smp_processor_id()))-> \ })
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); 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(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_BH(field) SNMP_INC_STATS_BH(udp_stats_in6, field)
#define UDP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(udp_stats_in6, field) #define UDP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(udp_stats_in6, field)
extern atomic_t inet6_sock_nr; 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
{ {
struct ip6_ra_chain *next; struct ip6_ra_chain *next;
......
...@@ -304,6 +304,8 @@ struct linux_mib ...@@ -304,6 +304,8 @@ struct linux_mib
#define SNMP_INC_STATS_BH(mib, field) \ #define SNMP_INC_STATS_BH(mib, field) \
(per_cpu_ptr(mib[0], smp_processor_id())->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) \ #define SNMP_INC_STATS_USER(mib, field) \
(per_cpu_ptr(mib[1], smp_processor_id())->field++) (per_cpu_ptr(mib[1], smp_processor_id())->field++)
#define SNMP_INC_STATS(mib, field) \ #define SNMP_INC_STATS(mib, field) \
......
...@@ -6,6 +6,7 @@ obj-y := main.o version.o mounts.o initramfs.o ...@@ -6,6 +6,7 @@ obj-y := main.o version.o mounts.o initramfs.o
mounts-y := do_mounts.o mounts-y := do_mounts.o
mounts-$(CONFIG_DEVFS_FS) += do_mounts_devfs.o mounts-$(CONFIG_DEVFS_FS) += do_mounts_devfs.o
mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.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 mounts-$(CONFIG_BLK_DEV_MD) += do_mounts_md.o
# files to be removed upon make clean # 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) ...@@ -248,115 +248,6 @@ int __init rd_load_disk(int n)
return rd_load_image("/dev/root"); 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 #ifdef BUILD_CRAMDISK
/* /*
......
...@@ -74,27 +74,20 @@ static int __br_dev_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -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) int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
struct net_bridge *br;
int ret; int ret;
br = dev->priv; rcu_read_lock();
read_lock(&br->lock);
ret = __br_dev_xmit(skb, dev); ret = __br_dev_xmit(skb, dev);
read_unlock(&br->lock); rcu_read_unlock();
return ret; return ret;
} }
static int br_dev_open(struct net_device *dev) static int br_dev_open(struct net_device *dev)
{ {
struct net_bridge *br;
netif_start_queue(dev); netif_start_queue(dev);
br = dev->priv; br_stp_enable_bridge(dev->priv);
write_lock(&br->lock);
br_stp_enable_bridge(br);
write_unlock(&br->lock);
return 0; return 0;
} }
...@@ -105,12 +98,7 @@ static void br_dev_set_multicast_list(struct net_device *dev) ...@@ -105,12 +98,7 @@ static void br_dev_set_multicast_list(struct net_device *dev)
static int br_dev_stop(struct net_device *dev) static int br_dev_stop(struct net_device *dev)
{ {
struct net_bridge *br; br_stp_disable_bridge(dev->priv);
br = dev->priv;
write_lock(&br->lock);
br_stp_disable_bridge(br);
write_unlock(&br->lock);
netif_stop_queue(dev); netif_stop_queue(dev);
......
...@@ -21,7 +21,8 @@ ...@@ -21,7 +21,8 @@
#include <linux/netfilter_bridge.h> #include <linux/netfilter_bridge.h>
#include "br_private.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 || if (skb->dev == p->dev ||
p->state != BR_STATE_FORWARDING) p->state != BR_STATE_FORWARDING)
...@@ -52,7 +53,7 @@ int br_forward_finish(struct sk_buff *skb) ...@@ -52,7 +53,7 @@ int br_forward_finish(struct sk_buff *skb)
return 0; 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; skb->dev = to->dev;
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
...@@ -62,7 +63,7 @@ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb) ...@@ -62,7 +63,7 @@ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
br_forward_finish); 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; struct net_device *indev;
...@@ -73,8 +74,8 @@ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb) ...@@ -73,8 +74,8 @@ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
br_forward_finish); br_forward_finish);
} }
/* called under bridge lock */ /* called with rcu_read_lock */
void br_deliver(struct net_bridge_port *to, struct sk_buff *skb) void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
{ {
if (should_deliver(to, skb)) { if (should_deliver(to, skb)) {
__br_deliver(to, skb); __br_deliver(to, skb);
...@@ -84,8 +85,8 @@ void br_deliver(struct net_bridge_port *to, struct sk_buff *skb) ...@@ -84,8 +85,8 @@ void br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
kfree_skb(skb); kfree_skb(skb);
} }
/* called under bridge lock */ /* called with rcu_read_lock */
void br_forward(struct net_bridge_port *to, struct sk_buff *skb) void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
{ {
if (should_deliver(to, skb)) { if (should_deliver(to, skb)) {
__br_forward(to, skb); __br_forward(to, skb);
...@@ -97,7 +98,8 @@ void br_forward(struct net_bridge_port *to, struct sk_buff *skb) ...@@ -97,7 +98,8 @@ void br_forward(struct net_bridge_port *to, struct sk_buff *skb)
/* called under bridge lock */ /* called under bridge lock */
static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone, 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 *p;
struct net_bridge_port *prev; struct net_bridge_port *prev;
...@@ -115,8 +117,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone, ...@@ -115,8 +117,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
prev = NULL; prev = NULL;
p = br->port_list; list_for_each_entry_rcu(p, &br->port_list, list) {
while (p != NULL) {
if (should_deliver(p, skb)) { if (should_deliver(p, skb)) {
if (prev != NULL) { if (prev != NULL) {
struct sk_buff *skb2; struct sk_buff *skb2;
...@@ -132,8 +133,6 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone, ...@@ -132,8 +133,6 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
prev = p; prev = p;
} }
p = p->next;
} }
if (prev != NULL) { if (prev != NULL) {
...@@ -144,7 +143,8 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone, ...@@ -144,7 +143,8 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
kfree_skb(skb); 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) void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
{ {
br_flood(br, skb, clone, __br_deliver); br_flood(br, skb, clone, __br_deliver);
......
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/brlock.h>
#include <net/sock.h> #include <net/sock.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "br_private.h" #include "br_private.h"
...@@ -38,45 +38,39 @@ static int br_initial_port_cost(struct net_device *dev) ...@@ -38,45 +38,39 @@ static int br_initial_port_cost(struct net_device *dev)
return 100; return 100;
} }
/* called under BR_NETPROTO_LOCK and bridge lock */ static void destroy_nbp(void *arg)
static int __br_del_if(struct net_bridge *br, struct net_device *dev)
{ {
struct net_bridge_port *p; struct net_bridge_port *p = arg;
struct net_bridge_port **pptr; dev_put(p->dev);
kfree(p);
}
if ((p = dev->br_port) == NULL) /* called under bridge lock */
return -EINVAL; static void del_nbp(struct net_bridge_port *p)
{
struct net_device *dev = p->dev;
br_stp_disable_port(p); br_stp_disable_port(p);
dev_set_promiscuity(dev, -1); dev_set_promiscuity(dev, -1);
dev->br_port = NULL; dev->br_port = NULL;
pptr = &br->port_list; list_del_rcu(&p->list);
while (*pptr != NULL) {
if (*pptr == p) {
*pptr = p->next;
break;
}
pptr = &((*pptr)->next); br_fdb_delete_by_port(p->br, p);
}
br_fdb_delete_by_port(br, p); call_rcu(&p->rcu, destroy_nbp, p);
kfree(p);
dev_put(dev);
return 0;
} }
static void del_ifs(struct net_bridge *br) static void del_ifs(struct net_bridge *br)
{ {
br_write_lock_bh(BR_NETPROTO_LOCK); struct list_head *p, *n;
write_lock(&br->lock);
while (br->port_list != NULL) spin_lock_bh(&br->lock);
__br_del_if(br, br->port_list->dev); list_for_each_safe(p, n, &br->port_list) {
write_unlock(&br->lock); del_nbp(list_entry(p, struct net_bridge_port, list));
br_write_unlock_bh(BR_NETPROTO_LOCK); }
spin_unlock_bh(&br->lock);
} }
static struct net_bridge *new_nb(const char *name) static struct net_bridge *new_nb(const char *name)
...@@ -98,7 +92,8 @@ 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); ether_setup(dev);
br_dev_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->hash_lock = RW_LOCK_UNLOCKED;
br->bridge_id.prio[0] = 0x80; br->bridge_id.prio[0] = 0x80;
...@@ -155,8 +150,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device ...@@ -155,8 +150,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device
br_init_port(p); br_init_port(p);
p->state = BR_STATE_DISABLED; p->state = BR_STATE_DISABLED;
p->next = br->port_list; list_add_rcu(&p->list, &br->port_list);
br->port_list = p;
return p; return p;
} }
...@@ -218,9 +212,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -218,9 +212,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return -ELOOP; return -ELOOP;
dev_hold(dev); dev_hold(dev);
write_lock_bh(&br->lock); spin_lock_bh(&br->lock);
if ((p = new_nbp(br, dev)) == NULL) { if ((p = new_nbp(br, dev)) == NULL) {
write_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
dev_put(dev); dev_put(dev);
return -EXFULL; return -EXFULL;
} }
...@@ -231,21 +225,24 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -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); br_fdb_insert(br, p, dev->dev_addr, 1);
if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP)) if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP))
br_stp_enable_port(p); br_stp_enable_port(p);
write_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
return 0; return 0;
} }
int br_del_if(struct net_bridge *br, struct net_device *dev) 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); spin_lock_bh(&br->lock);
write_lock(&br->lock); if ((p = dev->br_port) == NULL || p->br != br)
retval = __br_del_if(br, dev); retval = -EINVAL;
br_stp_recalculate_bridge_id(br); else {
write_unlock(&br->lock); del_nbp(p);
br_write_unlock_bh(BR_NETPROTO_LOCK); br_stp_recalculate_bridge_id(br);
}
spin_unlock_bh(&br->lock);
return retval; return retval;
} }
...@@ -269,13 +266,11 @@ void br_get_port_ifindices(struct net_bridge *br, int *ifindices) ...@@ -269,13 +266,11 @@ void br_get_port_ifindices(struct net_bridge *br, int *ifindices)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
read_lock(&br->lock); rcu_read_lock();
p = br->port_list; list_for_each_entry_rcu(p, &br->port_list, list) {
while (p != NULL) {
ifindices[p->port_no] = p->dev->ifindex; 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) ...@@ -59,15 +59,16 @@ int br_handle_frame_finish(struct sk_buff *skb)
dest = skb->mac.ethernet->h_dest; dest = skb->mac.ethernet->h_dest;
rcu_read_lock();
p = skb->dev->br_port; p = skb->dev->br_port;
if (p == NULL) smp_read_barrier_depends();
goto err_nolock;
br = p->br; if (p == NULL || p->state == BR_STATE_DISABLED) {
read_lock(&br->lock); kfree(skb);
if (skb->dev->br_port == NULL) goto out;
goto err; }
br = p->br;
passedup = 0; passedup = 0;
if (br->dev.flags & IFF_PROMISC) { if (br->dev.flags & IFF_PROMISC) {
struct sk_buff *skb2; struct sk_buff *skb2;
...@@ -105,35 +106,20 @@ int br_handle_frame_finish(struct sk_buff *skb) ...@@ -105,35 +106,20 @@ int br_handle_frame_finish(struct sk_buff *skb)
br_flood_forward(br, skb, 0); br_flood_forward(br, skb, 0);
out: out:
read_unlock(&br->lock); rcu_read_unlock();
return 0;
err:
read_unlock(&br->lock);
err_nolock:
kfree_skb(skb);
return 0; return 0;
} }
int br_handle_frame(struct sk_buff *skb) int br_handle_frame(struct sk_buff *skb)
{ {
struct net_bridge *br;
unsigned char *dest; unsigned char *dest;
struct net_bridge_port *p; struct net_bridge_port *p;
dest = skb->mac.ethernet->h_dest; dest = skb->mac.ethernet->h_dest;
rcu_read_lock();
p = skb->dev->br_port; p = skb->dev->br_port;
if (p == NULL) if (p == NULL || p->state == BR_STATE_DISABLED)
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)
goto err; goto err;
if (skb->mac.ethernet->h_source[0] & 1) if (skb->mac.ethernet->h_source[0] & 1)
...@@ -141,39 +127,30 @@ int br_handle_frame(struct sk_buff *skb) ...@@ -141,39 +127,30 @@ int br_handle_frame(struct sk_buff *skb)
if (p->state == BR_STATE_LEARNING || if (p->state == BR_STATE_LEARNING ||
p->state == BR_STATE_FORWARDING) 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) && !memcmp(dest, bridge_ula, 5) &&
!(dest[5] & 0xF0)) !(dest[5] & 0xF0)) {
goto handle_special_frame; if (!dest[5])
br_stp_handle_bpdu(skb);
goto err;
}
if (p->state == BR_STATE_FORWARDING) { if (p->state == BR_STATE_FORWARDING) {
if (br_should_route_hook && br_should_route_hook(&skb)) { if (br_should_route_hook && br_should_route_hook(&skb)) {
read_unlock(&br->lock); rcu_read_unlock();
return -1; return -1;
} }
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish); br_handle_frame_finish);
read_unlock(&br->lock); rcu_read_unlock();
return 0; return 0;
} }
err: err:
read_unlock(&br->lock); rcu_read_unlock();
err_nolock:
kfree_skb(skb);
return 0;
handle_special_frame:
if (!dest[5]) {
br_stp_handle_bpdu(skb);
read_unlock(&br->lock);
return 0;
}
kfree_skb(skb); kfree_skb(skb);
read_unlock(&br->lock);
return 0; return 0;
} }
...@@ -68,8 +68,8 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -68,8 +68,8 @@ static int br_ioctl_device(struct net_bridge *br,
{ {
struct __bridge_info b; struct __bridge_info b;
read_lock(&br->lock);
memset(&b, 0, sizeof(struct __bridge_info)); memset(&b, 0, sizeof(struct __bridge_info));
rcu_read_lock();
memcpy(&b.designated_root, &br->designated_root, 8); memcpy(&b.designated_root, &br->designated_root, 8);
memcpy(&b.bridge_id, &br->bridge_id, 8); memcpy(&b.bridge_id, &br->bridge_id, 8);
b.root_path_cost = br->root_path_cost; b.root_path_cost = br->root_path_cost;
...@@ -89,7 +89,7 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -89,7 +89,7 @@ static int br_ioctl_device(struct net_bridge *br,
b.tcn_timer_value = timer_residue(&br->tcn_timer); b.tcn_timer_value = timer_residue(&br->tcn_timer);
b.topology_change_timer_value = timer_residue(&br->topology_change_timer); b.topology_change_timer_value = timer_residue(&br->topology_change_timer);
b.gc_timer_value = timer_residue(&br->gc_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))) if (copy_to_user((void *)arg0, &b, sizeof(b)))
return -EFAULT; return -EFAULT;
...@@ -116,27 +116,27 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -116,27 +116,27 @@ static int br_ioctl_device(struct net_bridge *br,
} }
case BRCTL_SET_BRIDGE_FORWARD_DELAY: case BRCTL_SET_BRIDGE_FORWARD_DELAY:
write_lock(&br->lock); spin_lock_bh(&br->lock);
br->bridge_forward_delay = user_to_ticks(arg0); br->bridge_forward_delay = user_to_ticks(arg0);
if (br_is_root_bridge(br)) if (br_is_root_bridge(br))
br->forward_delay = br->bridge_forward_delay; br->forward_delay = br->bridge_forward_delay;
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return 0; return 0;
case BRCTL_SET_BRIDGE_HELLO_TIME: case BRCTL_SET_BRIDGE_HELLO_TIME:
write_lock(&br->lock); spin_lock_bh(&br->lock);
br->bridge_hello_time = user_to_ticks(arg0); br->bridge_hello_time = user_to_ticks(arg0);
if (br_is_root_bridge(br)) if (br_is_root_bridge(br))
br->hello_time = br->bridge_hello_time; br->hello_time = br->bridge_hello_time;
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return 0; return 0;
case BRCTL_SET_BRIDGE_MAX_AGE: case BRCTL_SET_BRIDGE_MAX_AGE:
write_lock(&br->lock); spin_lock_bh(&br->lock);
br->bridge_max_age = user_to_ticks(arg0); br->bridge_max_age = user_to_ticks(arg0);
if (br_is_root_bridge(br)) if (br_is_root_bridge(br))
br->max_age = br->bridge_max_age; br->max_age = br->bridge_max_age;
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return 0; return 0;
case BRCTL_SET_AGEING_TIME: case BRCTL_SET_AGEING_TIME:
...@@ -152,9 +152,9 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -152,9 +152,9 @@ static int br_ioctl_device(struct net_bridge *br,
struct __port_info p; struct __port_info p;
struct net_bridge_port *pt; struct net_bridge_port *pt;
read_lock(&br->lock); rcu_read_lock();
if ((pt = br_get_port(br, arg1)) == NULL) { if ((pt = br_get_port(br, arg1)) == NULL) {
read_unlock(&br->lock); rcu_read_unlock();
return -EINVAL; return -EINVAL;
} }
...@@ -172,7 +172,7 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -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.forward_delay_timer_value = timer_residue(&pt->forward_delay_timer);
p.hold_timer_value = timer_residue(&pt->hold_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))) if (copy_to_user((void *)arg0, &p, sizeof(p)))
return -EFAULT; return -EFAULT;
...@@ -185,9 +185,9 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -185,9 +185,9 @@ static int br_ioctl_device(struct net_bridge *br,
return 0; return 0;
case BRCTL_SET_BRIDGE_PRIORITY: case BRCTL_SET_BRIDGE_PRIORITY:
write_lock(&br->lock); spin_lock_bh(&br->lock);
br_stp_set_bridge_priority(br, arg0); br_stp_set_bridge_priority(br, arg0);
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return 0; return 0;
case BRCTL_SET_PORT_PRIORITY: case BRCTL_SET_PORT_PRIORITY:
...@@ -195,12 +195,12 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -195,12 +195,12 @@ static int br_ioctl_device(struct net_bridge *br,
struct net_bridge_port *p; struct net_bridge_port *p;
int ret = 0; int ret = 0;
write_lock(&br->lock); spin_lock_bh(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL) if ((p = br_get_port(br, arg0)) == NULL)
ret = -EINVAL; ret = -EINVAL;
else else
br_stp_set_port_priority(p, arg1); br_stp_set_port_priority(p, arg1);
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return ret; return ret;
} }
...@@ -209,12 +209,12 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -209,12 +209,12 @@ static int br_ioctl_device(struct net_bridge *br,
struct net_bridge_port *p; struct net_bridge_port *p;
int ret = 0; int ret = 0;
write_lock(&br->lock); spin_lock_bh(&br->lock);
if ((p = br_get_port(br, arg0)) == NULL) if ((p = br_get_port(br, arg0)) == NULL)
ret = -EINVAL; ret = -EINVAL;
else else
br_stp_set_path_cost(p, arg1); br_stp_set_path_cost(p, arg1);
write_unlock(&br->lock); spin_unlock_bh(&br->lock);
return ret; return ret;
} }
......
...@@ -41,10 +41,10 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v ...@@ -41,10 +41,10 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
switch (event) switch (event)
{ {
case NETDEV_CHANGEADDR: case NETDEV_CHANGEADDR:
write_lock_bh(&br->lock); spin_lock_bh(&br->lock);
br_fdb_changeaddr(p, dev->dev_addr); br_fdb_changeaddr(p, dev->dev_addr);
br_stp_recalculate_bridge_id(br); br_stp_recalculate_bridge_id(br);
write_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
break; break;
case NETDEV_GOING_DOWN: case NETDEV_GOING_DOWN:
...@@ -53,17 +53,17 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v ...@@ -53,17 +53,17 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
case NETDEV_DOWN: case NETDEV_DOWN:
if (br->dev.flags & IFF_UP) { if (br->dev.flags & IFF_UP) {
write_lock_bh(&br->lock); spin_lock_bh(&br->lock);
br_stp_disable_port(p); br_stp_disable_port(p);
write_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
} }
break; break;
case NETDEV_UP: case NETDEV_UP:
if (!(br->dev.flags & IFF_UP)) { if (!(br->dev.flags & IFF_UP)) {
write_lock_bh(&br->lock); spin_lock_bh(&br->lock);
br_stp_enable_port(p); br_stp_enable_port(p);
write_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
} }
break; break;
......
...@@ -55,9 +55,9 @@ struct net_bridge_fdb_entry ...@@ -55,9 +55,9 @@ struct net_bridge_fdb_entry
struct net_bridge_port struct net_bridge_port
{ {
struct net_bridge_port *next;
struct net_bridge *br; struct net_bridge *br;
struct net_device *dev; struct net_device *dev;
struct list_head list;
int port_no; int port_no;
/* STP */ /* STP */
...@@ -75,12 +75,14 @@ struct net_bridge_port ...@@ -75,12 +75,14 @@ struct net_bridge_port
struct br_timer forward_delay_timer; struct br_timer forward_delay_timer;
struct br_timer hold_timer; struct br_timer hold_timer;
struct br_timer message_age_timer; struct br_timer message_age_timer;
struct rcu_head rcu;
}; };
struct net_bridge struct net_bridge
{ {
rwlock_t lock; spinlock_t lock;
struct net_bridge_port *port_list; struct list_head port_list;
struct net_device dev; struct net_device dev;
struct net_device_stats statistics; struct net_device_stats statistics;
rwlock_t hash_lock; rwlock_t hash_lock;
...@@ -137,10 +139,10 @@ extern void br_fdb_insert(struct net_bridge *br, ...@@ -137,10 +139,10 @@ extern void br_fdb_insert(struct net_bridge *br,
int is_local); int is_local);
/* br_forward.c */ /* 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); struct sk_buff *skb);
extern int br_dev_queue_push_xmit(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); struct sk_buff *skb);
extern int br_forward_finish(struct sk_buff *skb); extern int br_forward_finish(struct sk_buff *skb);
extern void br_flood_deliver(struct net_bridge *br, extern void br_flood_deliver(struct net_bridge *br,
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
/* called under ioctl_lock or bridge lock */ /* called under bridge lock */
int br_is_root_bridge(struct net_bridge *br) int br_is_root_bridge(struct net_bridge *br)
{ {
return !memcmp(&br->bridge_id, &br->designated_root, 8); return !memcmp(&br->bridge_id, &br->designated_root, 8);
...@@ -35,17 +35,14 @@ int br_is_designated_port(struct net_bridge_port *p) ...@@ -35,17 +35,14 @@ int br_is_designated_port(struct net_bridge_port *p)
(p->designated_port == p->port_id); (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 *br_get_port(struct net_bridge *br, int port_no)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->port_no == port_no) if (p->port_no == port_no)
return p; return p;
p = p->next;
} }
return NULL; return NULL;
...@@ -109,12 +106,10 @@ static void br_root_selection(struct net_bridge *br) ...@@ -109,12 +106,10 @@ static void br_root_selection(struct net_bridge *br)
root_port = 0; root_port = 0;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (br_should_become_root_port(p, root_port)) if (br_should_become_root_port(p, root_port))
root_port = p->port_no; root_port = p->port_no;
p = p->next;
} }
br->root_port = root_port; br->root_port = root_port;
...@@ -241,13 +236,11 @@ static void br_designated_port_selection(struct net_bridge *br) ...@@ -241,13 +236,11 @@ static void br_designated_port_selection(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED && if (p->state != BR_STATE_DISABLED &&
br_should_become_designated_port(p)) br_should_become_designated_port(p))
br_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) ...@@ -313,13 +306,10 @@ void br_config_bpdu_generation(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED && if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p)) br_is_designated_port(p))
br_transmit_config(p); br_transmit_config(p);
p = p->next;
} }
} }
...@@ -391,8 +381,7 @@ void br_port_state_selection(struct net_bridge *br) ...@@ -391,8 +381,7 @@ void br_port_state_selection(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED) { if (p->state != BR_STATE_DISABLED) {
if (p->port_no == br->root_port) { if (p->port_no == br->root_port) {
p->config_pending = 0; p->config_pending = 0;
...@@ -407,8 +396,6 @@ void br_port_state_selection(struct net_bridge *br) ...@@ -407,8 +396,6 @@ void br_port_state_selection(struct net_bridge *br)
br_make_blocking(p); br_make_blocking(p);
} }
} }
p = p->next;
} }
} }
...@@ -419,18 +406,13 @@ static void br_topology_change_acknowledge(struct net_bridge_port *p) ...@@ -419,18 +406,13 @@ static void br_topology_change_acknowledge(struct net_bridge_port *p)
br_transmit_config(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) void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
{ {
struct net_bridge *br; struct net_bridge *br;
int was_root; int was_root;
if (p->state == BR_STATE_DISABLED)
return;
br = p->br; br = p->br;
read_lock(&br->lock);
was_root = br_is_root_bridge(br); was_root = br_is_root_bridge(br);
if (br_supersedes_port_info(p, bpdu)) { if (br_supersedes_port_info(p, bpdu)) {
br_record_config_information(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 ...@@ -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)) { } else if (br_is_designated_port(p)) {
br_reply(p); br_reply(p);
} }
read_unlock(&br->lock);
} }
/* lock-safe */ /* called under bridge lock */
void br_received_tcn_bpdu(struct net_bridge_port *p) void br_received_tcn_bpdu(struct net_bridge_port *p)
{ {
read_lock(&p->br->lock); if (br_is_designated_port(p)) {
if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p)) {
printk(KERN_INFO "%s: received tcn bpdu on port %i(%s)\n", printk(KERN_INFO "%s: received tcn bpdu on port %i(%s)\n",
p->br->dev.name, p->port_no, p->dev->name); p->br->dev.name, p->port_no, p->dev->name);
br_topology_change_detection(p->br); br_topology_change_detection(p->br);
br_topology_change_acknowledge(p); br_topology_change_acknowledge(p);
} }
read_unlock(&p->br->lock);
} }
...@@ -132,18 +132,23 @@ void br_send_tcn_bpdu(struct net_bridge_port *p) ...@@ -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}; 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) void br_stp_handle_bpdu(struct sk_buff *skb)
{ {
unsigned char *buf; unsigned char *buf;
struct net_bridge_port *p; struct net_bridge_port *p;
struct net_bridge *br;
buf = skb->mac.raw + 14; buf = skb->mac.raw + 14;
p = skb->dev->br_port; p = skb->dev->br_port;
if (!p->br->stp_enabled || memcmp(buf, header, 6)) { br = p->br;
kfree_skb(skb);
return; 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) { if (buf[6] == BPDU_TYPE_CONFIG) {
struct br_config_bpdu bpdu; struct br_config_bpdu bpdu;
...@@ -178,16 +183,14 @@ void br_stp_handle_bpdu(struct sk_buff *skb) ...@@ -178,16 +183,14 @@ void br_stp_handle_bpdu(struct sk_buff *skb)
bpdu.hello_time = br_get_ticks(buf+34); bpdu.hello_time = br_get_ticks(buf+34);
bpdu.forward_delay = br_get_ticks(buf+36); bpdu.forward_delay = br_get_ticks(buf+36);
kfree_skb(skb);
br_received_config_bpdu(p, &bpdu); br_received_config_bpdu(p, &bpdu);
return; goto out;
} }
if (buf[6] == BPDU_TYPE_TCN) { if (buf[6] == BPDU_TYPE_TCN) {
br_received_tcn_bpdu(p); br_received_tcn_bpdu(p);
kfree_skb(skb); goto out;
return;
} }
out:
kfree_skb(skb); spin_unlock_bh(&br->lock);
} }
...@@ -44,6 +44,7 @@ void br_stp_enable_bridge(struct net_bridge *br) ...@@ -44,6 +44,7 @@ void br_stp_enable_bridge(struct net_bridge *br)
struct net_bridge_port *p; struct net_bridge_port *p;
struct timer_list *timer = &br->tick; struct timer_list *timer = &br->tick;
spin_lock_bh(&br->lock);
init_timer(timer); init_timer(timer);
timer->data = (unsigned long) br; timer->data = (unsigned long) br;
timer->function = br_tick; timer->function = br_tick;
...@@ -53,22 +54,21 @@ void br_stp_enable_bridge(struct net_bridge *br) ...@@ -53,22 +54,21 @@ void br_stp_enable_bridge(struct net_bridge *br)
br_timer_set(&br->hello_timer, jiffies); br_timer_set(&br->hello_timer, jiffies);
br_config_bpdu_generation(br); br_config_bpdu_generation(br);
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->dev->flags & IFF_UP) if (p->dev->flags & IFF_UP)
br_stp_enable_port(p); br_stp_enable_port(p);
p = p->next;
} }
br_timer_set(&br->gc_timer, jiffies); 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) void br_stp_disable_bridge(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
spin_lock_bh(&br->lock);
br->topology_change = 0; br->topology_change = 0;
br->topology_change_detected = 0; br->topology_change_detected = 0;
br_timer_clear(&br->hello_timer); br_timer_clear(&br->hello_timer);
...@@ -77,13 +77,11 @@ void br_stp_disable_bridge(struct net_bridge *br) ...@@ -77,13 +77,11 @@ void br_stp_disable_bridge(struct net_bridge *br)
br_timer_clear(&br->gc_timer); br_timer_clear(&br->gc_timer);
br_fdb_cleanup(br); br_fdb_cleanup(br);
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED) if (p->state != BR_STATE_DISABLED)
br_stp_disable_port(p); br_stp_disable_port(p);
p = p->next;
} }
spin_unlock_bh(&br->lock);
del_timer_sync(&br->tick); del_timer_sync(&br->tick);
} }
...@@ -133,15 +131,13 @@ static void br_stp_change_bridge_id(struct net_bridge *br, unsigned char *addr) ...@@ -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->bridge_id.addr, addr, ETH_ALEN);
memcpy(br->dev.dev_addr, addr, ETH_ALEN); memcpy(br->dev.dev_addr, addr, ETH_ALEN);
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN)) if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN))
memcpy(p->designated_bridge.addr, addr, ETH_ALEN); memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN)) if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN))
memcpy(p->designated_root.addr, addr, ETH_ALEN); memcpy(p->designated_root.addr, addr, ETH_ALEN);
p = p->next;
} }
br_configuration_update(br); br_configuration_update(br);
...@@ -160,13 +156,11 @@ void br_stp_recalculate_bridge_id(struct net_bridge *br) ...@@ -160,13 +156,11 @@ void br_stp_recalculate_bridge_id(struct net_bridge *br)
addr = br_mac_zero; addr = br_mac_zero;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (addr == br_mac_zero || if (addr == br_mac_zero ||
memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0) memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
addr = p->dev->dev_addr; addr = p->dev->dev_addr;
p = p->next;
} }
if (memcmp(br->bridge_id.addr, addr, ETH_ALEN)) 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) ...@@ -181,15 +175,13 @@ void br_stp_set_bridge_priority(struct net_bridge *br, int newprio)
wasroot = br_is_root_bridge(br); wasroot = br_is_root_bridge(br);
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED && if (p->state != BR_STATE_DISABLED &&
br_is_designated_port(p)) { br_is_designated_port(p)) {
p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF; p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF;
p->designated_bridge.prio[1] = newprio & 0xFF; p->designated_bridge.prio[1] = newprio & 0xFF;
} }
p = p->next;
} }
br->bridge_id.prio[0] = (newprio >> 8) & 0xFF; br->bridge_id.prio[0] = (newprio >> 8) & 0xFF;
......
...@@ -32,13 +32,10 @@ static int br_is_designated_for_some_port(struct net_bridge *br) ...@@ -32,13 +32,10 @@ static int br_is_designated_for_some_port(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED && if (p->state != BR_STATE_DISABLED &&
!memcmp(&p->designated_bridge, &br->bridge_id, 8)) !memcmp(&p->designated_bridge, &br->bridge_id, 8))
return 1; return 1;
p = p->next;
} }
return 0; return 0;
...@@ -162,12 +159,9 @@ static void br_check_timers(struct net_bridge *br) ...@@ -162,12 +159,9 @@ static void br_check_timers(struct net_bridge *br)
br_topology_change_timer_expired(br); br_topology_change_timer_expired(br);
} }
p = br->port_list; list_for_each_entry(p, &br->port_list, list) {
while (p != NULL) {
if (p->state != BR_STATE_DISABLED) if (p->state != BR_STATE_DISABLED)
br_check_port_timers(p); br_check_port_timers(p);
p = p->next;
} }
} }
...@@ -175,10 +169,10 @@ void br_tick(unsigned long __data) ...@@ -175,10 +169,10 @@ void br_tick(unsigned long __data)
{ {
struct net_bridge *br = (struct net_bridge *)__data; struct net_bridge *br = (struct net_bridge *)__data;
read_lock(&br->lock); if (spin_trylock_bh(&br->lock)) {
br_check_timers(br); br_check_timers(br);
read_unlock(&br->lock); spin_unlock_bh(&br->lock);
}
br->tick.expires = jiffies + 1; br->tick.expires = jiffies + 1;
add_timer(&br->tick); add_timer(&br->tick);
} }
...@@ -300,6 +300,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev) ...@@ -300,6 +300,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
printk("Freeing alive inet6 device %p\n", idev); printk("Freeing alive inet6 device %p\n", idev);
return; return;
} }
snmp6_unregister_dev(idev);
inet6_dev_count--; inet6_dev_count--;
kfree(idev); kfree(idev);
} }
...@@ -332,6 +333,15 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) ...@@ -332,6 +333,15 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
/* We refer to the device */ /* We refer to the device */
dev_hold(dev); 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 #ifdef CONFIG_IPV6_PRIVACY
get_random_bytes(ndev->rndid, sizeof(ndev->rndid)); get_random_bytes(ndev->rndid, sizeof(ndev->rndid));
get_random_bytes(ndev->entropy, sizeof(ndev->entropy)); get_random_bytes(ndev->entropy, sizeof(ndev->entropy));
......
...@@ -631,79 +631,72 @@ inet6_unregister_protosw(struct inet_protosw *p) ...@@ -631,79 +631,72 @@ inet6_unregister_protosw(struct inet_protosw *p)
inet_unregister_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; int i;
ipv6_statistics[0] = kmalloc_percpu(sizeof (struct ipv6_mib), if (ptr == NULL)
GFP_KERNEL); return -EINVAL;
if (!ipv6_statistics[0])
goto err_ip_mib0; ptr[0] = kmalloc_percpu(mibsize, GFP_KERNEL);
ipv6_statistics[1] = kmalloc_percpu(sizeof (struct ipv6_mib), if (!ptr[0])
GFP_KERNEL); goto err0;
if (!ipv6_statistics[1])
goto err_ip_mib1; ptr[1] = kmalloc_percpu(mibsize, GFP_KERNEL);
if (!ptr[1])
icmpv6_statistics[0] = kmalloc_percpu(sizeof (struct icmpv6_mib), goto err1;
GFP_KERNEL);
if (!icmpv6_statistics[0]) /* Zero percpu version of the mibs */
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 */
for (i = 0; i < NR_CPUS; i++) { for (i = 0; i < NR_CPUS; i++) {
if (cpu_possible(i)) { if (cpu_possible(i)) {
memset(per_cpu_ptr(ipv6_statistics[0], i), 0, memset(per_cpu_ptr(ptr[0], i), 0, mibsize);
sizeof (struct ipv6_mib)); memset(per_cpu_ptr(ptr[1], i), 0, mibsize);
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));
} }
} }
return 0; return 0;
err_udp_mib1: err1:
kfree_percpu(udp_stats_in6[0]); kfree_percpu(ptr[0]);
err_udp_mib0: ptr[0] = NULL;
kfree_percpu(icmpv6_statistics[1]); err0:
err_icmp_mib1: return -ENOMEM;
kfree_percpu(icmpv6_statistics[0]); }
err_icmp_mib0:
kfree_percpu(ipv6_statistics[1]); void
err_ip_mib1: snmp6_mib_free(void *ptr[2])
kfree_percpu(ipv6_statistics[0]); {
err_ip_mib0: 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; return -ENOMEM;
} }
static void cleanup_ipv6_mibs(void) static void cleanup_ipv6_mibs(void)
{ {
kfree_percpu(ipv6_statistics[0]); snmp6_mib_free((void **)ipv6_statistics);
kfree_percpu(ipv6_statistics[1]); snmp6_mib_free((void **)icmpv6_statistics);
kfree_percpu(icmpv6_statistics[0]); snmp6_mib_free((void **)udp_stats_in6);
kfree_percpu(icmpv6_statistics[1]);
kfree_percpu(udp_stats_in6[0]);
kfree_percpu(udp_stats_in6[1]);
} }
extern int ipv6_misc_proc_init(void); extern int ipv6_misc_proc_init(void);
...@@ -819,6 +812,7 @@ static int __init inet6_init(void) ...@@ -819,6 +812,7 @@ static int __init inet6_init(void)
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
proc_anycast6_fail: proc_anycast6_fail:
proc_net_remove("snmp6"); proc_net_remove("snmp6");
proc_net_remove("dev_snmp6");
proc_net_remove("sockstat6"); proc_net_remove("sockstat6");
proc_misc6_fail: proc_misc6_fail:
proc_net_remove("udp6"); proc_net_remove("udp6");
...@@ -854,6 +848,7 @@ static void inet6_exit(void) ...@@ -854,6 +848,7 @@ static void inet6_exit(void)
proc_net_remove("tcp6"); proc_net_remove("tcp6");
proc_net_remove("udp6"); proc_net_remove("udp6");
proc_net_remove("sockstat6"); proc_net_remove("sockstat6");
proc_net_remove("dev_snmp6");
proc_net_remove("snmp6"); proc_net_remove("snmp6");
proc_net_remove("anycast6"); proc_net_remove("anycast6");
#endif #endif
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
* yoshfuji : ensure to sent parameter problem for * yoshfuji : ensure to sent parameter problem for
* fragments. * fragments.
* YOSHIFUJI Hideaki @USAGI: added sysctl for icmp rate limit. * YOSHIFUJI Hideaki @USAGI: added sysctl for icmp rate limit.
* Randy Dunlap and
* YOSHIFUJI Hideaki @USAGI: Per-interface statistics support
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -247,6 +249,7 @@ static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset) ...@@ -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, void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
struct net_device *dev) struct net_device *dev)
{ {
struct inet6_dev *idev;
struct ipv6hdr *hdr = skb->nh.ipv6h; struct ipv6hdr *hdr = skb->nh.ipv6h;
struct sock *sk = icmpv6_socket->sk; struct sock *sk = icmpv6_socket->sk;
struct in6_addr *saddr = NULL; struct in6_addr *saddr = NULL;
...@@ -351,11 +354,16 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, ...@@ -351,11 +354,16 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
msg.len = len; msg.len = len;
idev = in6_dev_get(skb->dev);
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1, ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
MSG_DONTWAIT); MSG_DONTWAIT);
if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
ICMP6_STATS_PTR_BH(Icmp6OutDestUnreachs) [type-ICMPV6_DEST_UNREACH]++; ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6OutDestUnreachs, type - ICMPV6_DEST_UNREACH);
ICMP6_INC_STATS_BH(Icmp6OutMsgs); ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
out: out:
icmpv6_xmit_unlock(); icmpv6_xmit_unlock();
} }
...@@ -363,6 +371,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, ...@@ -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) static void icmpv6_echo_reply(struct sk_buff *skb)
{ {
struct sock *sk = icmpv6_socket->sk; struct sock *sk = icmpv6_socket->sk;
struct inet6_dev *idev;
struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw; struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
struct in6_addr *saddr; struct in6_addr *saddr;
struct icmpv6_msg msg; struct icmpv6_msg msg;
...@@ -394,14 +403,19 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -394,14 +403,19 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
fl.fl_icmp_type = ICMPV6_ECHO_REPLY; fl.fl_icmp_type = ICMPV6_ECHO_REPLY;
fl.fl_icmp_code = 0; fl.fl_icmp_code = 0;
idev = in6_dev_get(skb->dev);
icmpv6_xmit_lock(); icmpv6_xmit_lock();
ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1, ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1,
MSG_DONTWAIT); MSG_DONTWAIT);
ICMP6_INC_STATS_BH(Icmp6OutEchoReplies); ICMP6_INC_STATS_BH(idev, Icmp6OutEchoReplies);
ICMP6_INC_STATS_BH(Icmp6OutMsgs); ICMP6_INC_STATS_BH(idev, Icmp6OutMsgs);
icmpv6_xmit_unlock(); 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) 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) ...@@ -464,12 +478,13 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
{ {
struct sk_buff *skb = *pskb; struct sk_buff *skb = *pskb;
struct net_device *dev = skb->dev; struct net_device *dev = skb->dev;
struct inet6_dev *idev = __in6_dev_get(dev);
struct in6_addr *saddr, *daddr; struct in6_addr *saddr, *daddr;
struct ipv6hdr *orig_hdr; struct ipv6hdr *orig_hdr;
struct icmp6hdr *hdr; struct icmp6hdr *hdr;
int type; int type;
ICMP6_INC_STATS_BH(Icmp6InMsgs); ICMP6_INC_STATS_BH(idev, Icmp6InMsgs);
saddr = &skb->nh.ipv6h->saddr; saddr = &skb->nh.ipv6h->saddr;
daddr = &skb->nh.ipv6h->daddr; daddr = &skb->nh.ipv6h->daddr;
...@@ -517,9 +532,9 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) ...@@ -517,9 +532,9 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
type = hdr->icmp6_type; type = hdr->icmp6_type;
if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) 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) 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) { switch (type) {
case ICMPV6_ECHO_REQUEST: case ICMPV6_ECHO_REQUEST:
...@@ -597,7 +612,7 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) ...@@ -597,7 +612,7 @@ static int icmpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
return 0; return 0;
discard_it: discard_it:
ICMP6_INC_STATS_BH(Icmp6InErrors); ICMP6_INC_STATS_BH(idev, Icmp6InErrors);
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
} }
......
...@@ -34,5 +34,6 @@ EXPORT_SYMBOL(ipv6_get_saddr); ...@@ -34,5 +34,6 @@ EXPORT_SYMBOL(ipv6_get_saddr);
EXPORT_SYMBOL(ipv6_chk_addr); EXPORT_SYMBOL(ipv6_chk_addr);
EXPORT_SYMBOL(in6addr_any); EXPORT_SYMBOL(in6addr_any);
EXPORT_SYMBOL(in6addr_loopback); EXPORT_SYMBOL(in6addr_loopback);
EXPORT_SYMBOL(in6_dev_finish_destroy);
EXPORT_SYMBOL(xfrm6_rcv); EXPORT_SYMBOL(xfrm6_rcv);
EXPORT_SYMBOL(xfrm6_clear_mutable_options); EXPORT_SYMBOL(xfrm6_clear_mutable_options);
...@@ -1253,6 +1253,7 @@ static void mld_sendpack(struct sk_buff *skb) ...@@ -1253,6 +1253,7 @@ static void mld_sendpack(struct sk_buff *skb)
struct ipv6hdr *pip6 = skb->nh.ipv6h; struct ipv6hdr *pip6 = skb->nh.ipv6h;
struct mld2_report *pmr = (struct mld2_report *)skb->h.raw; struct mld2_report *pmr = (struct mld2_report *)skb->h.raw;
int payload_len, mldlen; int payload_len, mldlen;
struct inet6_dev *idev = in6_dev_get(skb->dev);
payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h - payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h -
sizeof(struct ipv6hdr); sizeof(struct ipv6hdr);
...@@ -1262,7 +1263,9 @@ static void mld_sendpack(struct sk_buff *skb) ...@@ -1262,7 +1263,9 @@ static void mld_sendpack(struct sk_buff *skb)
pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen, pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,
IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0)); IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0));
dev_queue_xmit(skb); 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) 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) ...@@ -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) static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
{ {
struct sock *sk = igmp6_socket->sk; struct sock *sk = igmp6_socket->sk;
struct inet6_dev *idev;
struct sk_buff *skb; struct sk_buff *skb;
struct icmp6hdr *hdr; struct icmp6hdr *hdr;
struct in6_addr *snd_addr; struct in6_addr *snd_addr;
...@@ -1577,12 +1581,17 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) ...@@ -1577,12 +1581,17 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
IPPROTO_ICMPV6, IPPROTO_ICMPV6,
csum_partial((__u8 *) hdr, len, 0)); csum_partial((__u8 *) hdr, len, 0));
idev = in6_dev_get(skb->dev);
dev_queue_xmit(skb); dev_queue_xmit(skb);
if (type == ICMPV6_MGM_REDUCTION) if (type == ICMPV6_MGM_REDUCTION)
ICMP6_INC_STATS(Icmp6OutGroupMembReductions); ICMP6_INC_STATS(idev, Icmp6OutGroupMembReductions);
else else
ICMP6_INC_STATS(Icmp6OutGroupMembResponses); ICMP6_INC_STATS(idev, Icmp6OutGroupMembResponses);
ICMP6_INC_STATS(Icmp6OutMsgs); ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
return; return;
out: out:
......
...@@ -415,6 +415,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -415,6 +415,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
{ {
static struct in6_addr tmpaddr; static struct in6_addr tmpaddr;
struct inet6_ifaddr *ifp; struct inet6_ifaddr *ifp;
struct inet6_dev *idev;
struct flowi fl; struct flowi fl;
struct rt6_info *rt = NULL; struct rt6_info *rt = NULL;
struct dst_entry* dst; struct dst_entry* dst;
...@@ -497,10 +498,14 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -497,10 +498,14 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
dst_clone(dst); dst_clone(dst);
skb->dst = dst; skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb); dst_output(skb);
ICMP6_INC_STATS(Icmp6OutNeighborAdvertisements); ICMP6_INC_STATS(idev, Icmp6OutNeighborAdvertisements);
ICMP6_INC_STATS(Icmp6OutMsgs); ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
} }
void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, 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, ...@@ -510,6 +515,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
struct flowi fl; struct flowi fl;
struct rt6_info *rt = NULL; struct rt6_info *rt = NULL;
struct dst_entry* dst; struct dst_entry* dst;
struct inet6_dev *idev;
struct sock *sk = ndisc_socket->sk; struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb; struct sk_buff *skb;
struct nd_msg *msg; struct nd_msg *msg;
...@@ -576,10 +582,14 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, ...@@ -576,10 +582,14 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
/* send it! */ /* send it! */
dst_clone(dst); dst_clone(dst);
skb->dst = dst; skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb); dst_output(skb);
ICMP6_INC_STATS(Icmp6OutNeighborSolicits); ICMP6_INC_STATS(idev, Icmp6OutNeighborSolicits);
ICMP6_INC_STATS(Icmp6OutMsgs); 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, 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, ...@@ -588,6 +598,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
struct flowi fl; struct flowi fl;
struct rt6_info *rt = NULL; struct rt6_info *rt = NULL;
struct dst_entry* dst; struct dst_entry* dst;
struct inet6_dev *idev;
struct sock *sk = ndisc_socket->sk; struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb; struct sk_buff *skb;
struct icmp6hdr *hdr; struct icmp6hdr *hdr;
...@@ -644,10 +655,14 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, ...@@ -644,10 +655,14 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
/* send it! */ /* send it! */
dst_clone(dst); dst_clone(dst);
skb->dst = dst; skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb); dst_output(skb);
ICMP6_INC_STATS(Icmp6OutRouterSolicits); ICMP6_INC_STATS(idev, Icmp6OutRouterSolicits);
ICMP6_INC_STATS(Icmp6OutMsgs); 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, ...@@ -1271,6 +1286,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
struct net_device *dev; struct net_device *dev;
struct rt6_info *rt; struct rt6_info *rt;
struct dst_entry *dst; struct dst_entry *dst;
struct inet6_dev *idev;
struct flowi fl; struct flowi fl;
u8 *opt; u8 *opt;
int rd_len; int rd_len;
...@@ -1379,10 +1395,14 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, ...@@ -1379,10 +1395,14 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
csum_partial((u8 *) icmph, len, 0)); csum_partial((u8 *) icmph, len, 0));
skb->dst = dst; skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb); dst_output(skb);
ICMP6_INC_STATS(Icmp6OutRedirects); ICMP6_INC_STATS(idev, Icmp6OutRedirects);
ICMP6_INC_STATS(Icmp6OutMsgs); ICMP6_INC_STATS(idev, Icmp6OutMsgs);
if (likely(idev != NULL))
in6_dev_put(idev);
} }
static void pndisc_redo(struct sk_buff *skb) static void pndisc_redo(struct sk_buff *skb)
......
...@@ -10,12 +10,14 @@ ...@@ -10,12 +10,14 @@
* Version: $Id: proc.c,v 1.17 2002/02/01 22:01:04 davem Exp $ * Version: $Id: proc.c,v 1.17 2002/02/01 22:01:04 davem Exp $
* *
* Authors: David S. Miller (davem@caip.rutgers.edu) * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version * as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*/ */
#include <linux/config.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/net.h> #include <linux/net.h>
...@@ -28,6 +30,10 @@ ...@@ -28,6 +30,10 @@
#include <net/transp_v6.h> #include <net/transp_v6.h>
#include <net/ipv6.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) static int fold_prot_inuse(struct proto *proto)
{ {
int res = 0; int res = 0;
...@@ -53,14 +59,16 @@ static int sockstat6_seq_show(struct seq_file *seq, void *v) ...@@ -53,14 +59,16 @@ static int sockstat6_seq_show(struct seq_file *seq, void *v)
} }
static struct snmp6_item struct snmp6_item
{ {
char *name; char *name;
void **mib;
int offset; 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 */ /* 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(Ip6InReceives),
SNMP6_GEN(Ip6InHdrErrors), SNMP6_GEN(Ip6InHdrErrors),
SNMP6_GEN(Ip6InTooBigErrors), SNMP6_GEN(Ip6InTooBigErrors),
...@@ -84,6 +92,10 @@ static struct snmp6_item ...@@ -84,6 +92,10 @@ static struct snmp6_item
SNMP6_GEN(Ip6InMcastPkts), SNMP6_GEN(Ip6InMcastPkts),
SNMP6_GEN(Ip6OutMcastPkts), SNMP6_GEN(Ip6OutMcastPkts),
#undef SNMP6_GEN #undef SNMP6_GEN
SNMP6_SENTINEL
};
static struct snmp6_item snmp6_icmp6_list[] = {
/* icmpv6 mib according to draft-ietf-ipngwg-ipv6-icmp-mib-02 /* icmpv6 mib according to draft-ietf-ipngwg-ipv6-icmp-mib-02
Exceptions: {In|Out}AdminProhibs are removed, because I see Exceptions: {In|Out}AdminProhibs are removed, because I see
...@@ -94,7 +106,7 @@ static struct snmp6_item ...@@ -94,7 +106,7 @@ static struct snmp6_item
OutRouterAdvertisements too. OutRouterAdvertisements too.
OutGroupMembQueries 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(Icmp6InMsgs),
SNMP6_GEN(Icmp6InErrors), SNMP6_GEN(Icmp6InErrors),
SNMP6_GEN(Icmp6InDestUnreachs), SNMP6_GEN(Icmp6InDestUnreachs),
...@@ -124,12 +136,17 @@ static struct snmp6_item ...@@ -124,12 +136,17 @@ static struct snmp6_item
SNMP6_GEN(Icmp6OutGroupMembResponses), SNMP6_GEN(Icmp6OutGroupMembResponses),
SNMP6_GEN(Icmp6OutGroupMembReductions), SNMP6_GEN(Icmp6OutGroupMembReductions),
#undef SNMP6_GEN #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(InDatagrams),
SNMP6_GEN(NoPorts), SNMP6_GEN(NoPorts),
SNMP6_GEN(InErrors), SNMP6_GEN(InErrors),
SNMP6_GEN(OutDatagrams) SNMP6_GEN(OutDatagrams),
#undef SNMP6_GEN #undef SNMP6_GEN
SNMP6_SENTINEL
}; };
static unsigned long static unsigned long
...@@ -151,18 +168,30 @@ fold_field(void *mib[], int offt) ...@@ -151,18 +168,30 @@ fold_field(void *mib[], int offt)
return res; 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; 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++) static int snmp6_seq_show(struct seq_file *seq, void *v)
seq_printf(seq, "%-32s\t%lu\n", snmp6_list[i].name, {
fold_field(snmp6_list[i].mib, snmp6_list[i].offset)); 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; return 0;
} }
static int sockstat6_seq_open(struct inode *inode, struct file *file) static int sockstat6_seq_open(struct inode *inode, struct file *file)
{ {
return single_open(file, sockstat6_seq_show, NULL); return single_open(file, sockstat6_seq_show, NULL);
...@@ -177,7 +206,7 @@ static struct file_operations sockstat6_seq_fops = { ...@@ -177,7 +206,7 @@ static struct file_operations sockstat6_seq_fops = {
static int snmp6_seq_open(struct inode *inode, struct file *file) 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 = { static struct file_operations snmp6_seq_fops = {
...@@ -187,6 +216,57 @@ static struct file_operations snmp6_seq_fops = { ...@@ -187,6 +216,57 @@ static struct file_operations snmp6_seq_fops = {
.release = single_release, .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 __init ipv6_misc_proc_init(void)
{ {
int rc = 0; int rc = 0;
...@@ -197,6 +277,9 @@ int __init ipv6_misc_proc_init(void) ...@@ -197,6 +277,9 @@ int __init ipv6_misc_proc_init(void)
goto proc_snmp6_fail; goto proc_snmp6_fail;
else else
p->proc_fops = &snmp6_seq_fops; 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); p = create_proc_entry("sockstat6", S_IRUGO, proc_net);
if (!p) if (!p)
goto proc_sockstat6_fail; goto proc_sockstat6_fail;
...@@ -206,6 +289,8 @@ int __init ipv6_misc_proc_init(void) ...@@ -206,6 +289,8 @@ int __init ipv6_misc_proc_init(void)
return rc; return rc;
proc_sockstat6_fail: proc_sockstat6_fail:
remove_proc_entry("dev_snmp6", proc_net);
proc_dev_snmp6_fail:
remove_proc_entry("snmp6", proc_net); remove_proc_entry("snmp6", proc_net);
proc_snmp6_fail: proc_snmp6_fail:
rc = -ENOMEM; rc = -ENOMEM;
......
...@@ -751,7 +751,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -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); sk = tcp_v6_lookup(&hdr->daddr, th->dest, &hdr->saddr, th->source, skb->dev->ifindex);
if (sk == NULL) { if (sk == NULL) {
ICMP6_INC_STATS_BH(Icmp6InErrors); ICMP6_INC_STATS_BH(__in6_dev_get(skb->dev), Icmp6InErrors);
return; return;
} }
......
...@@ -92,6 +92,7 @@ extern struct notifier_block sctp_inetaddr_notifier; ...@@ -92,6 +92,7 @@ extern struct notifier_block sctp_inetaddr_notifier;
void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int type, int code, int offset, __u32 info) int type, int code, int offset, __u32 info)
{ {
struct inet6_dev *idev;
struct ipv6hdr *iph = (struct ipv6hdr *)skb->data; struct ipv6hdr *iph = (struct ipv6hdr *)skb->data;
struct sctphdr *sh = (struct sctphdr *)(skb->data + offset); struct sctphdr *sh = (struct sctphdr *)(skb->data + offset);
struct sock *sk; struct sock *sk;
...@@ -102,6 +103,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -102,6 +103,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
char *saveip, *savesctp; char *saveip, *savesctp;
int err; int err;
idev = in6_dev_get(skb->dev);
/* Fix up skb to look at the embedded net header. */ /* Fix up skb to look at the embedded net header. */
saveip = skb->nh.raw; saveip = skb->nh.raw;
savesctp = skb->h.raw; savesctp = skb->h.raw;
...@@ -112,8 +115,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -112,8 +115,8 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
skb->nh.raw = saveip; skb->nh.raw = saveip;
skb->h.raw = savesctp; skb->h.raw = savesctp;
if (!sk) { if (!sk) {
ICMP6_INC_STATS_BH(Icmp6InErrors); ICMP6_INC_STATS_BH(idev, Icmp6InErrors);
return; goto out;
} }
/* Warning: The sock lock is held. Remember to call /* 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, ...@@ -139,6 +142,9 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
out_unlock: out_unlock:
sctp_err_finish(sk, ep, asoc); sctp_err_finish(sk, ep, asoc);
out:
if (likely(idev != NULL))
in6_dev_put(idev);
} }
/* Based on tcp_v6_xmit() in tcp_ipv6.c. */ /* 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