aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/DocBook/usb.tmpl1
-rw-r--r--Documentation/cpu-freq/governors.txt62
-rw-r--r--Documentation/networking/ip-sysctl.txt23
-rw-r--r--MAINTAINERS14
-rw-r--r--Makefile4
-rw-r--r--arch/arm/kernel/calls.S8
-rw-r--r--arch/arm/kernel/entry-armv.S49
-rw-r--r--arch/arm/kernel/entry-common.S20
-rw-r--r--arch/arm/mach-pxa/mainstone.c21
-rw-r--r--arch/arm/mach-pxa/pxa27x.c6
-rw-r--r--arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c3
-rw-r--r--arch/i386/kernel/cpu/cpufreq/powernow-k8.c50
-rw-r--r--arch/i386/kernel/cpu/cpufreq/powernow-k8.h9
-rw-r--r--arch/i386/kernel/cpu/cpufreq/speedstep-ich.c47
-rw-r--r--arch/i386/kernel/cpu/cpufreq/speedstep-lib.c32
-rw-r--r--arch/i386/kernel/cpu/cpufreq/speedstep-lib.h1
-rw-r--r--arch/i386/kernel/cpu/cpufreq/speedstep-smi.c1
-rw-r--r--arch/i386/kernel/cpu/proc.c6
-rw-r--r--arch/i386/kernel/process.c4
-rw-r--r--arch/i386/pci/Makefile2
-rw-r--r--arch/i386/pci/mmconfig.c2
-rw-r--r--arch/ia64/configs/sn2_defconfig2
-rw-r--r--arch/ia64/kernel/setup.c8
-rw-r--r--arch/ia64/kernel/time.c29
-rw-r--r--arch/ia64/kernel/uncached.c6
-rw-r--r--arch/ia64/kernel/vmlinux.lds.S3
-rw-r--r--arch/ia64/sn/kernel/sn2/sn2_smp.c2
-rw-r--r--arch/mips/kernel/linux32.c4
-rw-r--r--arch/powerpc/configs/cell_defconfig7
-rw-r--r--arch/powerpc/configs/g5_defconfig9
-rw-r--r--arch/powerpc/configs/iseries_defconfig7
-rw-r--r--arch/powerpc/configs/maple_defconfig10
-rw-r--r--arch/powerpc/configs/ppc64_defconfig7
-rw-r--r--arch/powerpc/configs/pseries_defconfig7
-rw-r--r--arch/powerpc/kernel/entry_64.S4
-rw-r--r--arch/powerpc/mm/hash_utils_64.c2
-rw-r--r--arch/powerpc/platforms/pseries/xics.c21
-rw-r--r--arch/ppc/platforms/85xx/mpc85xx_cds_common.c3
-rw-r--r--arch/ppc/syslib/ppc4xx_dma.c1
-rw-r--r--arch/sparc/Kconfig4
-rw-r--r--arch/sparc/kernel/sys_sunos.c2
-rw-r--r--arch/sparc/kernel/vmlinux.lds.S18
-rw-r--r--arch/sparc64/Kconfig4
-rw-r--r--arch/sparc64/Makefile5
-rw-r--r--arch/sparc64/kernel/sys_sunos32.c2
-rw-r--r--arch/sparc64/kernel/vmlinux.lds.S18
-rw-r--r--arch/sparc64/solaris/misc.c2
-rw-r--r--arch/um/Kconfig2
-rw-r--r--arch/um/Makefile-x86_644
-rw-r--r--arch/um/include/sysdep-i386/stub.h29
-rw-r--r--arch/um/include/sysdep-x86_64/stub.h30
-rw-r--r--arch/um/kernel/skas/clone.c23
-rw-r--r--arch/um/os-Linux/start_up.c22
-rw-r--r--arch/um/os-Linux/user_syms.c5
-rw-r--r--arch/um/scripts/Makefile.rules5
-rw-r--r--arch/um/sys-i386/Makefile8
-rw-r--r--arch/um/sys-x86_64/Makefile5
-rw-r--r--arch/x86_64/kernel/setup.c6
-rw-r--r--arch/x86_64/mm/init.c2
-rw-r--r--arch/x86_64/pci/Makefile2
-rw-r--r--block/scsi_ioctl.c45
-rw-r--r--drivers/acpi/processor_idle.c20
-rw-r--r--drivers/acpi/processor_thermal.c4
-rw-r--r--drivers/acpi/utilities/utmisc.c18
-rw-r--r--drivers/block/Kconfig3
-rw-r--r--drivers/block/ub.c439
-rw-r--r--drivers/bluetooth/bcm203x.c1
-rw-r--r--drivers/bluetooth/bfusb.c1
-rw-r--r--drivers/bluetooth/bpa10x.c1
-rw-r--r--drivers/bluetooth/hci_usb.c1
-rw-r--r--drivers/char/Kconfig4
-rw-r--r--drivers/char/drm/radeon_cp.c4
-rw-r--r--drivers/char/drm/radeon_drv.h1
-rw-r--r--drivers/char/ip2/i2pack.h2
-rw-r--r--drivers/char/keyboard.c10
-rw-r--r--drivers/char/random.c10
-rw-r--r--drivers/char/vc_screen.c2
-rw-r--r--drivers/char/watchdog/booke_wdt.c2
-rw-r--r--drivers/char/watchdog/pcwd_usb.c1
-rw-r--r--drivers/cpufreq/cpufreq.c24
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c10
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c10
-rw-r--r--drivers/fc4/Kconfig8
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c11
-rw-r--r--drivers/ieee1394/hosts.h1
-rw-r--r--drivers/ieee1394/nodemgr.c67
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c2
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c2
-rw-r--r--drivers/input/joystick/iforce/iforce-usb.c1
-rw-r--r--drivers/input/joystick/warrior.c2
-rw-r--r--drivers/input/misc/Kconfig2
-rw-r--r--drivers/input/mouse/sermouse.c2
-rw-r--r--drivers/input/serio/i8042.h2
-rw-r--r--drivers/isdn/hisax/hfc_usb.c1
-rw-r--r--drivers/isdn/hisax/st5481_init.c1
-rw-r--r--drivers/macintosh/therm_pm72.c8
-rw-r--r--drivers/md/md.c2
-rw-r--r--drivers/media/dvb/b2c2/flexcop-usb.c1
-rw-r--r--drivers/media/dvb/cinergyT2/cinergyT2.c1
-rw-r--r--drivers/media/dvb/dvb-usb/a800.c1
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.c1
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mb.c1
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mc.c1
-rw-r--r--drivers/media/dvb/dvb-usb/digitv.c1
-rw-r--r--drivers/media/dvb/dvb-usb/dtt200u.c1
-rw-r--r--drivers/media/dvb/dvb-usb/nova-t-usb2.c1
-rw-r--r--drivers/media/dvb/dvb-usb/umt-010.c1
-rw-r--r--drivers/media/dvb/dvb-usb/vp702x.c1
-rw-r--r--drivers/media/dvb/dvb-usb/vp7045.c1
-rw-r--r--drivers/media/dvb/ttpci/av7110.c3
-rw-r--r--drivers/media/dvb/ttpci/av7110_hw.h3
-rw-r--r--drivers/media/video/cpia_usb.c1
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c42
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c6
-rw-r--r--drivers/media/video/em28xx/em28xx-i2c.c2
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c3
-rw-r--r--drivers/media/video/em28xx/em28xx.h8
-rw-r--r--drivers/media/video/saa7127.c6
-rw-r--r--drivers/media/video/saa7134/Kconfig26
-rw-r--r--drivers/media/video/saa7134/Makefile7
-rw-r--r--drivers/media/video/saa7134/saa7134-alsa.c18
-rw-r--r--drivers/media/video/saa7134/saa7134-oss.c18
-rw-r--r--drivers/media/video/tveeprom.c2
-rw-r--r--drivers/message/i2o/pci.c6
-rw-r--r--drivers/mmc/mmc_block.c14
-rw-r--r--drivers/mtd/maps/Kconfig2
-rw-r--r--drivers/mtd/onenand/generic.c4
-rw-r--r--drivers/mtd/onenand/onenand_base.c53
-rw-r--r--drivers/mtd/onenand/onenand_bbt.c4
-rw-r--r--drivers/net/forcedeth.c15
-rw-r--r--drivers/net/irda/irda-usb.c1
-rw-r--r--drivers/net/irda/stir4200.c1
-rw-r--r--drivers/net/ns83820.c1
-rw-r--r--drivers/net/phy/phy_device.c4
-rw-r--r--drivers/net/ppp_generic.c3
-rw-r--r--drivers/net/pppoe.c31
-rw-r--r--drivers/net/pppox.c10
-rw-r--r--drivers/net/sk98lin/skge.c1
-rw-r--r--drivers/net/skge.c1
-rw-r--r--drivers/net/sungem.c4
-rw-r--r--drivers/net/tg3.c198
-rw-r--r--drivers/net/tg3.h7
-rw-r--r--drivers/net/wireless/ipw2200.c15
-rw-r--r--drivers/net/wireless/orinoco_nortel.c6
-rw-r--r--drivers/pci/Makefile7
-rw-r--r--drivers/s390/net/qeth_eddp.c3
-rw-r--r--drivers/s390/net/qeth_main.c61
-rw-r--r--drivers/s390/net/qeth_mpc.c2
-rw-r--r--drivers/s390/net/qeth_mpc.h4
-rw-r--r--drivers/s390/net/qeth_proc.c250
-rw-r--r--drivers/s390/net/qeth_sys.c6
-rw-r--r--drivers/s390/net/qeth_tso.h4
-rw-r--r--drivers/scsi/dpt_i2o.c25
-rw-r--r--drivers/scsi/libata-scsi.c5
-rw-r--r--drivers/scsi/scsi_scan.c49
-rw-r--r--drivers/scsi/scsi_transport_fc.c59
-rw-r--r--drivers/serial/Kconfig10
-rw-r--r--drivers/serial/amba-pl011.c2
-rw-r--r--drivers/serial/pxa.c4
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/atm/Kconfig13
-rw-r--r--drivers/usb/atm/Makefile1
-rw-r--r--drivers/usb/atm/cxacru.c1
-rw-r--r--drivers/usb/atm/speedtch.c1
-rw-r--r--drivers/usb/atm/ueagle-atm.c1820
-rw-r--r--drivers/usb/atm/usbatm.c4
-rw-r--r--drivers/usb/atm/xusbatm.c1
-rw-r--r--drivers/usb/class/audio.c1
-rw-r--r--drivers/usb/class/cdc-acm.c232
-rw-r--r--drivers/usb/class/cdc-acm.h33
-rw-r--r--drivers/usb/class/usb-midi.c1
-rw-r--r--drivers/usb/class/usblp.c45
-rw-r--r--drivers/usb/core/Makefile2
-rw-r--r--drivers/usb/core/buffer.c3
-rw-r--r--drivers/usb/core/devices.c24
-rw-r--r--drivers/usb/core/devio.c3
-rw-r--r--drivers/usb/core/driver.c472
-rw-r--r--drivers/usb/core/hcd.c10
-rw-r--r--drivers/usb/core/hcd.h1
-rw-r--r--drivers/usb/core/hub.c486
-rw-r--r--drivers/usb/core/hub.h3
-rw-r--r--drivers/usb/core/message.c6
-rw-r--r--drivers/usb/core/usb.c471
-rw-r--r--drivers/usb/core/usb.h6
-rw-r--r--drivers/usb/gadget/dummy_hcd.c91
-rw-r--r--drivers/usb/gadget/file_storage.c93
-rw-r--r--drivers/usb/gadget/serial.c2
-rw-r--r--drivers/usb/host/Makefile4
-rw-r--r--drivers/usb/host/ehci-hcd.c9
-rw-r--r--drivers/usb/host/ehci-hub.c4
-rw-r--r--drivers/usb/host/ehci-pci.c20
-rw-r--r--drivers/usb/host/ehci-q.c14
-rw-r--r--drivers/usb/host/isp116x-hcd.c422
-rw-r--r--drivers/usb/host/isp116x.h83
-rw-r--r--drivers/usb/host/ohci-hcd.c14
-rw-r--r--drivers/usb/host/ohci-hub.c2
-rw-r--r--drivers/usb/host/ohci-pxa27x.c121
-rw-r--r--drivers/usb/host/pci-quirks.c6
-rw-r--r--drivers/usb/host/sl811-hcd.c14
-rw-r--r--drivers/usb/host/sl811_cs.c5
-rw-r--r--drivers/usb/host/uhci-debug.c14
-rw-r--r--drivers/usb/host/uhci-hcd.c34
-rw-r--r--drivers/usb/host/uhci-hcd.h32
-rw-r--r--drivers/usb/host/uhci-q.c30
-rw-r--r--drivers/usb/image/mdc800.c1
-rw-r--r--drivers/usb/image/microtek.c1
-rw-r--r--drivers/usb/input/Kconfig14
-rw-r--r--drivers/usb/input/Makefile1
-rw-r--r--drivers/usb/input/acecad.c1
-rw-r--r--drivers/usb/input/aiptek.c9
-rw-r--r--drivers/usb/input/appletouch.c1
-rw-r--r--drivers/usb/input/ati_remote.c22
-rw-r--r--drivers/usb/input/ati_remote2.c477
-rw-r--r--drivers/usb/input/fixp-arith.h2
-rw-r--r--drivers/usb/input/hid-core.c3
-rw-r--r--drivers/usb/input/hid-input.c5
-rw-r--r--drivers/usb/input/hiddev.c1
-rw-r--r--drivers/usb/input/itmtouch.c1
-rw-r--r--drivers/usb/input/kbtab.c3
-rw-r--r--drivers/usb/input/keyspan_remote.c3
-rw-r--r--drivers/usb/input/mtouchusb.c1
-rw-r--r--drivers/usb/input/powermate.c1
-rw-r--r--drivers/usb/input/touchkitusb.c149
-rw-r--r--drivers/usb/input/usbkbd.c1
-rw-r--r--drivers/usb/input/usbmouse.c1
-rw-r--r--drivers/usb/input/wacom.c3
-rw-r--r--drivers/usb/input/xpad.c7
-rw-r--r--drivers/usb/input/yealink.c1
-rw-r--r--drivers/usb/media/dabusb.c1
-rw-r--r--drivers/usb/media/dsbr100.c1
-rw-r--r--drivers/usb/media/ibmcam.c2
-rw-r--r--drivers/usb/media/konicawc.c6
-rw-r--r--drivers/usb/media/ov511.c3
-rw-r--r--drivers/usb/media/pwc/pwc-ctrl.c2
-rw-r--r--drivers/usb/media/pwc/pwc-if.c1
-rw-r--r--drivers/usb/media/se401.c1
-rw-r--r--drivers/usb/media/sn9c102_core.c24
-rw-r--r--drivers/usb/media/stv680.c1
-rw-r--r--drivers/usb/media/stv680.h6
-rw-r--r--drivers/usb/media/usbvideo.c4
-rw-r--r--drivers/usb/media/vicam.c1
-rw-r--r--drivers/usb/media/w9968cf.c7
-rw-r--r--drivers/usb/misc/auerswald.c5
-rw-r--r--drivers/usb/misc/cytherm.c1
-rw-r--r--drivers/usb/misc/emi26.c1
-rw-r--r--drivers/usb/misc/emi62.c1
-rw-r--r--drivers/usb/misc/idmouse.c1
-rw-r--r--drivers/usb/misc/ldusb.c1
-rw-r--r--drivers/usb/misc/legousbtower.c1
-rw-r--r--drivers/usb/misc/phidgetkit.c1
-rw-r--r--drivers/usb/misc/phidgetservo.c1
-rw-r--r--drivers/usb/misc/rio500.c5
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c11
-rw-r--r--drivers/usb/misc/usblcd.c1
-rw-r--r--drivers/usb/misc/usbled.c1
-rw-r--r--drivers/usb/misc/usbtest.c1
-rw-r--r--drivers/usb/misc/uss720.c1
-rw-r--r--drivers/usb/mon/mon_text.c19
-rw-r--r--drivers/usb/net/asix.c5
-rw-r--r--drivers/usb/net/catc.c1
-rw-r--r--drivers/usb/net/cdc_ether.c1
-rw-r--r--drivers/usb/net/cdc_subset.c1
-rw-r--r--drivers/usb/net/gl620a.c1
-rw-r--r--drivers/usb/net/kaweth.c1
-rw-r--r--drivers/usb/net/net1080.c1
-rw-r--r--drivers/usb/net/pegasus.c144
-rw-r--r--drivers/usb/net/plusb.c1
-rw-r--r--drivers/usb/net/rndis_host.c1
-rw-r--r--drivers/usb/net/rtl8150.c1
-rw-r--r--drivers/usb/net/zaurus.c1
-rw-r--r--drivers/usb/net/zd1201.c11
-rw-r--r--drivers/usb/serial/airprime.c2
-rw-r--r--drivers/usb/serial/anydata.c2
-rw-r--r--drivers/usb/serial/belkin_sa.c2
-rw-r--r--drivers/usb/serial/cp2101.c2
-rw-r--r--drivers/usb/serial/cyberjack.c2
-rw-r--r--drivers/usb/serial/cypress_m8.c1
-rw-r--r--drivers/usb/serial/digi_acceleport.c2
-rw-r--r--drivers/usb/serial/empeg.c2
-rw-r--r--drivers/usb/serial/ftdi_sio.c6
-rw-r--r--drivers/usb/serial/ftdi_sio.h13
-rw-r--r--drivers/usb/serial/garmin_gps.c2
-rw-r--r--drivers/usb/serial/generic.c2
-rw-r--r--drivers/usb/serial/hp4x.c2
-rw-r--r--drivers/usb/serial/io_edgeport.c6
-rw-r--r--drivers/usb/serial/io_edgeport.h3
-rw-r--r--drivers/usb/serial/io_fw_boot2.h2
-rw-r--r--drivers/usb/serial/io_ti.c4
-rw-r--r--drivers/usb/serial/ipaq.c2
-rw-r--r--drivers/usb/serial/ipw.c2
-rw-r--r--drivers/usb/serial/ir-usb.c2
-rw-r--r--drivers/usb/serial/keyspan.h2
-rw-r--r--drivers/usb/serial/keyspan_pda.c2
-rw-r--r--drivers/usb/serial/kl5kusb105.c2
-rw-r--r--drivers/usb/serial/kobil_sct.c2
-rw-r--r--drivers/usb/serial/mct_u232.c2
-rw-r--r--drivers/usb/serial/omninet.c2
-rw-r--r--drivers/usb/serial/option.c2
-rw-r--r--drivers/usb/serial/pl2303.c4
-rw-r--r--drivers/usb/serial/safe_serial.c6
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c9
-rw-r--r--drivers/usb/serial/usb-serial.c48
-rw-r--r--drivers/usb/serial/usb-serial.h4
-rw-r--r--drivers/usb/serial/visor.c2
-rw-r--r--drivers/usb/serial/whiteheat.c2
-rw-r--r--drivers/usb/storage/Kconfig23
-rw-r--r--drivers/usb/storage/Makefile5
-rw-r--r--drivers/usb/storage/alauda.c1119
-rw-r--r--drivers/usb/storage/alauda.h100
-rw-r--r--drivers/usb/storage/debug.c1
-rw-r--r--drivers/usb/storage/initializers.h4
-rw-r--r--drivers/usb/storage/libusual.c266
-rw-r--r--drivers/usb/storage/onetouch.c27
-rw-r--r--drivers/usb/storage/protocol.h14
-rw-r--r--drivers/usb/storage/scsiglue.c4
-rw-r--r--drivers/usb/storage/sddr09.c214
-rw-r--r--drivers/usb/storage/sddr09.h15
-rw-r--r--drivers/usb/storage/transport.h31
-rw-r--r--drivers/usb/storage/unusual_devs.h74
-rw-r--r--drivers/usb/storage/usb.c160
-rw-r--r--drivers/usb/storage/usb.h40
-rw-r--r--drivers/usb/usb-skeleton.c29
-rw-r--r--drivers/video/Kconfig10
-rw-r--r--drivers/video/console/Kconfig20
-rw-r--r--drivers/video/console/fbcon_ud.c8
-rw-r--r--drivers/video/intelfb/intelfb.h5
-rw-r--r--drivers/video/intelfb/intelfbdrv.c51
-rw-r--r--drivers/video/logo/Kconfig2
-rw-r--r--drivers/video/sbuslib.c3
-rw-r--r--drivers/w1/dscore.c1
-rw-r--r--fs/9p/trans_sock.c1
-rw-r--r--fs/compat.c2
-rw-r--r--fs/hostfs/hostfs_kern.c9
-rw-r--r--fs/lockd/clntlock.c4
-rw-r--r--fs/nfs/callback.c3
-rw-r--r--fs/nfs/direct.c24
-rw-r--r--fs/nfs/file.c26
-rw-r--r--fs/nfs/inode.c28
-rw-r--r--fs/nfsd/nfs2acl.c2
-rw-r--r--fs/nfsd/nfs3acl.c2
-rw-r--r--fs/partitions/Kconfig2
-rw-r--r--fs/proc/generic.c47
-rw-r--r--fs/read_write.c34
-rw-r--r--fs/relayfs/relay.c8
-rw-r--r--include/acpi/acglobal.h2
-rw-r--r--include/asm-alpha/bitops.h1
-rw-r--r--include/asm-arm/arch-pxa/ohci.h18
-rw-r--r--include/asm-arm/bitops.h2
-rw-r--r--include/asm-arm26/bitops.h1
-rw-r--r--include/asm-cris/bitops.h1
-rw-r--r--include/asm-frv/bitops.h1
-rw-r--r--include/asm-generic/bitops.h1
-rw-r--r--include/asm-h8300/bitops.h1
-rw-r--r--include/asm-i386/bitops.h1
-rw-r--r--include/asm-i386/param.h3
-rw-r--r--include/asm-ia64/bitops.h1
-rw-r--r--include/asm-ia64/delay.h10
-rw-r--r--include/asm-ia64/topology.h2
-rw-r--r--include/asm-m32r/bitops.h1
-rw-r--r--include/asm-m68k/bitops.h1
-rw-r--r--include/asm-m68knommu/bitops.h1
-rw-r--r--include/asm-mips/bitops.h2
-rw-r--r--include/asm-parisc/bitops.h1
-rw-r--r--include/asm-powerpc/bitops.h1
-rw-r--r--include/asm-ppc/ppc4xx_dma.h3
-rw-r--r--include/asm-s390/bitops.h1
-rw-r--r--include/asm-sh/bitops.h1
-rw-r--r--include/asm-sh64/bitops.h1
-rw-r--r--include/asm-sparc/bitops.h1
-rw-r--r--include/asm-sparc64/bitops.h1
-rw-r--r--include/asm-v850/bitops.h1
-rw-r--r--include/asm-x86_64/bitops.h27
-rw-r--r--include/asm-x86_64/param.h3
-rw-r--r--include/asm-x86_64/rwlock.h2
-rw-r--r--include/asm-x86_64/topology.h2
-rw-r--r--include/asm-xtensa/bitops.h1
-rw-r--r--include/linux/bitops.h9
-rw-r--r--include/linux/cache.h2
-rw-r--r--include/linux/cpufreq.h10
-rw-r--r--include/linux/dccp.h7
-rw-r--r--include/linux/etherdevice.h3
-rw-r--r--include/linux/if_pppox.h3
-rw-r--r--include/linux/ip.h121
-rw-r--r--include/linux/ipv6.h79
-rw-r--r--include/linux/ipv6_route.h1
-rw-r--r--include/linux/irq.h2
-rw-r--r--include/linux/mm.h1
-rw-r--r--include/linux/mtd/onenand.h5
-rw-r--r--include/linux/n_r3964.h20
-rw-r--r--include/linux/net.h4
-rw-r--r--include/linux/nfs_fs.h1
-rw-r--r--include/linux/pfkeyv2.h13
-rw-r--r--include/linux/pkt_sched.h7
-rw-r--r--include/linux/preempt.h1
-rw-r--r--include/linux/random.h6
-rw-r--r--include/linux/relayfs_fs.h5
-rw-r--r--include/linux/rtnetlink.h4
-rw-r--r--include/linux/security.h132
-rw-r--r--include/linux/skbuff.h5
-rw-r--r--include/linux/socket.h1
-rw-r--r--include/linux/sysctl.h1
-rw-r--r--include/linux/tcp.h21
-rw-r--r--include/linux/udp.h6
-rw-r--r--include/linux/usb.h33
-rw-r--r--include/linux/usb_usual.h126
-rw-r--r--include/linux/xfrm.h29
-rw-r--r--include/net/af_unix.h12
-rw-r--r--include/net/atmclip.h2
-rw-r--r--include/net/dst.h1
-rw-r--r--include/net/flow.h7
-rw-r--r--include/net/genetlink.h2
-rw-r--r--include/net/icmp.h9
-rw-r--r--include/net/ieee80211_crypt.h9
-rw-r--r--include/net/if_inet6.h2
-rw-r--r--include/net/inet6_connection_sock.h42
-rw-r--r--include/net/inet6_hashtables.h32
-rw-r--r--include/net/inet_common.h4
-rw-r--r--include/net/inet_connection_sock.h45
-rw-r--r--include/net/inet_ecn.h2
-rw-r--r--include/net/inet_hashtables.h24
-rw-r--r--include/net/inet_sock.h193
-rw-r--r--include/net/inet_timewait_sock.h8
-rw-r--r--include/net/inetpeer.h1
-rw-r--r--include/net/ip.h19
-rw-r--r--include/net/ip_fib.h2
-rw-r--r--include/net/ip_vs.h12
-rw-r--r--include/net/ipv6.h12
-rw-r--r--include/net/ndisc.h17
-rw-r--r--include/net/neighbour.h2
-rw-r--r--include/net/pkt_act.h1
-rw-r--r--include/net/protocol.h3
-rw-r--r--include/net/raw.h2
-rw-r--r--include/net/request_sock.h2
-rw-r--r--include/net/sctp/structs.h76
-rw-r--r--include/net/sctp/user.h30
-rw-r--r--include/net/sock.h32
-rw-r--r--include/net/tcp.h246
-rw-r--r--include/net/tcp_states.h16
-rw-r--r--include/net/timewait_sock.h31
-rw-r--r--include/net/transp_v6.h2
-rw-r--r--include/net/udp.h4
-rw-r--r--include/net/xfrm.h31
-rw-r--r--include/scsi/scsi_transport_fc.h11
-rw-r--r--init/Kconfig1
-rw-r--r--init/main.c4
-rw-r--r--ipc/sem.c2
-rw-r--r--kernel/futex.c6
-rw-r--r--kernel/params.c2
-rw-r--r--kernel/sysctl.c29
-rw-r--r--lib/spinlock_debug.c18
-rw-r--r--lib/swiotlb.c5
-rw-r--r--mm/memory.c3
-rw-r--r--mm/mempolicy.c4
-rw-r--r--mm/mmap.c2
-rw-r--r--mm/mremap.c2
-rw-r--r--net/8021q/vlan.c6
-rw-r--r--net/appletalk/ddp.c23
-rw-r--r--net/atm/pvc.c2
-rw-r--r--net/atm/svc.c2
-rw-r--r--net/ax25/af_ax25.c6
-rw-r--r--net/bluetooth/af_bluetooth.c5
-rw-r--r--net/bluetooth/bnep/sock.c2
-rw-r--r--net/bluetooth/cmtp/sock.c2
-rw-r--r--net/bluetooth/hci_sock.c2
-rw-r--r--net/bluetooth/hidp/sock.c2
-rw-r--r--net/bluetooth/l2cap.c9
-rw-r--r--net/bluetooth/rfcomm/sock.c4
-rw-r--r--net/bluetooth/sco.c9
-rw-r--r--net/bridge/br.c1
-rw-r--r--net/bridge/br_device.c91
-rw-r--r--net/bridge/br_if.c55
-rw-r--r--net/bridge/br_input.c11
-rw-r--r--net/bridge/br_netfilter.c23
-rw-r--r--net/bridge/br_notify.c14
-rw-r--r--net/bridge/br_private.h7
-rw-r--r--net/bridge/br_stp_if.c5
-rw-r--r--net/bridge/netfilter/Kconfig6
-rw-r--r--net/bridge/netfilter/ebt_log.c73
-rw-r--r--net/bridge/netfilter/ebt_ulog.c53
-rw-r--r--net/core/datagram.c36
-rw-r--r--net/core/dev.c1
-rw-r--r--net/core/filter.c116
-rw-r--r--net/core/flow.c8
-rw-r--r--net/core/netpoll.c1
-rw-r--r--net/core/pktgen.c6
-rw-r--r--net/core/skbuff.c27
-rw-r--r--net/core/sock.c21
-rw-r--r--net/core/stream.c10
-rw-r--r--net/dccp/Makefile4
-rw-r--r--net/dccp/ackvec.c33
-rw-r--r--net/dccp/ackvec.h12
-rw-r--r--net/dccp/ccid.h2
-rw-r--r--net/dccp/dccp.h24
-rw-r--r--net/dccp/diag.c2
-rw-r--r--net/dccp/input.c79
-rw-r--r--net/dccp/ipv4.c307
-rw-r--r--net/dccp/ipv6.c1261
-rw-r--r--net/dccp/ipv6.h37
-rw-r--r--net/dccp/minisocks.c23
-rw-r--r--net/dccp/output.c47
-rw-r--r--net/dccp/proto.c56
-rw-r--r--net/decnet/af_decnet.c6
-rw-r--r--net/decnet/dn_neigh.c13
-rw-r--r--net/decnet/dn_nsp_in.c17
-rw-r--r--net/econet/af_econet.c9
-rw-r--r--net/ieee80211/ieee80211_rx.c5
-rw-r--r--net/ipv4/Kconfig8
-rw-r--r--net/ipv4/Makefile1
-rw-r--r--net/ipv4/af_inet.c19
-rw-r--r--net/ipv4/ah4.c1
-rw-r--r--net/ipv4/arp.c1
-rw-r--r--net/ipv4/devinet.c1
-rw-r--r--net/ipv4/esp4.c1
-rw-r--r--net/ipv4/fib_frontend.c1
-rw-r--r--net/ipv4/fib_hash.c1
-rw-r--r--net/ipv4/fib_rules.c1
-rw-r--r--net/ipv4/fib_semantics.c2
-rw-r--r--net/ipv4/fib_trie.c8
-rw-r--r--net/ipv4/icmp.c1
-rw-r--r--net/ipv4/igmp.c2
-rw-r--r--net/ipv4/inet_connection_sock.c25
-rw-r--r--net/ipv4/inet_diag.c14
-rw-r--r--net/ipv4/inet_hashtables.c178
-rw-r--r--net/ipv4/inet_timewait_sock.c5
-rw-r--r--net/ipv4/inetpeer.c1
-rw-r--r--net/ipv4/ip_fragment.c68
-rw-r--r--net/ipv4/ip_input.c1
-rw-r--r--net/ipv4/ip_options.c1
-rw-r--r--net/ipv4/ip_output.c1
-rw-r--r--net/ipv4/ip_sockglue.c14
-rw-r--r--net/ipv4/ipcomp.c1
-rw-r--r--net/ipv4/ipconfig.c2
-rw-r--r--net/ipv4/ipmr.c1
-rw-r--r--net/ipv4/ipvs/ip_vs_app.c28
-rw-r--r--net/ipv4/ipvs/ip_vs_conn.c21
-rw-r--r--net/ipv4/ipvs/ip_vs_core.c2
-rw-r--r--net/ipv4/ipvs/ip_vs_ctl.c10
-rw-r--r--net/ipv4/ipvs/ip_vs_dh.c2
-rw-r--r--net/ipv4/ipvs/ip_vs_est.c3
-rw-r--r--net/ipv4/ipvs/ip_vs_lblc.c29
-rw-r--r--net/ipv4/ipvs/ip_vs_lblcr.c29
-rw-r--r--net/ipv4/ipvs/ip_vs_proto_ah.c2
-rw-r--r--net/ipv4/ipvs/ip_vs_proto_esp.c2
-rw-r--r--net/ipv4/ipvs/ip_vs_proto_tcp.c24
-rw-r--r--net/ipv4/ipvs/ip_vs_proto_udp.c3
-rw-r--r--net/ipv4/ipvs/ip_vs_sh.c2
-rw-r--r--net/ipv4/ipvs/ip_vs_sync.c2
-rw-r--r--net/ipv4/netfilter/Makefile3
-rw-r--r--net/ipv4/netfilter/arp_tables.c175
-rw-r--r--net/ipv4/netfilter/ip_conntrack_amanda.c2
-rw-r--r--net/ipv4/netfilter/ip_conntrack_proto_gre.c1
-rw-r--r--net/ipv4/netfilter/ip_conntrack_proto_udp.c1
-rw-r--r--net/ipv4/netfilter/ip_conntrack_standalone.c1
-rw-r--r--net/ipv4/netfilter/ip_nat_snmp_basic.c2
-rw-r--r--net/ipv4/netfilter/ip_tables.c199
-rw-r--r--net/ipv4/netfilter/ipt_MASQUERADE.c2
-rw-r--r--net/ipv4/netfilter/ipt_physdev.c1
-rw-r--r--net/ipv4/proc.c1
-rw-r--r--net/ipv4/syncookies.c4
-rw-r--r--net/ipv4/sysctl_net_ipv4.c11
-rw-r--r--net/ipv4/tcp.c10
-rw-r--r--net/ipv4/tcp_bic.c85
-rw-r--r--net/ipv4/tcp_cong.c28
-rw-r--r--net/ipv4/tcp_cubic.c411
-rw-r--r--net/ipv4/tcp_input.c99
-rw-r--r--net/ipv4/tcp_ipv4.c269
-rw-r--r--net/ipv4/tcp_minisocks.c16
-rw-r--r--net/ipv4/tcp_output.c118
-rw-r--r--net/ipv4/tcp_vegas.c4
-rw-r--r--net/ipv4/udp.c22
-rw-r--r--net/ipv4/xfrm4_policy.c1
-rw-r--r--net/ipv6/Makefile3
-rw-r--r--net/ipv6/addrconf.c135
-rw-r--r--net/ipv6/af_inet6.c90
-rw-r--r--net/ipv6/ah6.c1
-rw-r--r--net/ipv6/esp6.c1
-rw-r--r--net/ipv6/exthdrs.c4
-rw-r--r--net/ipv6/icmp.c16
-rw-r--r--net/ipv6/inet6_connection_sock.c199
-rw-r--r--net/ipv6/inet6_hashtables.c183
-rw-r--r--net/ipv6/ip6_flowlabel.c2
-rw-r--r--net/ipv6/ip6_output.c2
-rw-r--r--net/ipv6/ipcomp6.c1
-rw-r--r--net/ipv6/ipv6_sockglue.c24
-rw-r--r--net/ipv6/mcast.c142
-rw-r--r--net/ipv6/netfilter/Kconfig2
-rw-r--r--net/ipv6/netfilter/ip6_tables.c191
-rw-r--r--net/ipv6/netfilter/ip6t_LOG.c1
-rw-r--r--net/ipv6/netfilter/ip6t_ah.c1
-rw-r--r--net/ipv6/netfilter/ip6t_esp.c1
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c4
-rw-r--r--net/ipv6/raw.c16
-rw-r--r--net/ipv6/route.c15
-rw-r--r--net/ipv6/tcp_ipv6.c639
-rw-r--r--net/ipv6/udp.c16
-rw-r--r--net/ipv6/xfrm6_policy.c1
-rw-r--r--net/ipx/af_ipx.c6
-rw-r--r--net/irda/af_irda.c23
-rw-r--r--net/key/af_key.c201
-rw-r--r--net/llc/af_llc.c11
-rw-r--r--net/netfilter/nfnetlink_log.c2
-rw-r--r--net/netfilter/nfnetlink_queue.c2
-rw-r--r--net/netlink/af_netlink.c4
-rw-r--r--net/netlink/genetlink.c2
-rw-r--r--net/netrom/af_netrom.c16
-rw-r--r--net/netrom/nr_in.c6
-rw-r--r--net/nonet.c5
-rw-r--r--net/packet/af_packet.c10
-rw-r--r--net/rose/af_rose.c2
-rw-r--r--net/sched/sch_netem.c49
-rw-r--r--net/sched/sch_teql.c1
-rw-r--r--net/sctp/associola.c81
-rw-r--r--net/sctp/input.c36
-rw-r--r--net/sctp/ipv6.c2
-rw-r--r--net/sctp/output.c17
-rw-r--r--net/sctp/protocol.c3
-rw-r--r--net/sctp/sm_sideeffect.c26
-rw-r--r--net/sctp/sm_statefuns.c20
-rw-r--r--net/sctp/socket.c705
-rw-r--r--net/sctp/transport.c32
-rw-r--r--net/socket.c243
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c6
-rw-r--r--net/sunrpc/rpc_pipe.c4
-rw-r--r--net/sunrpc/svcsock.c2
-rw-r--r--net/sunrpc/xprtsock.c2
-rw-r--r--net/unix/af_unix.c55
-rw-r--r--net/unix/garbage.c4
-rw-r--r--net/wanrouter/af_wanpipe.c6
-rw-r--r--net/x25/af_x25.c6
-rw-r--r--net/xfrm/xfrm_policy.c137
-rw-r--r--net/xfrm/xfrm_state.c14
-rw-r--r--net/xfrm/xfrm_user.c148
-rw-r--r--security/Kconfig13
-rw-r--r--security/dummy.c45
-rw-r--r--security/selinux/Makefile2
-rw-r--r--security/selinux/hooks.c39
-rw-r--r--security/selinux/include/av_perm_to_string.h2
-rw-r--r--security/selinux/include/av_permissions.h2
-rw-r--r--security/selinux/include/xfrm.h54
-rw-r--r--security/selinux/xfrm.c311
-rw-r--r--sound/sparc/Kconfig2
-rw-r--r--sound/usb/usbaudio.c1
-rw-r--r--sound/usb/usx2y/usbusx2y.c1
643 files changed, 15098 insertions, 5955 deletions
diff --git a/Documentation/DocBook/usb.tmpl b/Documentation/DocBook/usb.tmpl
index 15ce0f21e5e0..320af25de3a2 100644
--- a/Documentation/DocBook/usb.tmpl
+++ b/Documentation/DocBook/usb.tmpl
@@ -253,6 +253,7 @@
!Edrivers/usb/core/urb.c
!Edrivers/usb/core/message.c
!Edrivers/usb/core/file.c
+!Edrivers/usb/core/driver.c
!Edrivers/usb/core/usb.c
!Edrivers/usb/core/hub.c
</chapter>
diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt
index 933fae74c337..f4b8dc4237e6 100644
--- a/Documentation/cpu-freq/governors.txt
+++ b/Documentation/cpu-freq/governors.txt
@@ -27,6 +27,7 @@ Contents:
2.2 Powersave
2.3 Userspace
2.4 Ondemand
+2.5 Conservative
3. The Governor Interface in the CPUfreq Core
@@ -110,9 +111,64 @@ directory.
The CPUfreq govenor "ondemand" sets the CPU depending on the
current usage. To do this the CPU must have the capability to
-switch the frequency very fast.
-
-
+switch the frequency very quickly. There are a number of sysfs file
+accessible parameters:
+
+sampling_rate: measured in uS (10^-6 seconds), this is how often you
+want the kernel to look at the CPU usage and to make decisions on
+what to do about the frequency. Typically this is set to values of
+around '10000' or more.
+
+show_sampling_rate_(min|max): the minimum and maximum sampling rates
+available that you may set 'sampling_rate' to.
+
+up_threshold: defines what the average CPU usaged between the samplings
+of 'sampling_rate' needs to be for the kernel to make a decision on
+whether it should increase the frequency. For example when it is set
+to its default value of '80' it means that between the checking
+intervals the CPU needs to be on average more than 80% in use to then
+decide that the CPU frequency needs to be increased.
+
+sampling_down_factor: this parameter controls the rate that the CPU
+makes a decision on when to decrease the frequency. When set to its
+default value of '5' it means that at 1/5 the sampling_rate the kernel
+makes a decision to lower the frequency. Five "lower rate" decisions
+have to be made in a row before the CPU frequency is actually lower.
+If set to '1' then the frequency decreases as quickly as it increases,
+if set to '2' it decreases at half the rate of the increase.
+
+ignore_nice_load: this parameter takes a value of '0' or '1', when set
+to '0' (its default) then all processes are counted towards towards the
+'cpu utilisation' value. When set to '1' then processes that are
+run with a 'nice' value will not count (and thus be ignored) in the
+overal usage calculation. This is useful if you are running a CPU
+intensive calculation on your laptop that you do not care how long it
+takes to complete as you can 'nice' it and prevent it from taking part
+in the deciding process of whether to increase your CPU frequency.
+
+
+2.5 Conservative
+----------------
+
+The CPUfreq governor "conservative", much like the "ondemand"
+governor, sets the CPU depending on the current usage. It differs in
+behaviour in that it gracefully increases and decreases the CPU speed
+rather than jumping to max speed the moment there is any load on the
+CPU. This behaviour more suitable in a battery powered environment.
+The governor is tweaked in the same manner as the "ondemand" governor
+through sysfs with the addition of:
+
+freq_step: this describes what percentage steps the cpu freq should be
+increased and decreased smoothly by. By default the cpu frequency will
+increase in 5% chunks of your maximum cpu frequency. You can change this
+value to anywhere between 0 and 100 where '0' will effectively lock your
+CPU at a speed regardless of its load whilst '100' will, in theory, make
+it behave identically to the "ondemand" governor.
+
+down_threshold: same as the 'up_threshold' found for the "ondemand"
+governor but for the opposite direction. For example when set to its
+default value of '20' it means that if the CPU usage needs to be below
+20% between samples to have the frequency decreased.
3. The Governor Interface in the CPUfreq Core
=============================================
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index ebc09a159f62..2b7cf19a06ad 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -46,6 +46,29 @@ ipfrag_secret_interval - INTEGER
for the hash secret) for IP fragments.
Default: 600
+ipfrag_max_dist - INTEGER
+ ipfrag_max_dist is a non-negative integer value which defines the
+ maximum "disorder" which is allowed among fragments which share a
+ common IP source address. Note that reordering of packets is
+ not unusual, but if a large number of fragments arrive from a source
+ IP address while a particular fragment queue remains incomplete, it
+ probably indicates that one or more fragments belonging to that queue
+ have been lost. When ipfrag_max_dist is positive, an additional check
+ is done on fragments before they are added to a reassembly queue - if
+ ipfrag_max_dist (or more) fragments have arrived from a particular IP
+ address between additions to any IP fragment queue using that source
+ address, it's presumed that one or more fragments in the queue are
+ lost. The existing fragment queue will be dropped, and a new one
+ started. An ipfrag_max_dist value of zero disables this check.
+
+ Using a very small value, e.g. 1 or 2, for ipfrag_max_dist can
+ result in unnecessarily dropping fragment queues when normal
+ reordering of packets occurs, which could lead to poor application
+ performance. Using a very large value, e.g. 50000, increases the
+ likelihood of incorrectly reassembling IP fragments that originate
+ from different IP datagrams, which could result in data corruption.
+ Default: 64
+
INET peer storage:
inet_peer_threshold - INTEGER
diff --git a/MAINTAINERS b/MAINTAINERS
index da6973adacda..3fd7687a6ad9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -536,7 +536,7 @@ P: Mauro Carvalho Chehab
M: mchehab@brturbo.com.br
L: video4linux-list@redhat.com
W: http://linuxtv.org
-T: quilt http://www.linuxtv.org/download/quilt/
+T: git kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb.git
S: Maintained
BUSLOGIC SCSI DRIVER
@@ -834,7 +834,7 @@ P: LinuxTV.org Project
M: linux-dvb-maintainer@linuxtv.org
L: linux-dvb@linuxtv.org (subscription required)
W: http://linuxtv.org/
-T: quilt http://www.linuxtv.org/download/quilt/
+T: git kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb.git
S: Supported
EATA-DMA SCSI DRIVER
@@ -2640,6 +2640,12 @@ L: linux-usb-users@lists.sourceforge.net
L: linux-usb-devel@lists.sourceforge.net
S: Maintained
+USB ISP116X DRIVER
+P: Olav Kongas
+M: ok@artecdesign.ee
+L: linux-usb-devel@lists.sourceforge.net
+S: Maintained
+
USB KAWASAKI LSI DRIVER
P: Oliver Neukum
M: oliver@neukum.name
@@ -2651,7 +2657,7 @@ USB MASS STORAGE DRIVER
P: Matthew Dharm
M: mdharm-usb@one-eyed-alien.net
L: linux-usb-users@lists.sourceforge.net
-L: linux-usb-devel@lists.sourceforge.net
+L: usb-storage@lists.one-eyed-alien.net
S: Maintained
W: http://www.one-eyed-alien.net/~mdharm/linux-usb/
@@ -2896,7 +2902,7 @@ P: Mauro Carvalho Chehab
M: mchehab@brturbo.com.br
L: video4linux-list@redhat.com
W: http://linuxtv.org
-T: quilt http://www.linuxtv.org/download/quilt/
+T: git kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb.git
S: Maintained
W1 DALLAS'S 1-WIRE BUS
diff --git a/Makefile b/Makefile
index ad457e1bd0d2..497884d3d556 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 15
-EXTRAVERSION =-rc5
-NAME=Affluent Albatross
+EXTRAVERSION =
+NAME=Sliding Snow Leopard
# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index 2ad4aa2a1536..55076a75e5bf 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -131,7 +131,7 @@ __syscall_start:
.long sys_wait4
/* 115 */ .long sys_swapoff
.long sys_sysinfo
- .long sys_ipc_wrapper
+ .long sys_ipc
.long sys_fsync
.long sys_sigreturn_wrapper
/* 120 */ .long sys_clone_wrapper
@@ -254,7 +254,7 @@ __syscall_start:
.long sys_fremovexattr
.long sys_tkill
.long sys_sendfile64
-/* 240 */ .long sys_futex_wrapper
+/* 240 */ .long sys_futex
.long sys_sched_setaffinity
.long sys_sched_getaffinity
.long sys_io_setup
@@ -284,7 +284,7 @@ __syscall_start:
.long sys_fstatfs64
.long sys_tgkill
.long sys_utimes
-/* 270 */ .long sys_arm_fadvise64_64_wrapper
+/* 270 */ .long sys_arm_fadvise64_64
.long sys_pciconfig_iobase
.long sys_pciconfig_read
.long sys_pciconfig_write
@@ -333,7 +333,7 @@ __syscall_start:
.long sys_inotify_init
.long sys_inotify_add_watch
.long sys_inotify_rm_watch
- .long sys_mbind_wrapper
+ .long sys_mbind
/* 320 */ .long sys_get_mempolicy
.long sys_set_mempolicy
__syscall_end:
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index d9fb819bf7cc..2a8d27e18fa7 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -614,6 +614,47 @@ __kuser_helper_start:
/*
* Reference prototype:
*
+ * void __kernel_memory_barrier(void)
+ *
+ * Input:
+ *
+ * lr = return address
+ *
+ * Output:
+ *
+ * none
+ *
+ * Clobbered:
+ *
+ * the Z flag might be lost
+ *
+ * Definition and user space usage example:
+ *
+ * typedef void (__kernel_dmb_t)(void);
+ * #define __kernel_dmb (*(__kernel_dmb_t *)0xffff0fa0)
+ *
+ * Apply any needed memory barrier to preserve consistency with data modified
+ * manually and __kuser_cmpxchg usage.
+ *
+ * This could be used as follows:
+ *
+ * #define __kernel_dmb() \
+ * asm volatile ( "mov r0, #0xffff0fff; mov lr, pc; sub pc, r0, #95" \
+ * : : : "lr","cc" )
+ */
+
+__kuser_memory_barrier: @ 0xffff0fa0
+
+#if __LINUX_ARM_ARCH__ >= 6 && defined(CONFIG_SMP)
+ mcr p15, 0, r0, c7, c10, 5 @ dmb
+#endif
+ mov pc, lr
+
+ .align 5
+
+/*
+ * Reference prototype:
+ *
* int __kernel_cmpxchg(int oldval, int newval, int *ptr)
*
* Input:
@@ -642,6 +683,8 @@ __kuser_helper_start:
* The C flag is also set if *ptr was changed to allow for assembly
* optimization in the calling code.
*
+ * Note: this routine already includes memory barriers as needed.
+ *
* For example, a user space atomic_add implementation could look like this:
*
* #define atomic_add(ptr, val) \
@@ -698,10 +741,16 @@ __kuser_cmpxchg: @ 0xffff0fc0
#else
+#ifdef CONFIG_SMP
+ mcr p15, 0, r0, c7, c10, 5 @ dmb
+#endif
ldrex r3, [r2]
subs r3, r3, r0
strexeq r3, r1, [r2]
rsbs r0, r3, #0
+#ifdef CONFIG_SMP
+ mcr p15, 0, r0, c7, c10, 5 @ dmb
+#endif
mov pc, lr
#endif
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index f7f183075237..e2b42997ad33 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -145,7 +145,7 @@ ENTRY(vector_swi)
#endif
enable_irq
- str r4, [sp, #-S_OFF]! @ push fifth arg
+ stmdb sp!, {r4, r5} @ push fifth and sixth args
get_thread_info tsk
ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing
@@ -204,7 +204,7 @@ ENTRY(sys_call_table)
* Special system call wrappers
*/
@ r0 = syscall number
-@ r5 = syscall table
+@ r8 = syscall table
.type sys_syscall, #function
sys_syscall:
eor scno, r0, #__NR_SYSCALL_BASE
@@ -255,22 +255,6 @@ sys_sigaltstack_wrapper:
ldr r2, [sp, #S_OFF + S_SP]
b do_sigaltstack
-sys_futex_wrapper:
- str r5, [sp, #4] @ push sixth arg
- b sys_futex
-
-sys_arm_fadvise64_64_wrapper:
- str r5, [sp, #4] @ push r5 to stack
- b sys_arm_fadvise64_64
-
-sys_mbind_wrapper:
- str r5, [sp, #4]
- b sys_mbind
-
-sys_ipc_wrapper:
- str r5, [sp, #4] @ push sixth arg
- b sys_ipc
-
/*
* Note: off_4k (r5) is always units of 4K. If we can't do the requested
* offset, we return EINVAL.
diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c
index 07892f4012d8..277498ae5b6c 100644
--- a/arch/arm/mach-pxa/mainstone.c
+++ b/arch/arm/mach-pxa/mainstone.c
@@ -43,6 +43,7 @@
#include <asm/arch/pxafb.h>
#include <asm/arch/mmc.h>
#include <asm/arch/irda.h>
+#include <asm/arch/ohci.h>
#include "generic.h"
@@ -393,6 +394,25 @@ static struct platform_device *platform_devices[] __initdata = {
&mst_flash_device[1],
};
+static int mainstone_ohci_init(struct device *dev)
+{
+ /* setup Port1 GPIO pin. */
+ pxa_gpio_mode( 88 | GPIO_ALT_FN_1_IN); /* USBHPWR1 */
+ pxa_gpio_mode( 89 | GPIO_ALT_FN_2_OUT); /* USBHPEN1 */
+
+ /* Set the Power Control Polarity Low and Power Sense
+ Polarity Low to active low. */
+ UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) &
+ ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE);
+
+ return 0;
+}
+
+static struct pxaohci_platform_data mainstone_ohci_platform_data = {
+ .port_mode = PMM_PERPORT_MODE,
+ .init = mainstone_ohci_init,
+};
+
static void __init mainstone_init(void)
{
int SW7 = 0; /* FIXME: get from SCR (Mst doc section 3.2.1.1) */
@@ -424,6 +444,7 @@ static void __init mainstone_init(void)
pxa_set_mci_info(&mainstone_mci_platform_data);
pxa_set_ficp_info(&mainstone_ficp_platform_data);
+ pxa_set_ohci_info(&mainstone_ohci_platform_data);
}
diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c
index c722a9a91fcc..b41b1efaa2cf 100644
--- a/arch/arm/mach-pxa/pxa27x.c
+++ b/arch/arm/mach-pxa/pxa27x.c
@@ -21,6 +21,7 @@
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/arch/pxa-regs.h>
+#include <asm/arch/ohci.h>
#include "generic.h"
@@ -194,6 +195,11 @@ static struct platform_device ohci_device = {
.resource = pxa27x_ohci_resources,
};
+void __init pxa_set_ohci_info(struct pxaohci_platform_data *info)
+{
+ ohci_device.dev.platform_data = info;
+}
+
static struct platform_device *devices[] __initdata = {
&ohci_device,
};
diff --git a/arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c b/arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c
index 04a405345203..2b62dee35c6c 100644
--- a/arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c
+++ b/arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c
@@ -177,9 +177,10 @@ static unsigned int nforce2_fsb_read(int bootfsb)
*/
static int nforce2_set_fsb(unsigned int fsb)
{
- u32 pll, temp = 0;
+ u32 temp = 0;
unsigned int tfsb;
int diff;
+ int pll = 0;
if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) {
printk(KERN_ERR "cpufreq: FSB %d is out of range!\n", fsb);
diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c
index 68a1fc87f4ca..0fbbd4c1072e 100644
--- a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c
+++ b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c
@@ -45,7 +45,7 @@
#define PFX "powernow-k8: "
#define BFX PFX "BIOS error: "
-#define VERSION "version 1.50.4"
+#define VERSION "version 1.60.0"
#include "powernow-k8.h"
/* serialize freq changes */
@@ -216,10 +216,10 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid)
do {
wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
- if (i++ > 100) {
- printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");
- return 1;
- }
+ if (i++ > 100) {
+ printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");
+ return 1;
+ }
} while (query_current_values_with_pending_wait(data));
if (savefid != data->currfid) {
@@ -336,7 +336,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
/* Phase 2 - core frequency transition */
static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
{
- u32 vcoreqfid, vcocurrfid, vcofiddiff, savevid = data->currvid;
+ u32 vcoreqfid, vcocurrfid, vcofiddiff, fid_interval, savevid = data->currvid;
if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
printk(KERN_ERR PFX "ph2: illegal lo-lo transition 0x%x 0x%x\n",
@@ -359,9 +359,11 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
: vcoreqfid - vcocurrfid;
while (vcofiddiff > 2) {
+ (data->currfid & 1) ? (fid_interval = 1) : (fid_interval = 2);
+
if (reqfid > data->currfid) {
if (data->currfid > LO_FID_TABLE_TOP) {
- if (write_new_fid(data, data->currfid + 2)) {
+ if (write_new_fid(data, data->currfid + fid_interval)) {
return 1;
}
} else {
@@ -371,7 +373,7 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
}
}
} else {
- if (write_new_fid(data, data->currfid - 2))
+ if (write_new_fid(data, data->currfid - fid_interval))
return 1;
}
@@ -464,7 +466,7 @@ static int check_supported_cpu(unsigned int cpu)
set_cpus_allowed(current, cpumask_of_cpu(cpu));
if (smp_processor_id() != cpu) {
- printk(KERN_ERR "limiting to cpu %u failed\n", cpu);
+ printk(KERN_ERR PFX "limiting to cpu %u failed\n", cpu);
goto out;
}
@@ -474,7 +476,7 @@ static int check_supported_cpu(unsigned int cpu)
eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
((eax & CPUID_XFAM) != CPUID_XFAM_K8) ||
- ((eax & CPUID_XMOD) > CPUID_XMOD_REV_F)) {
+ ((eax & CPUID_XMOD) > CPUID_XMOD_REV_G)) {
printk(KERN_INFO PFX "Processor cpuid %x not supported\n", eax);
goto out;
}
@@ -517,22 +519,24 @@ static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8
printk(KERN_ERR BFX "maxvid exceeded with pstate %d\n", j);
return -ENODEV;
}
- if ((pst[j].fid > MAX_FID)
- || (pst[j].fid & 1)
- || (j && (pst[j].fid < HI_FID_TABLE_BOTTOM))) {
+ if (pst[j].fid > MAX_FID) {
+ printk(KERN_ERR BFX "maxfid exceeded with pstate %d\n", j);
+ return -ENODEV;
+ }
+ if (j && (pst[j].fid < HI_FID_TABLE_BOTTOM)) {
/* Only first fid is allowed to be in "low" range */
- printk(KERN_ERR PFX "two low fids - %d : 0x%x\n", j, pst[j].fid);
+ printk(KERN_ERR BFX "two low fids - %d : 0x%x\n", j, pst[j].fid);
return -EINVAL;
}
if (pst[j].fid < lastfid)
lastfid = pst[j].fid;
}
if (lastfid & 1) {
- printk(KERN_ERR PFX "lastfid invalid\n");
+ printk(KERN_ERR BFX "lastfid invalid\n");
return -EINVAL;
}
if (lastfid > LO_FID_TABLE_TOP)
- printk(KERN_INFO PFX "first fid not from lo freq table\n");
+ printk(KERN_INFO BFX "first fid not from lo freq table\n");
return 0;
}
@@ -631,7 +635,7 @@ static int find_psb_table(struct powernow_k8_data *data)
dprintk("table vers: 0x%x\n", psb->tableversion);
if (psb->tableversion != PSB_VERSION_1_4) {
- printk(KERN_INFO BFX "PSB table is not v1.4\n");
+ printk(KERN_ERR BFX "PSB table is not v1.4\n");
return -ENODEV;
}
@@ -689,7 +693,7 @@ static int find_psb_table(struct powernow_k8_data *data)
* BIOS and Kernel Developer's Guide, which is available on
* www.amd.com
*/
- printk(KERN_INFO PFX "BIOS error - no PSB or ACPI _PSS objects\n");
+ printk(KERN_ERR PFX "BIOS error - no PSB or ACPI _PSS objects\n");
return -ENODEV;
}
@@ -912,7 +916,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi
set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
if (smp_processor_id() != pol->cpu) {
- printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+ printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
goto err_out;
}
@@ -982,6 +986,9 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
cpumask_t oldmask = CPU_MASK_ALL;
int rc, i;
+ if (!cpu_online(pol->cpu))
+ return -ENODEV;
+
if (!check_supported_cpu(pol->cpu))
return -ENODEV;
@@ -1021,7 +1028,7 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
if (smp_processor_id() != pol->cpu) {
- printk(KERN_ERR "limiting to cpu %u failed\n", pol->cpu);
+ printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
goto err_out;
}
@@ -1162,10 +1169,9 @@ static void __exit powernowk8_exit(void)
cpufreq_unregister_driver(&cpufreq_amd64_driver);
}
-MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and Mark Langsdorf <mark.langsdorf@amd.com.");
+MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and Mark Langsdorf <mark.langsdorf@amd.com>");
MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
MODULE_LICENSE("GPL");
late_initcall(powernowk8_init);
module_exit(powernowk8_exit);
-
diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k8.h b/arch/i386/kernel/cpu/cpufreq/powernow-k8.h
index b1e85bb36396..d0de37d58e9a 100644
--- a/arch/i386/kernel/cpu/cpufreq/powernow-k8.h
+++ b/arch/i386/kernel/cpu/cpufreq/powernow-k8.h
@@ -42,7 +42,7 @@ struct powernow_k8_data {
#define CPUID_XFAM 0x0ff00000 /* extended family */
#define CPUID_XFAM_K8 0
#define CPUID_XMOD 0x000f0000 /* extended model */
-#define CPUID_XMOD_REV_F 0x00040000
+#define CPUID_XMOD_REV_G 0x00060000
#define CPUID_USE_XFAM_XMOD 0x00000f00
#define CPUID_GET_MAX_CAPABILITIES 0x80000000
#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
@@ -86,13 +86,14 @@ struct powernow_k8_data {
* low fid table
* - lowest entry in the high fid table must be a <= 200MHz + 2 * the entry
* in the low fid table
- * - the parts can only step at 200 MHz intervals, so 1.9 GHz is never valid
+ * - the parts can only step at <= 200 MHz intervals, odd fid values are
+ * supported in revision G and later revisions.
* - lowest frequency must be >= interprocessor hypertransport link speed
* (only applies to MP systems obviously)
*/
/* fids (frequency identifiers) are arranged in 2 tables - lo and hi */
-#define LO_FID_TABLE_TOP 6 /* fid values marking the boundary */
+#define LO_FID_TABLE_TOP 7 /* fid values marking the boundary */
#define HI_FID_TABLE_BOTTOM 8 /* between the low and high tables */
#define LO_VCOFREQ_TABLE_TOP 1400 /* corresponding vco frequency values */
@@ -106,7 +107,7 @@ struct powernow_k8_data {
#define MIN_FREQ 800 /* Min and max freqs, per spec */
#define MAX_FREQ 5000
-#define INVALID_FID_MASK 0xffffffc1 /* not a valid fid if these bits are set */
+#define INVALID_FID_MASK 0xffffffc0 /* not a valid fid if these bits are set */
#define INVALID_VID_MASK 0xffffffc0 /* not a valid vid if these bits are set */
#define VID_OFF 0x3f
diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c b/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c
index 5b7d18a06afa..b425cd3d1838 100644
--- a/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c
+++ b/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c
@@ -40,6 +40,7 @@ static struct pci_dev *speedstep_chipset_dev;
*/
static unsigned int speedstep_processor = 0;
+static u32 pmbase;
/*
* There are only two frequency states for each processor. Values
@@ -56,34 +57,47 @@ static struct cpufreq_frequency_table speedstep_freqs[] = {
/**
- * speedstep_set_state - set the SpeedStep state
- * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
+ * speedstep_find_register - read the PMBASE address
*
- * Tries to change the SpeedStep state.
+ * Returns: -ENODEV if no register could be found
*/
-static void speedstep_set_state (unsigned int state)
+static int speedstep_find_register (void)
{
- u32 pmbase;
- u8 pm2_blk;
- u8 value;
- unsigned long flags;
-
- if (!speedstep_chipset_dev || (state > 0x1))
- return;
+ if (!speedstep_chipset_dev)
+ return -ENODEV;
/* get PMBASE */
pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
if (!(pmbase & 0x01)) {
printk(KERN_ERR "speedstep-ich: could not find speedstep register\n");
- return;
+ return -ENODEV;
}
pmbase &= 0xFFFFFFFE;
if (!pmbase) {
printk(KERN_ERR "speedstep-ich: could not find speedstep register\n");
- return;
+ return -ENODEV;
}
+ dprintk("pmbase is 0x%x\n", pmbase);
+ return 0;
+}
+
+/**
+ * speedstep_set_state - set the SpeedStep state
+ * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
+ *
+ * Tries to change the SpeedStep state.
+ */
+static void speedstep_set_state (unsigned int state)
+{
+ u8 pm2_blk;
+ u8 value;
+ unsigned long flags;
+
+ if (state > 0x1)
+ return;
+
/* Disable IRQs */
local_irq_save(flags);
@@ -315,10 +329,11 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
cpus_allowed = current->cpus_allowed;
set_cpus_allowed(current, policy->cpus);
- /* detect low and high frequency */
+ /* detect low and high frequency and transition latency */
result = speedstep_get_freqs(speedstep_processor,
&speedstep_freqs[SPEEDSTEP_LOW].frequency,
&speedstep_freqs[SPEEDSTEP_HIGH].frequency,
+ &policy->cpuinfo.transition_latency,
&speedstep_set_state);
set_cpus_allowed(current, cpus_allowed);
if (result)
@@ -335,7 +350,6 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
/* cpuinfo and default policy values */
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
- policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
policy->cur = speed;
result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
@@ -400,6 +414,9 @@ static int __init speedstep_init(void)
return -EINVAL;
}
+ if (speedstep_find_register())
+ return -ENODEV;
+
return cpufreq_register_driver(&speedstep_driver);
}
diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c
index d368b3f5fce8..7c47005a1805 100644
--- a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c
+++ b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c
@@ -320,11 +320,13 @@ EXPORT_SYMBOL_GPL(speedstep_detect_processor);
unsigned int speedstep_get_freqs(unsigned int processor,
unsigned int *low_speed,
unsigned int *high_speed,
+ unsigned int *transition_latency,
void (*set_state) (unsigned int state))
{
unsigned int prev_speed;
unsigned int ret = 0;
unsigned long flags;
+ struct timeval tv1, tv2;
if ((!processor) || (!low_speed) || (!high_speed) || (!set_state))
return -EINVAL;
@@ -337,7 +339,7 @@ unsigned int speedstep_get_freqs(unsigned int processor,
return -EIO;
dprintk("previous speed is %u\n", prev_speed);
-
+
local_irq_save(flags);
/* switch to low state */
@@ -350,8 +352,17 @@ unsigned int speedstep_get_freqs(unsigned int processor,
dprintk("low speed is %u\n", *low_speed);
+ /* start latency measurement */
+ if (transition_latency)
+ do_gettimeofday(&tv1);
+
/* switch to high state */
set_state(SPEEDSTEP_HIGH);
+
+ /* end latency measurement */
+ if (transition_latency)
+ do_gettimeofday(&tv2);
+
*high_speed = speedstep_get_processor_frequency(processor);
if (!*high_speed) {
ret = -EIO;
@@ -369,6 +380,25 @@ unsigned int speedstep_get_freqs(unsigned int processor,
if (*high_speed != prev_speed)
set_state(SPEEDSTEP_LOW);
+ if (transition_latency) {
+ *transition_latency = (tv2.tv_sec - tv1.tv_sec) * USEC_PER_SEC +
+ tv2.tv_usec - tv1.tv_usec;
+ dprintk("transition latency is %u uSec\n", *transition_latency);
+
+ /* convert uSec to nSec and add 20% for safety reasons */
+ *transition_latency *= 1200;
+
+ /* check if the latency measurement is too high or too low
+ * and set it to a safe value (500uSec) in that case
+ */
+ if (*transition_latency > 10000000 || *transition_latency < 50000) {
+ printk (KERN_WARNING "speedstep: frequency transition measured seems out of "
+ "range (%u nSec), falling back to a safe one of %u nSec.\n",
+ *transition_latency, 500000);
+ *transition_latency = 500000;
+ }
+ }
+
out:
local_irq_restore(flags);
return (ret);
diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h
index 261a2c9b7f6b..6a727fd3a77e 100644
--- a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h
+++ b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h
@@ -44,4 +44,5 @@ extern unsigned int speedstep_get_processor_frequency(unsigned int processor);
extern unsigned int speedstep_get_freqs(unsigned int processor,
unsigned int *low_speed,
unsigned int *high_speed,
+ unsigned int *transition_latency,
void (*set_state) (unsigned int state));
diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c b/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c
index 2718fb6f6aba..28cc5d524afc 100644
--- a/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c
+++ b/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c
@@ -269,6 +269,7 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy)
result = speedstep_get_freqs(speedstep_processor,
&speedstep_freqs[SPEEDSTEP_LOW].frequency,
&speedstep_freqs[SPEEDSTEP_HIGH].frequency,
+ NULL,
&speedstep_set_state);
if (result) {
diff --git a/arch/i386/kernel/cpu/proc.c b/arch/i386/kernel/cpu/proc.c
index e7921315ae9d..6d91b274589c 100644
--- a/arch/i386/kernel/cpu/proc.c
+++ b/arch/i386/kernel/cpu/proc.c
@@ -3,6 +3,7 @@
#include <linux/string.h>
#include <asm/semaphore.h>
#include <linux/seq_file.h>
+#include <linux/cpufreq.h>
/*
* Get CPU information for use by the procfs.
@@ -86,8 +87,11 @@ static int show_cpuinfo(struct seq_file *m, void *v)
seq_printf(m, "stepping\t: unknown\n");
if ( cpu_has(c, X86_FEATURE_TSC) ) {
+ unsigned int freq = cpufreq_quick_get(n);
+ if (!freq)
+ freq = cpu_khz;
seq_printf(m, "cpu MHz\t\t: %u.%03u\n",
- cpu_khz / 1000, (cpu_khz % 1000));
+ freq / 1000, (freq % 1000));
}
/* Cache size */
diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c
index df6c2bcde067..2333aead0563 100644
--- a/arch/i386/kernel/process.c
+++ b/arch/i386/kernel/process.c
@@ -554,7 +554,9 @@ int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
struct pt_regs ptregs;
ptregs = *(struct pt_regs *)
- ((unsigned long)tsk->thread_info+THREAD_SIZE - sizeof(ptregs));
+ ((unsigned long)tsk->thread_info +
+ /* see comments in copy_thread() about -8 */
+ THREAD_SIZE - sizeof(ptregs) - 8);
ptregs.xcs &= 0xffff;
ptregs.xds &= 0xffff;
ptregs.xes &= 0xffff;
diff --git a/arch/i386/pci/Makefile b/arch/i386/pci/Makefile
index ead6122dd06d..5461d4d5ea1e 100644
--- a/arch/i386/pci/Makefile
+++ b/arch/i386/pci/Makefile
@@ -1,7 +1,7 @@
obj-y := i386.o
obj-$(CONFIG_PCI_BIOS) += pcbios.o
-obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o
+obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o direct.o
obj-$(CONFIG_PCI_DIRECT) += direct.o
pci-y := fixup.o
diff --git a/arch/i386/pci/mmconfig.c b/arch/i386/pci/mmconfig.c
index 70a9cc132cf7..4bb4d4b0f73a 100644
--- a/arch/i386/pci/mmconfig.c
+++ b/arch/i386/pci/mmconfig.c
@@ -155,7 +155,7 @@ static __init void unreachable_devices(void)
addr = get_base_addr(0, 0, PCI_DEVFN(i, 0));
if (addr != 0)
pci_exp_set_dev_base(addr, 0, PCI_DEVFN(i, 0));
- if (addr == 0 || readl((u32 __iomem *)addr) != val1)
+ if (addr == 0 || readl((u32 __iomem *)mmcfg_virt_addr) != val1)
set_bit(i, fallback_slots);
spin_unlock_irqrestore(&pci_config_lock, flags);
}
diff --git a/arch/ia64/configs/sn2_defconfig b/arch/ia64/configs/sn2_defconfig
index e1924cc9687b..ff8bb3770c9d 100644
--- a/arch/ia64/configs/sn2_defconfig
+++ b/arch/ia64/configs/sn2_defconfig
@@ -113,7 +113,7 @@ CONFIG_IOSAPIC=y
CONFIG_IA64_SGI_SN_XP=m
CONFIG_FORCE_MAX_ZONEORDER=17
CONFIG_SMP=y
-CONFIG_NR_CPUS=512
+CONFIG_NR_CPUS=1024
# CONFIG_HOTPLUG_CPU is not set
CONFIG_SCHED_SMT=y
CONFIG_PREEMPT=y
diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c
index 5add0bcf87a7..088e5dded8dc 100644
--- a/arch/ia64/kernel/setup.c
+++ b/arch/ia64/kernel/setup.c
@@ -43,6 +43,7 @@
#include <linux/initrd.h>
#include <linux/platform.h>
#include <linux/pm.h>
+#include <linux/cpufreq.h>
#include <asm/ia32.h>
#include <asm/machvec.h>
@@ -517,6 +518,7 @@ show_cpuinfo (struct seq_file *m, void *v)
char family[32], features[128], *cp, sep;
struct cpuinfo_ia64 *c = v;
unsigned long mask;
+ unsigned int proc_freq;
int i;
mask = c->features;
@@ -549,6 +551,10 @@ show_cpuinfo (struct seq_file *m, void *v)
sprintf(cp, " 0x%lx", mask);
}
+ proc_freq = cpufreq_quick_get(cpunum);
+ if (!proc_freq)
+ proc_freq = c->proc_freq / 1000;
+
seq_printf(m,
"processor : %d\n"
"vendor : %s\n"
@@ -565,7 +571,7 @@ show_cpuinfo (struct seq_file *m, void *v)
"BogoMIPS : %lu.%02lu\n",
cpunum, c->vendor, family, c->model, c->revision, c->archrev,
features, c->ppn, c->number,
- c->proc_freq / 1000000, c->proc_freq % 1000000,
+ proc_freq / 1000, proc_freq % 1000,
c->itc_freq / 1000000, c->itc_freq % 1000000,
lpj*HZ/500000, (lpj*HZ/5000) % 100);
#ifdef CONFIG_SMP
diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index 5b7e736f3b49..028a2b95936c 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -249,3 +249,32 @@ time_init (void)
*/
set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec);
}
+
+#define SMALLUSECS 100
+
+void
+udelay (unsigned long usecs)
+{
+ unsigned long start;
+ unsigned long cycles;
+ unsigned long smallusecs;
+
+ /*
+ * Execute the non-preemptible delay loop (because the ITC might
+ * not be synchronized between CPUS) in relatively short time
+ * chunks, allowing preemption between the chunks.
+ */
+ while (usecs > 0) {
+ smallusecs = (usecs > SMALLUSECS) ? SMALLUSECS : usecs;
+ preempt_disable();
+ cycles = smallusecs*local_cpu_data->cyc_per_usec;
+ start = ia64_get_itc();
+
+ while (ia64_get_itc() - start < cycles)
+ cpu_relax();
+
+ preempt_enable();
+ usecs -= smallusecs;
+ }
+}
+EXPORT_SYMBOL(udelay);
diff --git a/arch/ia64/kernel/uncached.c b/arch/ia64/kernel/uncached.c
index c6d40446c2c4..b631cf86ed44 100644
--- a/arch/ia64/kernel/uncached.c
+++ b/arch/ia64/kernel/uncached.c
@@ -53,7 +53,7 @@ static void uncached_ipi_visibility(void *data)
if ((status != PAL_VISIBILITY_OK) &&
(status != PAL_VISIBILITY_OK_REMOTE_NEEDED))
printk(KERN_DEBUG "pal_prefetch_visibility() returns %i on "
- "CPU %i\n", status, get_cpu());
+ "CPU %i\n", status, raw_smp_processor_id());
}
@@ -63,7 +63,7 @@ static void uncached_ipi_mc_drain(void *data)
status = ia64_pal_mc_drain();
if (status)
printk(KERN_WARNING "ia64_pal_mc_drain() failed with %i on "
- "CPU %i\n", status, get_cpu());
+ "CPU %i\n", status, raw_smp_processor_id());
}
@@ -105,7 +105,7 @@ uncached_get_new_chunk(struct gen_pool *poolp)
status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL);
dprintk(KERN_INFO "pal_prefetch_visibility() returns %i on cpu %i\n",
- status, get_cpu());
+ status, raw_smp_processor_id());
if (!status) {
status = smp_call_function(uncached_ipi_visibility, NULL, 0, 1);
diff --git a/arch/ia64/kernel/vmlinux.lds.S b/arch/ia64/kernel/vmlinux.lds.S
index 30d8564e9603..73af6267d2ef 100644
--- a/arch/ia64/kernel/vmlinux.lds.S
+++ b/arch/ia64/kernel/vmlinux.lds.S
@@ -177,6 +177,9 @@ SECTIONS
}
. = ALIGN(PAGE_SIZE); /* make sure the gate page doesn't expose kernel data */
+ .data.read_mostly : AT(ADDR(.data.read_mostly) - LOAD_OFFSET)
+ { *(.data.read_mostly) }
+
.data.cacheline_aligned : AT(ADDR(.data.cacheline_aligned) - LOAD_OFFSET)
{ *(.data.cacheline_aligned) }
diff --git a/arch/ia64/sn/kernel/sn2/sn2_smp.c b/arch/ia64/sn/kernel/sn2/sn2_smp.c
index 5d54f5f4e926..471bbaa65d1b 100644
--- a/arch/ia64/sn/kernel/sn2/sn2_smp.c
+++ b/arch/ia64/sn/kernel/sn2/sn2_smp.c
@@ -202,7 +202,7 @@ sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start,
unsigned long end, unsigned long nbits)
{
int i, opt, shub1, cnode, mynasid, cpu, lcpu = 0, nasid, flushed = 0;
- int mymm = (mm == current->active_mm);
+ int mymm = (mm == current->active_mm && current->mm);
volatile unsigned long *ptc0, *ptc1;
unsigned long itc, itc2, flags, data0 = 0, data1 = 0, rr_value;
short nasids[MAX_NUMNODES], nix;
diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c
index 330cf84d21fe..60353f5acc48 100644
--- a/arch/mips/kernel/linux32.c
+++ b/arch/mips/kernel/linux32.c
@@ -420,7 +420,7 @@ asmlinkage ssize_t sys32_pread(unsigned int fd, char * buf,
goto out;
pos = merge_64(a4, a5);
ret = rw_verify_area(READ, file, &pos, count);
- if (ret)
+ if (ret < 0)
goto out;
ret = -EINVAL;
if (!file->f_op || !(read = file->f_op->read))
@@ -455,7 +455,7 @@ asmlinkage ssize_t sys32_pwrite(unsigned int fd, const char * buf,
goto out;
pos = merge_64(a4, a5);
ret = rw_verify_area(WRITE, file, &pos, count);
- if (ret)
+ if (ret < 0)
goto out;
ret = -EINVAL;
if (!file->f_op || !(write = file->f_op->write))
diff --git a/arch/powerpc/configs/cell_defconfig b/arch/powerpc/configs/cell_defconfig
index 4b433411b9e3..b657f7e44762 100644
--- a/arch/powerpc/configs/cell_defconfig
+++ b/arch/powerpc/configs/cell_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.15-rc1
-# Tue Nov 15 14:36:20 2005
+# Linux kernel version: 2.6.15-rc5
+# Tue Dec 20 15:59:26 2005
#
CONFIG_PPC64=y
CONFIG_64BIT=y
@@ -53,6 +53,7 @@ CONFIG_KOBJECT_UEVENT=y
# CONFIG_IKCONFIG is not set
# CONFIG_CPUSETS is not set
CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
@@ -151,7 +152,7 @@ CONFIG_FLATMEM_MANUAL=y
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
# CONFIG_SPARSEMEM_STATIC is not set
-CONFIG_SPLIT_PTLOCK_CPUS=4096
+CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_PPC_64K_PAGES is not set
CONFIG_SCHED_SMT=y
CONFIG_PROC_DEVICETREE=y
diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig
index e7c23e3902b8..3c22ccb18519 100644
--- a/arch/powerpc/configs/g5_defconfig
+++ b/arch/powerpc/configs/g5_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.15-rc1
-# Tue Nov 15 14:39:20 2005
+# Linux kernel version: 2.6.15-rc5
+# Tue Dec 20 15:59:30 2005
#
CONFIG_PPC64=y
CONFIG_64BIT=y
@@ -53,6 +53,7 @@ CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
# CONFIG_CPUSETS is not set
CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
@@ -162,7 +163,7 @@ CONFIG_FLATMEM_MANUAL=y
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
# CONFIG_SPARSEMEM_STATIC is not set
-CONFIG_SPLIT_PTLOCK_CPUS=4096
+CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_PPC_64K_PAGES is not set
# CONFIG_SCHED_SMT is not set
CONFIG_PROC_DEVICETREE=y
@@ -1203,6 +1204,7 @@ CONFIG_USB_MON=y
CONFIG_USB_SERIAL=m
CONFIG_USB_SERIAL_GENERIC=y
# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_ANYDATA is not set
CONFIG_USB_SERIAL_BELKIN=m
CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
# CONFIG_USB_SERIAL_CP2101 is not set
@@ -1233,7 +1235,6 @@ CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
CONFIG_USB_SERIAL_KLSI=m
CONFIG_USB_SERIAL_KOBIL_SCT=m
CONFIG_USB_SERIAL_MCT_U232=m
-# CONFIG_USB_SERIAL_NOKIA_DKU2 is not set
CONFIG_USB_SERIAL_PL2303=m
# CONFIG_USB_SERIAL_HP4X is not set
CONFIG_USB_SERIAL_SAFE=m
diff --git a/arch/powerpc/configs/iseries_defconfig b/arch/powerpc/configs/iseries_defconfig
index 5d0866707a75..751a622fb7a7 100644
--- a/arch/powerpc/configs/iseries_defconfig
+++ b/arch/powerpc/configs/iseries_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.15-rc1
-# Tue Nov 15 14:38:09 2005
+# Linux kernel version: 2.6.15-rc5
+# Tue Dec 20 15:59:32 2005
#
CONFIG_PPC64=y
CONFIG_64BIT=y
@@ -55,6 +55,7 @@ CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
# CONFIG_CPUSETS is not set
CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
@@ -144,7 +145,7 @@ CONFIG_FLATMEM_MANUAL=y
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
# CONFIG_SPARSEMEM_STATIC is not set
-CONFIG_SPLIT_PTLOCK_CPUS=4096
+CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_PPC_64K_PAGES is not set
# CONFIG_SCHED_SMT is not set
CONFIG_PROC_DEVICETREE=y
diff --git a/arch/powerpc/configs/maple_defconfig b/arch/powerpc/configs/maple_defconfig
index 92e42613ef06..07b6d3d23360 100644
--- a/arch/powerpc/configs/maple_defconfig
+++ b/arch/powerpc/configs/maple_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.15-rc1
-# Tue Nov 15 14:38:58 2005
+# Linux kernel version: 2.6.15-rc5
+# Tue Dec 20 15:59:36 2005
#
CONFIG_PPC64=y
CONFIG_64BIT=y
@@ -53,6 +53,7 @@ CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
# CONFIG_CPUSETS is not set
CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
@@ -149,7 +150,7 @@ CONFIG_FLATMEM_MANUAL=y
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
# CONFIG_SPARSEMEM_STATIC is not set
-CONFIG_SPLIT_PTLOCK_CPUS=4096
+CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_PPC_64K_PAGES is not set
# CONFIG_SCHED_SMT is not set
CONFIG_PROC_DEVICETREE=y
@@ -242,7 +243,6 @@ CONFIG_TCP_CONG_BIC=y
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set
-# CONFIG_NET_CLS_ROUTE is not set
#
# Network testing
@@ -794,6 +794,7 @@ CONFIG_USB_SERIAL=y
# CONFIG_USB_SERIAL_CONSOLE is not set
CONFIG_USB_SERIAL_GENERIC=y
# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_ANYDATA is not set
# CONFIG_USB_SERIAL_BELKIN is not set
# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
# CONFIG_USB_SERIAL_CP2101 is not set
@@ -824,7 +825,6 @@ CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
# CONFIG_USB_SERIAL_KLSI is not set
# CONFIG_USB_SERIAL_KOBIL_SCT is not set
# CONFIG_USB_SERIAL_MCT_U232 is not set
-# CONFIG_USB_SERIAL_NOKIA_DKU2 is not set
# CONFIG_USB_SERIAL_PL2303 is not set
# CONFIG_USB_SERIAL_HP4X is not set
# CONFIG_USB_SERIAL_SAFE is not set
diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig
index b5ba3bbd96fb..509399eab6f5 100644
--- a/arch/powerpc/configs/ppc64_defconfig
+++ b/arch/powerpc/configs/ppc64_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.15-rc1
-# Fri Nov 18 16:23:24 2005
+# Linux kernel version: 2.6.15-rc5
+# Tue Dec 20 15:59:38 2005
#
CONFIG_PPC64=y
CONFIG_64BIT=y
@@ -54,6 +54,7 @@ CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_CPUSETS=y
CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
@@ -176,7 +177,7 @@ CONFIG_HAVE_MEMORY_PRESENT=y
# CONFIG_SPARSEMEM_STATIC is not set
CONFIG_SPARSEMEM_EXTREME=y
# CONFIG_MEMORY_HOTPLUG is not set
-CONFIG_SPLIT_PTLOCK_CPUS=4096
+CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_PPC_64K_PAGES is not set
# CONFIG_SCHED_SMT is not set
CONFIG_PROC_DEVICETREE=y
diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig
index b589b196eb3f..a50ce0fa9243 100644
--- a/arch/powerpc/configs/pseries_defconfig
+++ b/arch/powerpc/configs/pseries_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.15-rc1
-# Tue Nov 15 14:36:55 2005
+# Linux kernel version: 2.6.15-rc5
+# Tue Dec 20 15:59:40 2005
#
CONFIG_PPC64=y
CONFIG_64BIT=y
@@ -55,6 +55,7 @@ CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_CPUSETS=y
CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
@@ -163,7 +164,7 @@ CONFIG_HAVE_MEMORY_PRESENT=y
# CONFIG_SPARSEMEM_STATIC is not set
CONFIG_SPARSEMEM_EXTREME=y
# CONFIG_MEMORY_HOTPLUG is not set
-CONFIG_SPLIT_PTLOCK_CPUS=4096
+CONFIG_SPLIT_PTLOCK_CPUS=4
CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID=y
# CONFIG_PPC_64K_PAGES is not set
CONFIG_SCHED_SMT=y
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 2d22bf03484e..bce33a38399f 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -183,8 +183,8 @@ syscall_exit_trace_cont:
ld r13,GPR13(r1) /* returning to usermode */
1: ld r2,GPR2(r1)
li r12,MSR_RI
- andc r10,r10,r12
- mtmsrd r10,1 /* clear MSR.RI */
+ andc r11,r10,r12
+ mtmsrd r11,1 /* clear MSR.RI */
ld r1,GPR1(r1)
mtlr r4
mtcr r5
diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index a33583f3b0e7..a606504678bd 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -514,7 +514,7 @@ void __init htab_initialize(void)
#undef KB
#undef MB
-void __init htab_initialize_secondary(void)
+void htab_initialize_secondary(void)
{
if (!platform_is_lpar())
mtspr(SPRN_SDR1, _SDR1);
diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c
index 72ac18067ece..0377decc0719 100644
--- a/arch/powerpc/platforms/pseries/xics.c
+++ b/arch/powerpc/platforms/pseries/xics.c
@@ -48,11 +48,6 @@ static struct hw_interrupt_type xics_pic = {
.set_affinity = xics_set_affinity
};
-static struct hw_interrupt_type xics_8259_pic = {
- .typename = " XICS/8259",
- .ack = xics_mask_and_ack_irq,
-};
-
/* This is used to map real irq numbers to virtual */
static struct radix_tree_root irq_map = RADIX_TREE_INIT(GFP_ATOMIC);
@@ -367,12 +362,7 @@ int xics_get_irq(struct pt_regs *regs)
/* for sanity, this had better be < NR_IRQS - 16 */
if (vec == xics_irq_8259_cascade_real) {
irq = i8259_irq(regs);
- if (irq == -1) {
- /* Spurious cascaded interrupt. Still must ack xics */
- xics_end_irq(irq_offset_up(xics_irq_8259_cascade));
-
- irq = -1;
- }
+ xics_end_irq(irq_offset_up(xics_irq_8259_cascade));
} else if (vec == XICS_IRQ_SPURIOUS) {
irq = -1;
} else {
@@ -542,6 +532,7 @@ nextnode:
xics_irq_8259_cascade_real = *ireg;
xics_irq_8259_cascade
= virt_irq_create_mapping(xics_irq_8259_cascade_real);
+ i8259_init(0, 0);
of_node_put(np);
}
@@ -565,12 +556,7 @@ nextnode:
#endif /* CONFIG_SMP */
}
- xics_8259_pic.enable = i8259_pic.enable;
- xics_8259_pic.disable = i8259_pic.disable;
- xics_8259_pic.end = i8259_pic.end;
- for (i = 0; i < 16; ++i)
- get_irq_desc(i)->handler = &xics_8259_pic;
- for (; i < NR_IRQS; ++i)
+ for (i = irq_offset_value(); i < NR_IRQS; ++i)
get_irq_desc(i)->handler = &xics_pic;
xics_setup_cpu();
@@ -590,7 +576,6 @@ static int __init xics_setup_i8259(void)
no_action, 0, "8259 cascade", NULL))
printk(KERN_ERR "xics_setup_i8259: couldn't get 8259 "
"cascade\n");
- i8259_init(0, 0);
}
return 0;
}
diff --git a/arch/ppc/platforms/85xx/mpc85xx_cds_common.c b/arch/ppc/platforms/85xx/mpc85xx_cds_common.c
index d8991b88dc9c..5e8cc5ec6ab5 100644
--- a/arch/ppc/platforms/85xx/mpc85xx_cds_common.c
+++ b/arch/ppc/platforms/85xx/mpc85xx_cds_common.c
@@ -130,10 +130,11 @@ mpc85xx_cds_show_cpuinfo(struct seq_file *m)
}
#ifdef CONFIG_CPM2
-static void cpm2_cascade(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t cpm2_cascade(int irq, void *dev_id, struct pt_regs *regs)
{
while((irq = cpm2_get_irq(regs)) >= 0)
__do_IRQ(irq, regs);
+ return IRQ_HANDLED;
}
static struct irqaction cpm2_irqaction = {
diff --git a/arch/ppc/syslib/ppc4xx_dma.c b/arch/ppc/syslib/ppc4xx_dma.c
index f15e64285f96..05ccd598dd4e 100644
--- a/arch/ppc/syslib/ppc4xx_dma.c
+++ b/arch/ppc/syslib/ppc4xx_dma.c
@@ -30,6 +30,7 @@
#include <asm/system.h>
#include <asm/io.h>
+#include <asm/dma.h>
#include <asm/ppc4xx_dma.h>
ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 3cfb8be3ff6d..56c34e7fd4ee 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -55,6 +55,10 @@ config NR_CPUS
depends on SMP
default "32"
+config SPARC
+ bool
+ default y
+
# Identify this as a Sparc32 build
config SPARC32
bool
diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c
index 81c894acd0db..d07ae02101ad 100644
--- a/arch/sparc/kernel/sys_sunos.c
+++ b/arch/sparc/kernel/sys_sunos.c
@@ -894,7 +894,7 @@ asmlinkage long sunos_sysconf (int name)
ret = ARG_MAX;
break;
case _SC_CHILD_MAX:
- ret = CHILD_MAX;
+ ret = -1; /* no limit */
break;
case _SC_CLK_TCK:
ret = HZ;
diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S
index 38938d2e63aa..346c19a949fd 100644
--- a/arch/sparc/kernel/vmlinux.lds.S
+++ b/arch/sparc/kernel/vmlinux.lds.S
@@ -85,19 +85,9 @@ SECTIONS
}
_end = . ;
PROVIDE (end = .);
- /* Stabs debugging sections. */
- .stab 0 : { *(.stab) }
- .stabstr 0 : { *(.stabstr) }
- .stab.excl 0 : { *(.stab.excl) }
- .stab.exclstr 0 : { *(.stab.exclstr) }
- .stab.index 0 : { *(.stab.index) }
- .stab.indexstr 0 : { *(.stab.indexstr) }
- .comment 0 : { *(.comment) }
- .debug 0 : { *(.debug) }
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- .line 0 : { *(.line) }
/DISCARD/ : { *(.exit.text) *(.exit.data) *(.exitcall.exit) }
+
+ STABS_DEBUG
+
+ DWARF_DEBUG
}
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
index 3fded69b1922..c4b7ad70cd7c 100644
--- a/arch/sparc64/Kconfig
+++ b/arch/sparc64/Kconfig
@@ -5,6 +5,10 @@
mainmenu "Linux/UltraSPARC Kernel Configuration"
+config SPARC
+ bool
+ default y
+
config SPARC64
bool
default y
diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile
index 43fe382da078..cad10c5b83d3 100644
--- a/arch/sparc64/Makefile
+++ b/arch/sparc64/Makefile
@@ -17,7 +17,6 @@ CC := $(shell if $(CC) -m64 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then
NEW_GCC := $(call cc-option-yn, -m64 -mcmodel=medlow)
NEW_GAS := $(shell if $(LD) -V 2>&1 | grep 'elf64_sparc' > /dev/null; then echo y; else echo n; fi)
UNDECLARED_REGS := $(shell if $(CC) -c -x assembler /dev/null -Wa,--help | grep undeclared-regs > /dev/null; then echo y; else echo n; fi; )
-INLINE_LIMIT := $(call cc-option-yn, -m64 -finline-limit=100000)
export NEW_GCC
@@ -49,10 +48,6 @@ else
AFLAGS += -m64 -mcpu=ultrasparc $(CC_UNDECL)
endif
-ifeq ($(INLINE_LIMIT),y)
- CFLAGS := $(CFLAGS) -finline-limit=100000
-endif
-
ifeq ($(CONFIG_MCOUNT),y)
CFLAGS := $(CFLAGS) -pg
endif
diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c
index d0592ed54ea5..bfa4aa68312d 100644
--- a/arch/sparc64/kernel/sys_sunos32.c
+++ b/arch/sparc64/kernel/sys_sunos32.c
@@ -854,7 +854,7 @@ asmlinkage s32 sunos_sysconf (int name)
ret = ARG_MAX;
break;
case _SC_CHILD_MAX:
- ret = CHILD_MAX;
+ ret = -1; /* no limit */
break;
case _SC_CLK_TCK:
ret = HZ;
diff --git a/arch/sparc64/kernel/vmlinux.lds.S b/arch/sparc64/kernel/vmlinux.lds.S
index 2af0cf0a8640..467d13a0d5c1 100644
--- a/arch/sparc64/kernel/vmlinux.lds.S
+++ b/arch/sparc64/kernel/vmlinux.lds.S
@@ -90,19 +90,9 @@ SECTIONS
}
_end = . ;
PROVIDE (end = .);
- /* Stabs debugging sections. */
- .stab 0 : { *(.stab) }
- .stabstr 0 : { *(.stabstr) }
- .stab.excl 0 : { *(.stab.excl) }
- .stab.exclstr 0 : { *(.stab.exclstr) }
- .stab.index 0 : { *(.stab.index) }
- .stab.indexstr 0 : { *(.stab.indexstr) }
- .comment 0 : { *(.comment) }
- .debug 0 : { *(.debug) }
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- .line 0 : { *(.line) }
/DISCARD/ : { *(.exit.text) *(.exit.data) *(.exitcall.exit) }
+
+ STABS_DEBUG
+
+ DWARF_DEBUG
}
diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c
index 302efbcba70e..3ab4677395f2 100644
--- a/arch/sparc64/solaris/misc.c
+++ b/arch/sparc64/solaris/misc.c
@@ -353,7 +353,7 @@ asmlinkage int solaris_sysconf(int id)
{
switch (id) {
case SOLARIS_CONFIG_NGROUPS: return NGROUPS_MAX;
- case SOLARIS_CONFIG_CHILD_MAX: return CHILD_MAX;
+ case SOLARIS_CONFIG_CHILD_MAX: return -1; /* no limit */
case SOLARIS_CONFIG_OPEN_FILES: return OPEN_MAX;
case SOLARIS_CONFIG_POSIX_VER: return 199309;
case SOLARIS_CONFIG_PAGESIZE: return PAGE_SIZE;
diff --git a/arch/um/Kconfig b/arch/um/Kconfig
index 563301fe5df8..1eb21de9d1b5 100644
--- a/arch/um/Kconfig
+++ b/arch/um/Kconfig
@@ -289,6 +289,8 @@ source "arch/um/Kconfig.net"
source "drivers/net/Kconfig"
+source "drivers/connector/Kconfig"
+
source "fs/Kconfig"
source "security/Kconfig"
diff --git a/arch/um/Makefile-x86_64 b/arch/um/Makefile-x86_64
index 4f118d5cc2ee..38df311e75dc 100644
--- a/arch/um/Makefile-x86_64
+++ b/arch/um/Makefile-x86_64
@@ -12,3 +12,7 @@ CHECKFLAGS += -m64
ELF_ARCH := i386:x86-64
ELF_FORMAT := elf64-x86-64
+
+# Not on all 64-bit distros /lib is a symlink to /lib64. PLD is an example.
+
+LINK-$(CONFIG_LD_SCRIPT_DYN) += -Wl,-rpath,/lib64
diff --git a/arch/um/include/sysdep-i386/stub.h b/arch/um/include/sysdep-i386/stub.h
index 6ba8cbbe0d36..b492b12b4a10 100644
--- a/arch/um/include/sysdep-i386/stub.h
+++ b/arch/um/include/sysdep-i386/stub.h
@@ -6,8 +6,12 @@
#ifndef __SYSDEP_STUB_H
#define __SYSDEP_STUB_H
+#include <sys/mman.h>
#include <asm/ptrace.h>
#include <asm/unistd.h>
+#include "stub-data.h"
+#include "kern_constants.h"
+#include "uml-config.h"
extern void stub_segv_handler(int sig);
extern void stub_clone_handler(void);
@@ -76,23 +80,22 @@ static inline long stub_syscall5(long syscall, long arg1, long arg2, long arg3,
return ret;
}
-static inline long stub_syscall6(long syscall, long arg1, long arg2, long arg3,
- long arg4, long arg5, long arg6)
+static inline void trap_myself(void)
{
- long ret;
-
- __asm__ volatile ("push %%ebp ; movl %%eax,%%ebp ; movl %1,%%eax ; "
- "int $0x80 ; pop %%ebp"
- : "=a" (ret)
- : "g" (syscall), "b" (arg1), "c" (arg2), "d" (arg3),
- "S" (arg4), "D" (arg5), "0" (arg6));
-
- return ret;
+ __asm("int3");
}
-static inline void trap_myself(void)
+static inline void remap_stack(int fd, unsigned long offset)
{
- __asm("int3");
+ __asm__ volatile ("movl %%eax,%%ebp ; movl %0,%%eax ; int $0x80 ;"
+ "movl %7, %%ebx ; movl %%eax, (%%ebx)"
+ : : "g" (STUB_MMAP_NR), "b" (UML_CONFIG_STUB_DATA),
+ "c" (UM_KERN_PAGE_SIZE),
+ "d" (PROT_READ | PROT_WRITE),
+ "S" (MAP_FIXED | MAP_SHARED), "D" (fd),
+ "a" (offset),
+ "i" (&((struct stub_data *) UML_CONFIG_STUB_DATA)->err)
+ : "memory");
}
#endif
diff --git a/arch/um/include/sysdep-x86_64/stub.h b/arch/um/include/sysdep-x86_64/stub.h
index c41689c13dc9..92e989f81761 100644
--- a/arch/um/include/sysdep-x86_64/stub.h
+++ b/arch/um/include/sysdep-x86_64/stub.h
@@ -6,8 +6,12 @@
#ifndef __SYSDEP_STUB_H
#define __SYSDEP_STUB_H
+#include <sys/mman.h>
#include <asm/unistd.h>
#include <sysdep/ptrace_user.h>
+#include "stub-data.h"
+#include "kern_constants.h"
+#include "uml-config.h"
extern void stub_segv_handler(int sig);
extern void stub_clone_handler(void);
@@ -81,23 +85,23 @@ static inline long stub_syscall5(long syscall, long arg1, long arg2, long arg3,
return ret;
}
-static inline long stub_syscall6(long syscall, long arg1, long arg2, long arg3,
- long arg4, long arg5, long arg6)
+static inline void trap_myself(void)
{
- long ret;
-
- __asm__ volatile ("movq %5,%%r10 ; movq %6,%%r8 ; "
- "movq %7, %%r9; " __syscall : "=a" (ret)
- : "0" (syscall), "D" (arg1), "S" (arg2), "d" (arg3),
- "g" (arg4), "g" (arg5), "g" (arg6)
- : __syscall_clobber, "r10", "r8", "r9" );
-
- return ret;
+ __asm("int3");
}
-static inline void trap_myself(void)
+static inline void remap_stack(long fd, unsigned long offset)
{
- __asm("int3");
+ __asm__ volatile ("movq %4,%%r10 ; movq %5,%%r8 ; "
+ "movq %6, %%r9; " __syscall "; movq %7, %%rbx ; "
+ "movq %%rax, (%%rbx)":
+ : "a" (STUB_MMAP_NR), "D" (UML_CONFIG_STUB_DATA),
+ "S" (UM_KERN_PAGE_SIZE),
+ "d" (PROT_READ | PROT_WRITE),
+ "g" (MAP_FIXED | MAP_SHARED), "g" (fd),
+ "g" (offset),
+ "i" (&((struct stub_data *) UML_CONFIG_STUB_DATA)->err)
+ : __syscall_clobber, "r10", "r8", "r9" );
}
#endif
diff --git a/arch/um/kernel/skas/clone.c b/arch/um/kernel/skas/clone.c
index cb37ce9124a6..47b812b3bca8 100644
--- a/arch/um/kernel/skas/clone.c
+++ b/arch/um/kernel/skas/clone.c
@@ -18,11 +18,10 @@
* on some systems.
*/
-#define STUB_DATA(field) (((struct stub_data *) UML_CONFIG_STUB_DATA)->field)
-
void __attribute__ ((__section__ (".__syscall_stub")))
stub_clone_handler(void)
{
+ struct stub_data *data = (struct stub_data *) UML_CONFIG_STUB_DATA;
long err;
err = stub_syscall2(__NR_clone, CLONE_PARENT | CLONE_FILES | SIGCHLD,
@@ -35,17 +34,21 @@ stub_clone_handler(void)
if(err)
goto out;
- err = stub_syscall3(__NR_setitimer, ITIMER_VIRTUAL,
- (long) &STUB_DATA(timer), 0);
+ err = stub_syscall3(__NR_setitimer, ITIMER_VIRTUAL,
+ (long) &data->timer, 0);
if(err)
goto out;
- err = stub_syscall6(STUB_MMAP_NR, UML_CONFIG_STUB_DATA,
- UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE,
- MAP_FIXED | MAP_SHARED, STUB_DATA(fd),
- STUB_DATA(offset));
+ remap_stack(data->fd, data->offset);
+ goto done;
+
out:
- /* save current result. Parent: pid; child: retcode of mmap */
- STUB_DATA(err) = err;
+ /* save current result.
+ * Parent: pid;
+ * child: retcode of mmap already saved and it jumps around this
+ * assignment
+ */
+ data->err = err;
+ done:
trap_myself();
}
diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c
index 37517d49c4ae..29a9e3f43763 100644
--- a/arch/um/os-Linux/start_up.c
+++ b/arch/um/os-Linux/start_up.c
@@ -116,16 +116,16 @@ static int stop_ptraced_child(int pid, void *stack, int exitcode,
if(!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) {
int exit_with = WEXITSTATUS(status);
if (exit_with == 2)
- printk("check_ptrace : child exited with status 2. "
+ printf("check_ptrace : child exited with status 2. "
"Serious trouble happening! Try updating your "
"host skas patch!\nDisabling SYSEMU support.");
- printk("check_ptrace : child exited with exitcode %d, while "
+ printf("check_ptrace : child exited with exitcode %d, while "
"expecting %d; status 0x%x", exit_with,
exitcode, status);
if (mustpanic)
panic("\n");
else
- printk("\n");
+ printf("\n");
ret = -1;
}
@@ -183,7 +183,7 @@ static void __init check_sysemu(void)
void *stack;
int pid, n, status, count=0;
- printk("Checking syscall emulation patch for ptrace...");
+ printf("Checking syscall emulation patch for ptrace...");
sysemu_supported = 0;
pid = start_ptraced_child(&stack);
@@ -207,10 +207,10 @@ static void __init check_sysemu(void)
goto fail_stopped;
sysemu_supported = 1;
- printk("OK\n");
+ printf("OK\n");
set_using_sysemu(!force_sysemu_disabled);
- printk("Checking advanced syscall emulation patch for ptrace...");
+ printf("Checking advanced syscall emulation patch for ptrace...");
pid = start_ptraced_child(&stack);
if(ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
@@ -246,7 +246,7 @@ static void __init check_sysemu(void)
goto fail_stopped;
sysemu_supported = 2;
- printk("OK\n");
+ printf("OK\n");
if ( !force_sysemu_disabled )
set_using_sysemu(sysemu_supported);
@@ -255,7 +255,7 @@ static void __init check_sysemu(void)
fail:
stop_ptraced_child(pid, stack, 1, 0);
fail_stopped:
- printk("missing\n");
+ printf("missing\n");
}
static void __init check_ptrace(void)
@@ -263,7 +263,7 @@ static void __init check_ptrace(void)
void *stack;
int pid, syscall, n, status;
- printk("Checking that ptrace can change system call numbers...");
+ printf("Checking that ptrace can change system call numbers...");
pid = start_ptraced_child(&stack);
if(ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0)
@@ -292,7 +292,7 @@ static void __init check_ptrace(void)
}
}
stop_ptraced_child(pid, stack, 0, 1);
- printk("OK\n");
+ printf("OK\n");
check_sysemu();
}
@@ -472,6 +472,8 @@ int can_do_skas(void)
int have_devanon = 0;
+/* Runs on boot kernel stack - already safe to use printk. */
+
void check_devanon(void)
{
int fd;
diff --git a/arch/um/os-Linux/user_syms.c b/arch/um/os-Linux/user_syms.c
index 56d3f870926b..8da6ab31152a 100644
--- a/arch/um/os-Linux/user_syms.c
+++ b/arch/um/os-Linux/user_syms.c
@@ -34,6 +34,11 @@ EXPORT_SYMBOL(strstr);
int sym(void); \
EXPORT_SYMBOL(sym);
+extern void readdir64(void) __attribute__((weak));
+EXPORT_SYMBOL(readdir64);
+extern void truncate64(void) __attribute__((weak));
+EXPORT_SYMBOL(truncate64);
+
#ifdef SUBARCH_i386
EXPORT_SYMBOL(vsyscall_ehdr);
EXPORT_SYMBOL(vsyscall_end);
diff --git a/arch/um/scripts/Makefile.rules b/arch/um/scripts/Makefile.rules
index b3fbf125709b..2e41cabd3d93 100644
--- a/arch/um/scripts/Makefile.rules
+++ b/arch/um/scripts/Makefile.rules
@@ -21,11 +21,6 @@ define unprofile
endef
-# The stubs and unmap.o can't try to call mcount or update basic block data
-define unprofile
- $(patsubst -pg,,$(patsubst -fprofile-arcs -ftest-coverage,,$(1)))
-endef
-
# cmd_make_link checks to see if the $(foo-dir) variable starts with a /. If
# so, it's considered to be a path relative to $(srcdir) rather than
# $(srcdir)/arch/$(SUBARCH). This is because x86_64 wants to get ldt.c from
diff --git a/arch/um/sys-i386/Makefile b/arch/um/sys-i386/Makefile
index 150059dbee12..f5fd5b0156d0 100644
--- a/arch/um/sys-i386/Makefile
+++ b/arch/um/sys-i386/Makefile
@@ -1,6 +1,8 @@
-obj-y = bitops.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \
- ptrace_user.o semaphore.o signal.o sigcontext.o stub.o stub_segv.o \
- syscalls.o sysrq.o sys_call_table.o
+obj-y := bitops.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \
+ ptrace_user.o semaphore.o signal.o sigcontext.o syscalls.o sysrq.o \
+ sys_call_table.o
+
+obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o
obj-$(CONFIG_HIGHMEM) += highmem.o
obj-$(CONFIG_MODULES) += module.o
diff --git a/arch/um/sys-x86_64/Makefile b/arch/um/sys-x86_64/Makefile
index 00b2025427df..a351091fbd99 100644
--- a/arch/um/sys-x86_64/Makefile
+++ b/arch/um/sys-x86_64/Makefile
@@ -6,8 +6,9 @@
#XXX: why into lib-y?
lib-y = bitops.o bugs.o csum-partial.o delay.o fault.o ldt.o mem.o memcpy.o \
- ptrace.o ptrace_user.o sigcontext.o signal.o stub.o \
- stub_segv.o syscalls.o syscall_table.o sysrq.o thunk.o
+ ptrace.o ptrace_user.o sigcontext.o signal.o syscalls.o \
+ syscall_table.o sysrq.o thunk.o
+lib-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o
obj-y := ksyms.o
obj-$(CONFIG_MODULES) += module.o um_module.o
diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c
index 750e01dcbdf4..64c4534b930c 100644
--- a/arch/x86_64/kernel/setup.c
+++ b/arch/x86_64/kernel/setup.c
@@ -42,6 +42,7 @@
#include <linux/edd.h>
#include <linux/mmzone.h>
#include <linux/kexec.h>
+#include <linux/cpufreq.h>
#include <asm/mtrr.h>
#include <asm/uaccess.h>
@@ -1256,8 +1257,11 @@ static int show_cpuinfo(struct seq_file *m, void *v)
seq_printf(m, "stepping\t: unknown\n");
if (cpu_has(c,X86_FEATURE_TSC)) {
+ unsigned int freq = cpufreq_quick_get((unsigned)(c-cpu_data));
+ if (!freq)
+ freq = cpu_khz;
seq_printf(m, "cpu MHz\t\t: %u.%03u\n",
- cpu_khz / 1000, (cpu_khz % 1000));
+ freq / 1000, (freq % 1000));
}
/* Cache size */
diff --git a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c
index 286f6a624c3a..c016dfe84784 100644
--- a/arch/x86_64/mm/init.c
+++ b/arch/x86_64/mm/init.c
@@ -348,7 +348,7 @@ size_zones(unsigned long *z, unsigned long *h,
}
/* Compute holes */
- w = 0;
+ w = start_pfn;
for (i = 0; i < MAX_NR_ZONES; i++) {
unsigned long s = w;
w += z[i];
diff --git a/arch/x86_64/pci/Makefile b/arch/x86_64/pci/Makefile
index bb34e5ef916c..a8f75a2a0f6f 100644
--- a/arch/x86_64/pci/Makefile
+++ b/arch/x86_64/pci/Makefile
@@ -11,7 +11,7 @@ obj-y += fixup.o
obj-$(CONFIG_ACPI) += acpi.o
obj-y += legacy.o irq.o common.o
# mmconfig has a 64bit special
-obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o
+obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o direct.o
obj-$(CONFIG_NUMA) += k8-bus.o
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
index 4e390dfd3157..1d8852f7bbff 100644
--- a/block/scsi_ioctl.c
+++ b/block/scsi_ioctl.c
@@ -442,11 +442,37 @@ error:
return err;
}
+
+/* Send basic block requests */
+static int __blk_send_generic(request_queue_t *q, struct gendisk *bd_disk, int cmd, int data)
+{
+ struct request *rq;
+ int err;
+
+ rq = blk_get_request(q, WRITE, __GFP_WAIT);
+ rq->flags |= REQ_BLOCK_PC;
+ rq->data = NULL;
+ rq->data_len = 0;
+ rq->timeout = BLK_DEFAULT_TIMEOUT;
+ memset(rq->cmd, 0, sizeof(rq->cmd));
+ rq->cmd[0] = cmd;
+ rq->cmd[4] = data;
+ rq->cmd_len = 6;
+ err = blk_execute_rq(q, bd_disk, rq, 0);
+ blk_put_request(rq);
+
+ return err;
+}
+
+static inline int blk_send_start_stop(request_queue_t *q, struct gendisk *bd_disk, int data)
+{
+ return __blk_send_generic(q, bd_disk, GPCMD_START_STOP_UNIT, data);
+}
+
int scsi_cmd_ioctl(struct file *file, struct gendisk *bd_disk, unsigned int cmd, void __user *arg)
{
request_queue_t *q;
- struct request *rq;
- int close = 0, err;
+ int err;
q = bd_disk->queue;
if (!q)
@@ -564,19 +590,10 @@ int scsi_cmd_ioctl(struct file *file, struct gendisk *bd_disk, unsigned int cmd,
err = sg_scsi_ioctl(file, q, bd_disk, arg);
break;
case CDROMCLOSETRAY:
- close = 1;
+ err = blk_send_start_stop(q, bd_disk, 0x03);
+ break;
case CDROMEJECT:
- rq = blk_get_request(q, WRITE, __GFP_WAIT);
- rq->flags |= REQ_BLOCK_PC;
- rq->data = NULL;
- rq->data_len = 0;
- rq->timeout = BLK_DEFAULT_TIMEOUT;
- memset(rq->cmd, 0, sizeof(rq->cmd));
- rq->cmd[0] = GPCMD_START_STOP_UNIT;
- rq->cmd[4] = 0x02 + (close != 0);
- rq->cmd_len = 6;
- err = blk_execute_rq(q, bd_disk, rq, 0);
- blk_put_request(rq);
+ err = blk_send_start_stop(q, bd_disk, 0x02);
break;
default:
err = -ENOTTY;
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 5f51057518b0..807b0df308f1 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -274,8 +274,6 @@ static void acpi_processor_idle(void)
}
}
- cx->usage++;
-
#ifdef CONFIG_HOTPLUG_CPU
/*
* Check for P_LVL2_UP flag before entering C2 and above on
@@ -283,9 +281,12 @@ static void acpi_processor_idle(void)
* detection phase, to work cleanly with logical CPU hotplug.
*/
if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) &&
- !pr->flags.has_cst && acpi_fadt.plvl2_up)
- cx->type = ACPI_STATE_C1;
+ !pr->flags.has_cst && !acpi_fadt.plvl2_up)
+ cx = &pr->power.states[ACPI_STATE_C1];
#endif
+
+ cx->usage++;
+
/*
* Sleep:
* ------
@@ -386,6 +387,15 @@ static void acpi_processor_idle(void)
next_state = pr->power.state;
+#ifdef CONFIG_HOTPLUG_CPU
+ /* Don't do promotion/demotion */
+ if ((cx->type == ACPI_STATE_C1) && (num_online_cpus() > 1) &&
+ !pr->flags.has_cst && !acpi_fadt.plvl2_up) {
+ next_state = cx;
+ goto end;
+ }
+#endif
+
/*
* Promotion?
* ----------
@@ -557,7 +567,7 @@ static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr)
* Check for P_LVL2_UP flag before entering C2 and above on
* an SMP system.
*/
- if ((num_online_cpus() > 1) && acpi_fadt.plvl2_up)
+ if ((num_online_cpus() > 1) && !acpi_fadt.plvl2_up)
return_VALUE(-ENODEV);
#endif
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
index f37584015324..dc9817cfb882 100644
--- a/drivers/acpi/processor_thermal.c
+++ b/drivers/acpi/processor_thermal.c
@@ -102,8 +102,8 @@ static int cpu_has_cpufreq(unsigned int cpu)
{
struct cpufreq_policy policy;
if (!acpi_thermal_cpufreq_is_init || cpufreq_get_policy(&policy, cpu))
- return -ENODEV;
- return 0;
+ return 0;
+ return 1;
}
static int acpi_thermal_cpufreq_increase(unsigned int cpu)
diff --git a/drivers/acpi/utilities/utmisc.c b/drivers/acpi/utilities/utmisc.c
index 0c5abc536c7a..2ce872d75890 100644
--- a/drivers/acpi/utilities/utmisc.c
+++ b/drivers/acpi/utilities/utmisc.c
@@ -84,14 +84,14 @@ acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id)
/* Find a free owner ID */
- for (i = 0; i < 32; i++) {
- if (!(acpi_gbl_owner_id_mask & (1 << i))) {
+ for (i = 0; i < 64; i++) {
+ if (!(acpi_gbl_owner_id_mask & (1ULL << i))) {
ACPI_DEBUG_PRINT((ACPI_DB_VALUES,
- "Current owner_id mask: %8.8X New ID: %2.2X\n",
+ "Current owner_id mask: %16.16LX New ID: %2.2X\n",
acpi_gbl_owner_id_mask,
(unsigned int)(i + 1)));
- acpi_gbl_owner_id_mask |= (1 << i);
+ acpi_gbl_owner_id_mask |= (1ULL << i);
*owner_id = (acpi_owner_id) (i + 1);
goto exit;
}
@@ -106,7 +106,7 @@ acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id)
*/
*owner_id = 0;
status = AE_OWNER_ID_LIMIT;
- ACPI_REPORT_ERROR(("Could not allocate new owner_id (32 max), AE_OWNER_ID_LIMIT\n"));
+ ACPI_REPORT_ERROR(("Could not allocate new owner_id (64 max), AE_OWNER_ID_LIMIT\n"));
exit:
(void)acpi_ut_release_mutex(ACPI_MTX_CACHES);
@@ -123,7 +123,7 @@ acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id)
* control method or unloading a table. Either way, we would
* ignore any error anyway.
*
- * DESCRIPTION: Release a table or method owner ID. Valid IDs are 1 - 32
+ * DESCRIPTION: Release a table or method owner ID. Valid IDs are 1 - 64
*
******************************************************************************/
@@ -140,7 +140,7 @@ void acpi_ut_release_owner_id(acpi_owner_id * owner_id_ptr)
/* Zero is not a valid owner_iD */
- if ((owner_id == 0) || (owner_id > 32)) {
+ if ((owner_id == 0) || (owner_id > 64)) {
ACPI_REPORT_ERROR(("Invalid owner_id: %2.2X\n", owner_id));
return_VOID;
}
@@ -158,8 +158,8 @@ void acpi_ut_release_owner_id(acpi_owner_id * owner_id_ptr)
/* Free the owner ID only if it is valid */
- if (acpi_gbl_owner_id_mask & (1 << owner_id)) {
- acpi_gbl_owner_id_mask ^= (1 << owner_id);
+ if (acpi_gbl_owner_id_mask & (1ULL << owner_id)) {
+ acpi_gbl_owner_id_mask ^= (1ULL << owner_id);
}
(void)acpi_ut_release_mutex(ACPI_MTX_CACHES);
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 7b1cd93892be..c4b9d2adfc08 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -358,7 +358,8 @@ config BLK_DEV_UB
This driver supports certain USB attached storage devices
such as flash keys.
- Warning: Enabling this cripples the usb-storage driver.
+ If you enable this driver, it is recommended to avoid conflicts
+ with usb-storage by enabling USB_LIBUSUAL.
If unsure, say N.
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index bfb23d543ff7..10740a065088 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -9,7 +9,6 @@
*
* TODO (sorted by decreasing priority)
* -- Kill first_open (Al Viro fixed the block layer now)
- * -- Do resets with usb_device_reset (needs a thread context, use khubd)
* -- set readonly flag for CDs, set removable flag for CF readers
* -- do inquiry and verify we got a disk and not a tape (for LUN mismatch)
* -- special case some senses, e.g. 3a/0 -> no media present, reduce retries
@@ -29,6 +28,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/usb.h>
+#include <linux/usb_usual.h>
#include <linux/blkdev.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/timer.h>
@@ -107,16 +107,6 @@
*/
/*
- * Definitions which have to be scattered once we understand the layout better.
- */
-
-/* Transport (despite PR in the name) */
-#define US_PR_BULK 0x50 /* bulk only */
-
-/* Protocol */
-#define US_SC_SCSI 0x06 /* Transparent */
-
-/*
* This many LUNs per USB device.
* Every one of them takes a host, see UB_MAX_HOSTS.
*/
@@ -125,7 +115,7 @@
/*
*/
-#define UB_MINORS_PER_MAJOR 8
+#define UB_PARTS_PER_LUN 8
#define UB_MAX_CDB_SIZE 16 /* Corresponds to Bulk */
@@ -245,6 +235,13 @@ struct ub_scsi_cmd {
void *back;
};
+struct ub_request {
+ struct request *rq;
+ unsigned int current_try;
+ unsigned int nsg; /* sgv[nsg] */
+ struct scatterlist sgv[UB_MAX_REQ_SG];
+};
+
/*
*/
struct ub_capacity {
@@ -340,6 +337,8 @@ struct ub_lun {
int readonly;
int first_open; /* Kludge. See ub_bd_open. */
+ struct ub_request urq;
+
/* Use Ingo's mempool if or when we have more than one command. */
/*
* Currently we never need more than one command for the whole device.
@@ -360,6 +359,7 @@ struct ub_dev {
atomic_t poison; /* The USB device is disconnected */
int openc; /* protected by ub_lock! */
/* kref is too implicit for our taste */
+ int reset; /* Reset is running */
unsigned int tagcnt;
char name[12];
struct usb_device *dev;
@@ -387,6 +387,9 @@ struct ub_dev {
struct bulk_cs_wrap work_bcs;
struct usb_ctrlrequest work_cr;
+ struct work_struct reset_work;
+ wait_queue_head_t reset_wait;
+
int sg_stat[6];
struct ub_scsi_trace tr;
};
@@ -395,12 +398,14 @@ struct ub_dev {
*/
static void ub_cleanup(struct ub_dev *sc);
static int ub_request_fn_1(struct ub_lun *lun, struct request *rq);
-static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct request *rq);
-static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct request *rq);
+static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
+ struct ub_scsi_cmd *cmd, struct ub_request *urq);
+static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
+ struct ub_scsi_cmd *cmd, struct ub_request *urq);
static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
static void ub_end_rq(struct request *rq, int uptodate);
+static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
+ struct ub_request *urq, struct ub_scsi_cmd *cmd);
static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
static void ub_urb_complete(struct urb *urb, struct pt_regs *pt);
static void ub_scsi_action(unsigned long _dev);
@@ -415,6 +420,8 @@ static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
int stalled_pipe);
static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd);
+static void ub_reset_enter(struct ub_dev *sc);
+static void ub_reset_task(void *arg);
static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun);
static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
struct ub_capacity *ret);
@@ -422,13 +429,18 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum);
/*
*/
+#ifdef CONFIG_USB_LIBUSUAL
+
+#define ub_usb_ids storage_usb_ids
+#else
+
static struct usb_device_id ub_usb_ids[] = {
- // { USB_DEVICE_VER(0x0781, 0x0002, 0x0009, 0x0009) }, /* SDDR-31 */
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) },
{ }
};
MODULE_DEVICE_TABLE(usb, ub_usb_ids);
+#endif /* CONFIG_USB_LIBUSUAL */
/*
* Find me a way to identify "next free minor" for add_disk(),
@@ -522,6 +534,9 @@ static ssize_t ub_diag_show(struct device *dev, struct device_attribute *attr,
spin_lock_irqsave(&sc->lock, flags);
cnt += sprintf(page + cnt,
+ "poison %d reset %d\n",
+ atomic_read(&sc->poison), sc->reset);
+ cnt += sprintf(page + cnt,
"qlen %d qmax %d\n",
sc->cmd_queue.qlen, sc->cmd_queue.qmax);
cnt += sprintf(page + cnt,
@@ -770,7 +785,8 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
{
struct ub_dev *sc = lun->udev;
struct ub_scsi_cmd *cmd;
- int rc;
+ struct ub_request *urq;
+ int n_elem;
if (atomic_read(&sc->poison) || lun->changed) {
blkdev_dequeue_request(rq);
@@ -778,65 +794,70 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
return 0;
}
+ if (lun->urq.rq != NULL)
+ return -1;
if ((cmd = ub_get_cmd(lun)) == NULL)
return -1;
memset(cmd, 0, sizeof(struct ub_scsi_cmd));
blkdev_dequeue_request(rq);
+
+ urq = &lun->urq;
+ memset(urq, 0, sizeof(struct ub_request));
+ urq->rq = rq;
+
+ /*
+ * get scatterlist from block layer
+ */
+ n_elem = blk_rq_map_sg(lun->disk->queue, rq, &urq->sgv[0]);
+ if (n_elem < 0) {
+ printk(KERN_INFO "%s: failed request map (%d)\n",
+ lun->name, n_elem); /* P3 */
+ goto drop;
+ }
+ if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
+ printk(KERN_WARNING "%s: request with %d segments\n",
+ lun->name, n_elem);
+ goto drop;
+ }
+ urq->nsg = n_elem;
+ sc->sg_stat[n_elem < 5 ? n_elem : 5]++;
+
if (blk_pc_request(rq)) {
- rc = ub_cmd_build_packet(sc, lun, cmd, rq);
+ ub_cmd_build_packet(sc, lun, cmd, urq);
} else {
- rc = ub_cmd_build_block(sc, lun, cmd, rq);
- }
- if (rc != 0) {
- ub_put_cmd(lun, cmd);
- ub_end_rq(rq, 0);
- return 0;
+ ub_cmd_build_block(sc, lun, cmd, urq);
}
cmd->state = UB_CMDST_INIT;
cmd->lun = lun;
cmd->done = ub_rw_cmd_done;
- cmd->back = rq;
+ cmd->back = urq;
cmd->tag = sc->tagcnt++;
- if (ub_submit_scsi(sc, cmd) != 0) {
- ub_put_cmd(lun, cmd);
- ub_end_rq(rq, 0);
- return 0;
- }
+ if (ub_submit_scsi(sc, cmd) != 0)
+ goto drop;
return 0;
+
+drop:
+ ub_put_cmd(lun, cmd);
+ ub_end_rq(rq, 0);
+ return 0;
}
-static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct request *rq)
+static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
+ struct ub_scsi_cmd *cmd, struct ub_request *urq)
{
- int ub_dir;
- int n_elem;
+ struct request *rq = urq->rq;
unsigned int block, nblks;
if (rq_data_dir(rq) == WRITE)
- ub_dir = UB_DIR_WRITE;
+ cmd->dir = UB_DIR_WRITE;
else
- ub_dir = UB_DIR_READ;
- cmd->dir = ub_dir;
+ cmd->dir = UB_DIR_READ;
- /*
- * get scatterlist from block layer
- */
- n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]);
- if (n_elem <= 0) {
- printk(KERN_INFO "%s: failed request map (%d)\n",
- sc->name, n_elem); /* P3 */
- return -1; /* request with no s/g entries? */
- }
- if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
- printk(KERN_WARNING "%s: request with %d segments\n",
- sc->name, n_elem);
- return -1;
- }
- cmd->nsg = n_elem;
- sc->sg_stat[n_elem < 5 ? n_elem : 5]++;
+ cmd->nsg = urq->nsg;
+ memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg);
/*
* build the command
@@ -847,7 +868,7 @@ static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
block = rq->sector >> lun->capacity.bshift;
nblks = rq->nr_sectors >> lun->capacity.bshift;
- cmd->cdb[0] = (ub_dir == UB_DIR_READ)? READ_10: WRITE_10;
+ cmd->cdb[0] = (cmd->dir == UB_DIR_READ)? READ_10: WRITE_10;
/* 10-byte uses 4 bytes of LBA: 2147483648KB, 2097152MB, 2048GB */
cmd->cdb[2] = block >> 24;
cmd->cdb[3] = block >> 16;
@@ -858,14 +879,12 @@ static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
cmd->cdb_len = 10;
cmd->len = rq->nr_sectors * 512;
-
- return 0;
}
-static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct request *rq)
+static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
+ struct ub_scsi_cmd *cmd, struct ub_request *urq)
{
- int n_elem;
+ struct request *rq = urq->rq;
if (rq->data_len == 0) {
cmd->dir = UB_DIR_NONE;
@@ -874,40 +893,26 @@ static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
cmd->dir = UB_DIR_WRITE;
else
cmd->dir = UB_DIR_READ;
-
}
- /*
- * get scatterlist from block layer
- */
- n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]);
- if (n_elem < 0) {
- printk(KERN_INFO "%s: failed request map (%d)\n",
- sc->name, n_elem); /* P3 */
- return -1;
- }
- if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
- printk(KERN_WARNING "%s: request with %d segments\n",
- sc->name, n_elem);
- return -1;
- }
- cmd->nsg = n_elem;
- sc->sg_stat[n_elem < 5 ? n_elem : 5]++;
+ cmd->nsg = urq->nsg;
+ memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg);
memcpy(&cmd->cdb, rq->cmd, rq->cmd_len);
cmd->cdb_len = rq->cmd_len;
cmd->len = rq->data_len;
-
- return 0;
}
static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
{
- struct request *rq = cmd->back;
struct ub_lun *lun = cmd->lun;
+ struct ub_request *urq = cmd->back;
+ struct request *rq;
int uptodate;
+ rq = urq->rq;
+
if (cmd->error == 0) {
uptodate = 1;
@@ -928,9 +933,16 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
rq->errors = SAM_STAT_CHECK_CONDITION;
else
rq->errors = DID_ERROR << 16;
+ } else {
+ if (cmd->error == -EIO) {
+ if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0)
+ return;
+ }
}
}
+ urq->rq = NULL;
+
ub_put_cmd(lun, cmd);
ub_end_rq(rq, uptodate);
blk_start_queue(lun->disk->queue);
@@ -938,13 +950,45 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
static void ub_end_rq(struct request *rq, int uptodate)
{
- int rc;
-
- rc = end_that_request_first(rq, uptodate, rq->hard_nr_sectors);
- // assert(rc == 0);
+ end_that_request_first(rq, uptodate, rq->hard_nr_sectors);
end_that_request_last(rq);
}
+static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
+ struct ub_request *urq, struct ub_scsi_cmd *cmd)
+{
+
+ if (atomic_read(&sc->poison))
+ return -ENXIO;
+
+ ub_reset_enter(sc);
+
+ if (urq->current_try >= 3)
+ return -EIO;
+ urq->current_try++;
+ /* P3 */ printk("%s: dir %c len/act %d/%d "
+ "[sense %x %02x %02x] retry %d\n",
+ sc->name, UB_DIR_CHAR(cmd->dir), cmd->len, cmd->act_len,
+ cmd->key, cmd->asc, cmd->ascq, urq->current_try);
+
+ memset(cmd, 0, sizeof(struct ub_scsi_cmd));
+ ub_cmd_build_block(sc, lun, cmd, urq);
+
+ cmd->state = UB_CMDST_INIT;
+ cmd->lun = lun;
+ cmd->done = ub_rw_cmd_done;
+ cmd->back = urq;
+
+ cmd->tag = sc->tagcnt++;
+
+#if 0 /* Wasteful */
+ return ub_submit_scsi(sc, cmd);
+#else
+ ub_cmdq_add(sc, cmd);
+ return 0;
+#endif
+}
+
/*
* Submit a regular SCSI operation (not an auto-sense).
*
@@ -1075,7 +1119,7 @@ static void ub_scsi_dispatch(struct ub_dev *sc)
struct ub_scsi_cmd *cmd;
int rc;
- while ((cmd = ub_cmdq_peek(sc)) != NULL) {
+ while (!sc->reset && (cmd = ub_cmdq_peek(sc)) != NULL) {
if (cmd->state == UB_CMDST_DONE) {
ub_cmdq_pop(sc);
(*cmd->done)(sc, cmd);
@@ -1098,11 +1142,12 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
{
struct urb *urb = &sc->work_urb;
struct bulk_cs_wrap *bcs;
+ int len;
int rc;
if (atomic_read(&sc->poison)) {
- /* A little too simplistic, I feel... */
- goto Bad_End;
+ ub_state_done(sc, cmd, -ENODEV);
+ return;
}
if (cmd->state == UB_CMDST_CLEAR) {
@@ -1110,7 +1155,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
/*
* STALL while clearning STALL.
* The control pipe clears itself - nothing to do.
- * XXX Might try to reset the device here and retry.
*/
printk(KERN_NOTICE "%s: stall on control pipe\n",
sc->name);
@@ -1129,11 +1173,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
} else if (cmd->state == UB_CMDST_CLR2STS) {
if (urb->status == -EPIPE) {
- /*
- * STALL while clearning STALL.
- * The control pipe clears itself - nothing to do.
- * XXX Might try to reset the device here and retry.
- */
printk(KERN_NOTICE "%s: stall on control pipe\n",
sc->name);
goto Bad_End;
@@ -1151,11 +1190,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
} else if (cmd->state == UB_CMDST_CLRRS) {
if (urb->status == -EPIPE) {
- /*
- * STALL while clearning STALL.
- * The control pipe clears itself - nothing to do.
- * XXX Might try to reset the device here and retry.
- */
printk(KERN_NOTICE "%s: stall on control pipe\n",
sc->name);
goto Bad_End;
@@ -1172,7 +1206,12 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
ub_state_stat_counted(sc, cmd);
} else if (cmd->state == UB_CMDST_CMD) {
- if (urb->status == -EPIPE) {
+ switch (urb->status) {
+ case 0:
+ break;
+ case -EOVERFLOW:
+ goto Bad_End;
+ case -EPIPE:
rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
if (rc != 0) {
printk(KERN_NOTICE "%s: "
@@ -1182,17 +1221,20 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
* This is typically ENOMEM or some other such shit.
* Retrying is pointless. Just do Bad End on it...
*/
- goto Bad_End;
+ ub_state_done(sc, cmd, rc);
+ return;
}
cmd->state = UB_CMDST_CLEAR;
ub_cmdtr_state(sc, cmd);
return;
- }
- if (urb->status != 0) {
+ case -ESHUTDOWN: /* unplug */
+ case -EILSEQ: /* unplug timeout on uhci */
+ ub_state_done(sc, cmd, -ENODEV);
+ return;
+ default:
goto Bad_End;
}
if (urb->actual_length != US_BULK_CB_WRAP_LEN) {
- /* XXX Must do reset here to unconfuse the device */
goto Bad_End;
}
@@ -1211,11 +1253,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
printk(KERN_NOTICE "%s: "
"unable to submit clear (%d)\n",
sc->name, rc);
- /*
- * This is typically ENOMEM or some other such shit.
- * Retrying is pointless. Just do Bad End on it...
- */
- goto Bad_End;
+ ub_state_done(sc, cmd, rc);
+ return;
}
cmd->state = UB_CMDST_CLR2STS;
ub_cmdtr_state(sc, cmd);
@@ -1224,14 +1263,50 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
if (urb->status == -EOVERFLOW) {
/*
* A babble? Failure, but we must transfer CSW now.
- * XXX This is going to end in perpetual babble. Reset.
*/
cmd->error = -EOVERFLOW; /* A cheap trick... */
ub_state_stat(sc, cmd);
return;
}
- if (urb->status != 0)
- goto Bad_End;
+
+ if (cmd->dir == UB_DIR_WRITE) {
+ /*
+ * Do not continue writes in case of a failure.
+ * Doing so would cause sectors to be mixed up,
+ * which is worse than sectors lost.
+ *
+ * We must try to read the CSW, or many devices
+ * get confused.
+ */
+ len = urb->actual_length;
+ if (urb->status != 0 ||
+ len != cmd->sgv[cmd->current_sg].length) {
+ cmd->act_len += len;
+ ub_cmdtr_act_len(sc, cmd);
+
+ cmd->error = -EIO;
+ ub_state_stat(sc, cmd);
+ return;
+ }
+
+ } else {
+ /*
+ * If an error occurs on read, we record it, and
+ * continue to fetch data in order to avoid bubble.
+ *
+ * As a small shortcut, we stop if we detect that
+ * a CSW mixed into data.
+ */
+ if (urb->status != 0)
+ cmd->error = -EIO;
+
+ len = urb->actual_length;
+ if (urb->status != 0 ||
+ len != cmd->sgv[cmd->current_sg].length) {
+ if ((len & 0x1FF) == US_BULK_CS_WRAP_LEN)
+ goto Bad_End;
+ }
+ }
cmd->act_len += urb->actual_length;
ub_cmdtr_act_len(sc, cmd);
@@ -1249,11 +1324,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
printk(KERN_NOTICE "%s: "
"unable to submit clear (%d)\n",
sc->name, rc);
- /*
- * This is typically ENOMEM or some other such shit.
- * Retrying is pointless. Just do Bad End on it...
- */
- goto Bad_End;
+ ub_state_done(sc, cmd, rc);
+ return;
}
/*
@@ -1266,14 +1338,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
ub_cmdtr_state(sc, cmd);
return;
}
- if (urb->status == -EOVERFLOW) {
- /*
- * XXX We are screwed here. Retrying is pointless,
- * because the pipelined data will not get in until
- * we read with a big enough buffer. We must reset XXX.
- */
- goto Bad_End;
- }
+
+ /* Catch everything, including -EOVERFLOW and other nasties. */
if (urb->status != 0)
goto Bad_End;
@@ -1319,15 +1385,15 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
return;
}
- rc = le32_to_cpu(bcs->Residue);
- if (rc != cmd->len - cmd->act_len) {
+ len = le32_to_cpu(bcs->Residue);
+ if (len != cmd->len - cmd->act_len) {
/*
* It is all right to transfer less, the caller has
* to check. But it's not all right if the device
* counts disagree with our counts.
*/
/* P3 */ printk("%s: resid %d len %d act %d\n",
- sc->name, rc, cmd->len, cmd->act_len);
+ sc->name, len, cmd->len, cmd->act_len);
goto Bad_End;
}
@@ -1338,13 +1404,13 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
ub_state_sense(sc, cmd);
return;
case US_BULK_STAT_PHASE:
- /* XXX We must reset the transport here */
/* P3 */ printk("%s: status PHASE\n", sc->name);
goto Bad_End;
default:
printk(KERN_INFO "%s: unknown CSW status 0x%x\n",
sc->name, bcs->Status);
- goto Bad_End;
+ ub_state_done(sc, cmd, -EINVAL);
+ return;
}
/* Not zeroing error to preserve a babble indicator */
@@ -1364,7 +1430,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
printk(KERN_WARNING "%s: "
"wrong command state %d\n",
sc->name, cmd->state);
- goto Bad_End;
+ ub_state_done(sc, cmd, -EINVAL);
+ return;
}
return;
@@ -1612,6 +1679,93 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd)
}
/*
+ * Reset management
+ */
+
+static void ub_reset_enter(struct ub_dev *sc)
+{
+
+ if (sc->reset) {
+ /* This happens often on multi-LUN devices. */
+ return;
+ }
+ sc->reset = 1;
+
+#if 0 /* Not needed because the disconnect waits for us. */
+ unsigned long flags;
+ spin_lock_irqsave(&ub_lock, flags);
+ sc->openc++;
+ spin_unlock_irqrestore(&ub_lock, flags);
+#endif
+
+#if 0 /* We let them stop themselves. */
+ struct list_head *p;
+ struct ub_lun *lun;
+ list_for_each(p, &sc->luns) {
+ lun = list_entry(p, struct ub_lun, link);
+ blk_stop_queue(lun->disk->queue);
+ }
+#endif
+
+ schedule_work(&sc->reset_work);
+}
+
+static void ub_reset_task(void *arg)
+{
+ struct ub_dev *sc = arg;
+ unsigned long flags;
+ struct list_head *p;
+ struct ub_lun *lun;
+ int lkr, rc;
+
+ if (!sc->reset) {
+ printk(KERN_WARNING "%s: Running reset unrequested\n",
+ sc->name);
+ return;
+ }
+
+ if (atomic_read(&sc->poison)) {
+ printk(KERN_NOTICE "%s: Not resetting disconnected device\n",
+ sc->name); /* P3 This floods. Remove soon. XXX */
+ } else if (sc->dev->actconfig->desc.bNumInterfaces != 1) {
+ printk(KERN_NOTICE "%s: Not resetting multi-interface device\n",
+ sc->name); /* P3 This floods. Remove soon. XXX */
+ } else {
+ if ((lkr = usb_lock_device_for_reset(sc->dev, sc->intf)) < 0) {
+ printk(KERN_NOTICE
+ "%s: usb_lock_device_for_reset failed (%d)\n",
+ sc->name, lkr);
+ } else {
+ rc = usb_reset_device(sc->dev);
+ if (rc < 0) {
+ printk(KERN_NOTICE "%s: "
+ "usb_lock_device_for_reset failed (%d)\n",
+ sc->name, rc);
+ }
+
+ if (lkr)
+ usb_unlock_device(sc->dev);
+ }
+ }
+
+ /*
+ * In theory, no commands can be running while reset is active,
+ * so nobody can ask for another reset, and so we do not need any
+ * queues of resets or anything. We do need a spinlock though,
+ * to interact with block layer.
+ */
+ spin_lock_irqsave(&sc->lock, flags);
+ sc->reset = 0;
+ tasklet_schedule(&sc->tasklet);
+ list_for_each(p, &sc->luns) {
+ lun = list_entry(p, struct ub_lun, link);
+ blk_start_queue(lun->disk->queue);
+ }
+ wake_up(&sc->reset_wait);
+ spin_unlock_irqrestore(&sc->lock, flags);
+}
+
+/*
* This is called from a process context.
*/
static void ub_revalidate(struct ub_dev *sc, struct ub_lun *lun)
@@ -2146,7 +2300,7 @@ static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev,
if (ep_in == NULL || ep_out == NULL) {
printk(KERN_NOTICE "%s: failed endpoint check\n",
sc->name);
- return -EIO;
+ return -ENODEV;
}
/* Calculate and store the pipe values */
@@ -2172,6 +2326,9 @@ static int ub_probe(struct usb_interface *intf,
int rc;
int i;
+ if (usb_usual_check_type(dev_id, USB_US_TYPE_UB))
+ return -ENXIO;
+
rc = -ENOMEM;
if ((sc = kmalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL)
goto err_core;
@@ -2181,6 +2338,8 @@ static int ub_probe(struct usb_interface *intf,
usb_init_urb(&sc->work_urb);
tasklet_init(&sc->tasklet, ub_scsi_action, (unsigned long)sc);
atomic_set(&sc->poison, 0);
+ INIT_WORK(&sc->reset_work, ub_reset_task, sc);
+ init_waitqueue_head(&sc->reset_wait);
init_timer(&sc->work_timer);
sc->work_timer.data = (unsigned long) sc;
@@ -2201,7 +2360,8 @@ static int ub_probe(struct usb_interface *intf,
/* XXX Verify that we can handle the device (from descriptors) */
- ub_get_pipes(sc, sc->dev, intf);
+ if (ub_get_pipes(sc, sc->dev, intf) != 0)
+ goto err_dev_desc;
if (device_create_file(&sc->intf->dev, &dev_attr_diag) != 0)
goto err_diag;
@@ -2272,6 +2432,7 @@ static int ub_probe(struct usb_interface *intf,
/* device_remove_file(&sc->intf->dev, &dev_attr_diag); */
err_diag:
+err_dev_desc:
usb_set_intfdata(intf, NULL);
// usb_put_intf(sc->intf);
usb_put_dev(sc->dev);
@@ -2309,14 +2470,14 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum)
ub_revalidate(sc, lun);
rc = -ENOMEM;
- if ((disk = alloc_disk(UB_MINORS_PER_MAJOR)) == NULL)
+ if ((disk = alloc_disk(UB_PARTS_PER_LUN)) == NULL)
goto err_diskalloc;
lun->disk = disk;
sprintf(disk->disk_name, DRV_NAME "%c", lun->id + 'a');
sprintf(disk->devfs_name, DEVFS_NAME "/%c", lun->id + 'a');
disk->major = UB_MAJOR;
- disk->first_minor = lun->id * UB_MINORS_PER_MAJOR;
+ disk->first_minor = lun->id * UB_PARTS_PER_LUN;
disk->fops = &ub_bd_fops;
disk->private_data = lun;
disk->driverfs_dev = &sc->intf->dev;
@@ -2380,6 +2541,11 @@ static void ub_disconnect(struct usb_interface *intf)
atomic_set(&sc->poison, 1);
/*
+ * Wait for reset to end, if any.
+ */
+ wait_event(sc->reset_wait, !sc->reset);
+
+ /*
* Blow away queued commands.
*
* Actually, this never works, because before we get here
@@ -2392,7 +2558,7 @@ static void ub_disconnect(struct usb_interface *intf)
{
struct ub_scsi_cmd *cmd;
int cnt = 0;
- while ((cmd = ub_cmdq_pop(sc)) != NULL) {
+ while ((cmd = ub_cmdq_peek(sc)) != NULL) {
cmd->error = -ENOTCONN;
cmd->state = UB_CMDST_DONE;
ub_cmdtr_state(sc, cmd);
@@ -2461,7 +2627,6 @@ static void ub_disconnect(struct usb_interface *intf)
}
static struct usb_driver ub_driver = {
- .owner = THIS_MODULE,
.name = "ub",
.probe = ub_probe,
.disconnect = ub_disconnect,
@@ -2479,6 +2644,7 @@ static int __init ub_init(void)
if ((rc = usb_register(&ub_driver)) != 0)
goto err_register;
+ usb_usual_set_present(USB_US_TYPE_UB);
return 0;
err_register:
@@ -2494,6 +2660,7 @@ static void __exit ub_exit(void)
devfs_remove(DEVFS_NAME);
unregister_blkdev(UB_MAJOR, DRV_NAME);
+ usb_usual_clear_present(USB_US_TYPE_UB);
}
module_init(ub_init);
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
index 8e7fb3551775..3e7a067cc087 100644
--- a/drivers/bluetooth/bcm203x.c
+++ b/drivers/bluetooth/bcm203x.c
@@ -275,7 +275,6 @@ static void bcm203x_disconnect(struct usb_interface *intf)
}
static struct usb_driver bcm203x_driver = {
- .owner = THIS_MODULE,
.name = "bcm203x",
.probe = bcm203x_probe,
.disconnect = bcm203x_disconnect,
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index 067e27893e4a..8947c8837dac 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -768,7 +768,6 @@ static void bfusb_disconnect(struct usb_interface *intf)
}
static struct usb_driver bfusb_driver = {
- .owner = THIS_MODULE,
.name = "bfusb",
.probe = bfusb_probe,
.disconnect = bfusb_disconnect,
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
index 394796315adc..9446960ac742 100644
--- a/drivers/bluetooth/bpa10x.c
+++ b/drivers/bluetooth/bpa10x.c
@@ -619,7 +619,6 @@ static void bpa10x_disconnect(struct usb_interface *intf)
}
static struct usb_driver bpa10x_driver = {
- .owner = THIS_MODULE,
.name = "bpa10x",
.probe = bpa10x_probe,
.disconnect = bpa10x_disconnect,
diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c
index 057cb2b6e6d1..92382e823285 100644
--- a/drivers/bluetooth/hci_usb.c
+++ b/drivers/bluetooth/hci_usb.c
@@ -1044,7 +1044,6 @@ static void hci_usb_disconnect(struct usb_interface *intf)
}
static struct usb_driver hci_usb_driver = {
- .owner = THIS_MODULE,
.name = "hci_usb",
.probe = hci_usb_probe,
.disconnect = hci_usb_disconnect,
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index b46a72d782d6..84e68cdd451b 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -687,7 +687,7 @@ config NVRAM
config RTC
tristate "Enhanced Real Time Clock Support"
- depends on !PPC32 && !PARISC && !IA64 && !M68K
+ depends on !PPC32 && !PARISC && !IA64 && !M68K && (!SPARC || PCI)
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
@@ -735,7 +735,7 @@ config SGI_IP27_RTC
config GEN_RTC
tristate "Generic /dev/rtc emulation"
- depends on RTC!=y && !IA64 && !ARM && !M32R && !SPARC32 && !SPARC64
+ depends on RTC!=y && !IA64 && !ARM && !M32R && !SPARC
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
diff --git a/drivers/char/drm/radeon_cp.c b/drivers/char/drm/radeon_cp.c
index 9f2b4efd0c7a..342302d46743 100644
--- a/drivers/char/drm/radeon_cp.c
+++ b/drivers/char/drm/radeon_cp.c
@@ -1311,7 +1311,7 @@ static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on)
static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
{
- drm_radeon_private_t *dev_priv = dev->dev_private;;
+ drm_radeon_private_t *dev_priv = dev->dev_private;
DRM_DEBUG("\n");
dev_priv->is_pci = init->is_pci;
@@ -1522,7 +1522,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
dev_priv->gart_size = init->gart_size;
dev_priv->gart_vm_start = dev_priv->fb_location
- + RADEON_READ(RADEON_CONFIG_APER_SIZE) * 2;
+ + RADEON_READ(RADEON_CONFIG_APER_SIZE);
#if __OS_HAS_AGP
if (!dev_priv->is_pci)
diff --git a/drivers/char/drm/radeon_drv.h b/drivers/char/drm/radeon_drv.h
index 7bda7e33d2bd..d92ccee3e54c 100644
--- a/drivers/char/drm/radeon_drv.h
+++ b/drivers/char/drm/radeon_drv.h
@@ -379,6 +379,7 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp,
# define RADEON_PLL_WR_EN (1 << 7)
#define RADEON_CLOCK_CNTL_INDEX 0x0008
#define RADEON_CONFIG_APER_SIZE 0x0108
+#define RADEON_CONFIG_MEMSIZE 0x00f8
#define RADEON_CRTC_OFFSET 0x0224
#define RADEON_CRTC_OFFSET_CNTL 0x0228
# define RADEON_CRTC_TILE_EN (1 << 15)
diff --git a/drivers/char/ip2/i2pack.h b/drivers/char/ip2/i2pack.h
index e9b87a78622c..00342a677c90 100644
--- a/drivers/char/ip2/i2pack.h
+++ b/drivers/char/ip2/i2pack.h
@@ -358,7 +358,7 @@ typedef struct _failStat
#define MB_OUT_STRIPPED 0x40 // Board has read all output from fifo
#define MB_FATAL_ERROR 0x20 // Board has encountered a fatal error
-#pragma pack(4) // Reset padding to command-line default
+#pragma pack() // Reset padding to command-line default
#endif // I2PACK_H
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index 449d029ad4f4..8b603b2d1c42 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -930,8 +930,8 @@ static void kbd_refresh_leds(struct input_handle *handle)
}
#if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(CONFIG_ALPHA) ||\
- defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_SPARC32) ||\
- defined(CONFIG_SPARC64) || defined(CONFIG_PARISC) || defined(CONFIG_SUPERH) ||\
+ defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) ||\
+ defined(CONFIG_PARISC) || defined(CONFIG_SUPERH) ||\
(defined(CONFIG_ARM) && defined(CONFIG_KEYBOARD_ATKBD) && !defined(CONFIG_ARCH_RPC))
#define HW_RAW(dev) (test_bit(EV_MSC, dev->evbit) && test_bit(MSC_RAW, dev->mscbit) &&\
@@ -958,7 +958,7 @@ static unsigned short x86_keycodes[256] =
extern int mac_hid_mouse_emulate_buttons(int, int, int);
#endif /* CONFIG_MAC_EMUMOUSEBTN */
-#if defined(CONFIG_SPARC32) || defined(CONFIG_SPARC64)
+#ifdef CONFIG_SPARC
static int sparc_l1_a_state = 0;
extern void sun_do_break(void);
#endif
@@ -1045,7 +1045,7 @@ static void kbd_keycode(unsigned int keycode, int down,
if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT)
sysrq_alt = down;
-#if defined(CONFIG_SPARC32) || defined(CONFIG_SPARC64)
+#ifdef CONFIG_SPARC
if (keycode == KEY_STOP)
sparc_l1_a_state = down;
#endif
@@ -1072,7 +1072,7 @@ static void kbd_keycode(unsigned int keycode, int down,
return;
}
#endif
-#if defined(CONFIG_SPARC32) || defined(CONFIG_SPARC64)
+#ifdef CONFIG_SPARC
if (keycode == KEY_A && sparc_l1_a_state) {
sparc_l1_a_state = 0;
sun_do_break();
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 7999da25fe40..bdfdfd28594d 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1554,10 +1554,8 @@ __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
EXPORT_SYMBOL(secure_tcp_sequence_number);
-
-
-/* Generate secure starting point for ephemeral TCP port search */
-u32 secure_tcp_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport)
+/* Generate secure starting point for ephemeral IPV4 transport port search */
+u32 secure_ipv4_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport)
{
struct keydata *keyptr = get_keyptr();
u32 hash[4];
@@ -1575,7 +1573,7 @@ u32 secure_tcp_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport)
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-u32 secure_tcpv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, __u16 dport)
+u32 secure_ipv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, __u16 dport)
{
struct keydata *keyptr = get_keyptr();
u32 hash[12];
@@ -1586,7 +1584,7 @@ u32 secure_tcpv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, __u16 dp
return twothirdsMD4Transform(daddr, hash);
}
-EXPORT_SYMBOL(secure_tcpv6_port_ephemeral);
+EXPORT_SYMBOL(secure_ipv6_port_ephemeral);
#endif
#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
index f66c7ad6fd38..3c1dafaa3441 100644
--- a/drivers/char/vc_screen.c
+++ b/drivers/char/vc_screen.c
@@ -419,7 +419,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
while (this_round > 1) {
unsigned short w;
- w = get_unaligned(((const unsigned short *)con_buf0));
+ w = get_unaligned(((unsigned short *)con_buf0));
vcs_scr_writew(vc, w, org++);
con_buf0 += 2;
this_round -= 2;
diff --git a/drivers/char/watchdog/booke_wdt.c b/drivers/char/watchdog/booke_wdt.c
index c800cce73c1e..b6640606b44d 100644
--- a/drivers/char/watchdog/booke_wdt.c
+++ b/drivers/char/watchdog/booke_wdt.c
@@ -173,7 +173,7 @@ static int __init booke_wdt_init(void)
int ret = 0;
printk (KERN_INFO "PowerPC Book-E Watchdog Timer Loaded\n");
- ident.firmware_version = cpu_specs[0].pvr_value;
+ ident.firmware_version = cur_cpu_spec->pvr_value;
ret = misc_register(&booke_wdt_miscdev);
if (ret) {
diff --git a/drivers/char/watchdog/pcwd_usb.c b/drivers/char/watchdog/pcwd_usb.c
index 092e9b133750..1533f56baa42 100644
--- a/drivers/char/watchdog/pcwd_usb.c
+++ b/drivers/char/watchdog/pcwd_usb.c
@@ -151,7 +151,6 @@ static void usb_pcwd_disconnect (struct usb_interface *interface);
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver usb_pcwd_driver = {
- .owner = THIS_MODULE,
.name = DRIVER_NAME,
.probe = usb_pcwd_probe,
.disconnect = usb_pcwd_disconnect,
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 815902c2c856..a9163d02983a 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -823,6 +823,30 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, unsigne
/**
+ * cpufreq_quick_get - get the CPU frequency (in kHz) frpm policy->cur
+ * @cpu: CPU number
+ *
+ * This is the last known freq, without actually getting it from the driver.
+ * Return value will be same as what is shown in scaling_cur_freq in sysfs.
+ */
+unsigned int cpufreq_quick_get(unsigned int cpu)
+{
+ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+ unsigned int ret = 0;
+
+ if (policy) {
+ down(&policy->lock);
+ ret = policy->cur;
+ up(&policy->lock);
+ cpufreq_cpu_put(policy);
+ }
+
+ return (ret);
+}
+EXPORT_SYMBOL(cpufreq_quick_get);
+
+
+/**
* cpufreq_get - get the current CPU frequency (in kHz)
* @cpu: CPU number
*
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 2ed5c4363b53..39543a2bed0f 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -93,7 +93,7 @@ static inline unsigned int get_cpu_idle_time(unsigned int cpu)
{
return kstat_cpu(cpu).cpustat.idle +
kstat_cpu(cpu).cpustat.iowait +
- ( !dbs_tuners_ins.ignore_nice ?
+ ( dbs_tuners_ins.ignore_nice ?
kstat_cpu(cpu).cpustat.nice :
0);
}
@@ -127,7 +127,7 @@ show_one(sampling_rate, sampling_rate);
show_one(sampling_down_factor, sampling_down_factor);
show_one(up_threshold, up_threshold);
show_one(down_threshold, down_threshold);
-show_one(ignore_nice, ignore_nice);
+show_one(ignore_nice_load, ignore_nice);
show_one(freq_step, freq_step);
static ssize_t store_sampling_down_factor(struct cpufreq_policy *unused,
@@ -207,7 +207,7 @@ static ssize_t store_down_threshold(struct cpufreq_policy *unused,
return count;
}
-static ssize_t store_ignore_nice(struct cpufreq_policy *policy,
+static ssize_t store_ignore_nice_load(struct cpufreq_policy *policy,
const char *buf, size_t count)
{
unsigned int input;
@@ -272,7 +272,7 @@ define_one_rw(sampling_rate);
define_one_rw(sampling_down_factor);
define_one_rw(up_threshold);
define_one_rw(down_threshold);
-define_one_rw(ignore_nice);
+define_one_rw(ignore_nice_load);
define_one_rw(freq_step);
static struct attribute * dbs_attributes[] = {
@@ -282,7 +282,7 @@ static struct attribute * dbs_attributes[] = {
&sampling_down_factor.attr,
&up_threshold.attr,
&down_threshold.attr,
- &ignore_nice.attr,
+ &ignore_nice_load.attr,
&freq_step.attr,
NULL
};
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 17741111246b..e69fd8dd1f1c 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -89,7 +89,7 @@ static inline unsigned int get_cpu_idle_time(unsigned int cpu)
{
return kstat_cpu(cpu).cpustat.idle +
kstat_cpu(cpu).cpustat.iowait +
- ( !dbs_tuners_ins.ignore_nice ?
+ ( dbs_tuners_ins.ignore_nice ?
kstat_cpu(cpu).cpustat.nice :
0);
}
@@ -122,7 +122,7 @@ static ssize_t show_##file_name \
show_one(sampling_rate, sampling_rate);
show_one(sampling_down_factor, sampling_down_factor);
show_one(up_threshold, up_threshold);
-show_one(ignore_nice, ignore_nice);
+show_one(ignore_nice_load, ignore_nice);
static ssize_t store_sampling_down_factor(struct cpufreq_policy *unused,
const char *buf, size_t count)
@@ -182,7 +182,7 @@ static ssize_t store_up_threshold(struct cpufreq_policy *unused,
return count;
}
-static ssize_t store_ignore_nice(struct cpufreq_policy *policy,
+static ssize_t store_ignore_nice_load(struct cpufreq_policy *policy,
const char *buf, size_t count)
{
unsigned int input;
@@ -223,7 +223,7 @@ __ATTR(_name, 0644, show_##_name, store_##_name)
define_one_rw(sampling_rate);
define_one_rw(sampling_down_factor);
define_one_rw(up_threshold);
-define_one_rw(ignore_nice);
+define_one_rw(ignore_nice_load);
static struct attribute * dbs_attributes[] = {
&sampling_rate_max.attr,
@@ -231,7 +231,7 @@ static struct attribute * dbs_attributes[] = {
&sampling_rate.attr,
&sampling_down_factor.attr,
&up_threshold.attr,
- &ignore_nice.attr,
+ &ignore_nice_load.attr,
NULL
};
diff --git a/drivers/fc4/Kconfig b/drivers/fc4/Kconfig
index f00c02a13ed6..345dbe6f10df 100644
--- a/drivers/fc4/Kconfig
+++ b/drivers/fc4/Kconfig
@@ -26,7 +26,7 @@ comment "FC4 drivers"
config FC4_SOC
tristate "Sun SOC/Sbus"
- depends on FC4!=n && (SPARC32 || SPARC64)
+ depends on FC4!=n && SPARC
help
Serial Optical Channel is an interface card with one or two Fibre
Optic ports, each of which can be connected to a disk array. Note
@@ -38,7 +38,7 @@ config FC4_SOC
config FC4_SOCAL
tristate "Sun SOC+ (aka SOCAL)"
- depends on FC4!=n && (SPARC32 || SPARC64)
+ depends on FC4!=n && SPARC
---help---
Serial Optical Channel Plus is an interface card with up to two
Fibre Optic ports. This card supports FC Arbitrated Loop (usually
@@ -62,7 +62,7 @@ config SCSI_PLUTO
be called pluto.
config SCSI_FCAL
- tristate "Sun Enterprise Network Array (A5000 and EX500)" if SPARC32 || SPARC64
+ tristate "Sun Enterprise Network Array (A5000 and EX500)" if SPARC
depends on FC4!=n && SCSI
help
This driver drives FC-AL disks connected through a Fibre Channel
@@ -75,7 +75,7 @@ config SCSI_FCAL
config SCSI_FCAL
prompt "Generic FC-AL disk driver"
- depends on FC4!=n && SCSI && !SPARC32 && !SPARC64
+ depends on FC4!=n && SCSI && !SPARC
endmenu
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index afd7634e5cc9..81031eb51056 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -529,14 +529,15 @@ mv64xxx_i2c_probe(struct platform_device *pd)
i2c_set_adapdata(&drv_data->adapter, drv_data);
if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
- MV64XXX_I2C_CTLR_NAME, drv_data)) {
-
- dev_err(dev, "mv64xxx: Can't register intr handler "
- "irq: %d\n", drv_data->irq);
+ MV64XXX_I2C_CTLR_NAME, drv_data)) {
+ dev_err(&drv_data->adapter.dev,
+ "mv64xxx: Can't register intr handler irq: %d\n",
+ drv_data->irq);
rc = -EINVAL;
goto exit_unmap_regs;
} else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
- dev_err(dev, "mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
+ dev_err(&drv_data->adapter.dev,
+ "mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
goto exit_free_irq;
}
diff --git a/drivers/ieee1394/hosts.h b/drivers/ieee1394/hosts.h
index 38f42112dff0..ae9b02cc013f 100644
--- a/drivers/ieee1394/hosts.h
+++ b/drivers/ieee1394/hosts.h
@@ -41,6 +41,7 @@ struct hpsb_host {
/* this nodes state */
unsigned in_bus_reset:1;
unsigned is_shutdown:1;
+ unsigned resume_packet_sent:1;
/* this nodes' duties on the bus */
unsigned is_root:1;
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
index 7fff5a1d2ea4..0ea37b1bccb2 100644
--- a/drivers/ieee1394/nodemgr.c
+++ b/drivers/ieee1394/nodemgr.c
@@ -1349,6 +1349,33 @@ static void nodemgr_update_pdrv(struct node_entry *ne)
}
+/* Write the BROADCAST_CHANNEL as per IEEE1394a 8.3.2.3.11 and 8.4.2.3. This
+ * seems like an optional service but in the end it is practically mandatory
+ * as a consequence of these clauses.
+ *
+ * Note that we cannot do a broadcast write to all nodes at once because some
+ * pre-1394a devices would hang. */
+static void nodemgr_irm_write_bc(struct node_entry *ne, int generation)
+{
+ const u64 bc_addr = (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL);
+ quadlet_t bc_remote, bc_local;
+ int ret;
+
+ if (!ne->host->is_irm || ne->generation != generation ||
+ ne->nodeid == ne->host->node_id)
+ return;
+
+ bc_local = cpu_to_be32(ne->host->csr.broadcast_channel);
+
+ /* Check if the register is implemented and 1394a compliant. */
+ ret = hpsb_read(ne->host, ne->nodeid, generation, bc_addr, &bc_remote,
+ sizeof(bc_remote));
+ if (!ret && bc_remote & cpu_to_be32(0x80000000) &&
+ bc_remote != bc_local)
+ hpsb_node_write(ne, bc_addr, &bc_local, sizeof(bc_local));
+}
+
+
static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int generation)
{
struct device *dev;
@@ -1360,6 +1387,8 @@ static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int ge
if (!dev)
return;
+ nodemgr_irm_write_bc(ne, generation);
+
/* If "needs_probe", then this is either a new or changed node we
* rescan totally. If the generation matches for an existing node
* (one that existed prior to the bus reset) we send update calls
@@ -1413,9 +1442,25 @@ static void nodemgr_node_probe(struct host_info *hi, int generation)
return;
}
-/* Because we are a 1394a-2000 compliant IRM, we need to inform all the other
- * nodes of the broadcast channel. (Really we're only setting the validity
- * bit). Other IRM responsibilities go in here as well. */
+static int nodemgr_send_resume_packet(struct hpsb_host *host)
+{
+ struct hpsb_packet *packet;
+ int ret = 1;
+
+ packet = hpsb_make_phypacket(host,
+ 0x003c0000 | NODEID_TO_NODE(host->node_id) << 24);
+ if (packet) {
+ packet->no_waiter = 1;
+ packet->generation = get_hpsb_generation(host);
+ ret = hpsb_send_packet(packet);
+ }
+ if (ret)
+ HPSB_WARN("fw-host%d: Failed to broadcast resume packet",
+ host->id);
+ return ret;
+}
+
+/* Perform a few high-level IRM responsibilities. */
static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles)
{
quadlet_t bc;
@@ -1424,13 +1469,8 @@ static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles)
if (!host->is_irm || host->irm_id == (nodeid_t)-1)
return 1;
- host->csr.broadcast_channel |= 0x40000000; /* set validity bit */
-
- bc = cpu_to_be32(host->csr.broadcast_channel);
-
- hpsb_write(host, LOCAL_BUS | ALL_NODES, get_hpsb_generation(host),
- (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL),
- &bc, sizeof(quadlet_t));
+ /* We are a 1394a-2000 compliant IRM. Set the validity bit. */
+ host->csr.broadcast_channel |= 0x40000000;
/* If there is no bus manager then we should set the root node's
* force_root bit to promote bus stability per the 1394
@@ -1463,6 +1503,13 @@ static int nodemgr_do_irm_duties(struct hpsb_host *host, int cycles)
}
}
+ /* Some devices suspend their ports while being connected to an inactive
+ * host adapter, i.e. if connected before the low-level driver is
+ * loaded. They become visible either when physically unplugged and
+ * replugged, or when receiving a resume packet. Send one once. */
+ if (!host->resume_packet_sent && !nodemgr_send_resume_packet(host))
+ host->resume_packet_sent = 1;
+
return 1;
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 475d98fa9e26..780009c7eaa6 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -47,6 +47,8 @@
#include <linux/ip.h>
#include <linux/in.h>
+#include <net/dst.h>
+
MODULE_AUTHOR("Roland Dreier");
MODULE_DESCRIPTION("IP-over-InfiniBand net driver");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index ef3ee035bbc8..ed0c2ead8bc1 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -43,6 +43,8 @@
#include <linux/delay.h>
#include <linux/completion.h>
+#include <net/dst.h>
+
#include "ipoib.h"
#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c
index 64b4a3080985..bc2fce60f9f8 100644
--- a/drivers/input/joystick/iforce/iforce-usb.c
+++ b/drivers/input/joystick/iforce/iforce-usb.c
@@ -235,7 +235,6 @@ static struct usb_device_id iforce_usb_ids [] = {
MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
struct usb_driver iforce_usb_driver = {
- .owner = THIS_MODULE,
.name = "iforce",
.probe = iforce_usb_probe,
.disconnect = iforce_usb_disconnect,
diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c
index 99a642d2a1fe..1849b176cf18 100644
--- a/drivers/input/joystick/warrior.c
+++ b/drivers/input/joystick/warrior.c
@@ -172,7 +172,7 @@ static int warrior_connect(struct serio *serio, struct serio_driver *drv)
input_set_abs_params(input_dev, ABS_Y, -64, 64, 0, 8);
input_set_abs_params(input_dev, ABS_THROTTLE, -112, 112, 0, 0);
input_set_abs_params(input_dev, ABS_HAT0X, -1, 1, 0, 0);
- input_set_abs_params(input_dev, ABS_HAT0X, -1, 1, 0, 0);
+ input_set_abs_params(input_dev, ABS_HAT0Y, -1, 1, 0, 0);
serio_set_drvdata(serio, warrior);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 07813fc0523f..e08dbe08f46d 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -26,7 +26,7 @@ config INPUT_PCSPKR
config INPUT_SPARCSPKR
tristate "SPARC Speaker support"
- depends on PCI && (SPARC32 || SPARC64)
+ depends on PCI && SPARC
help
Say Y here if you want the standard Speaker on Sparc PCI systems
to be used for bells and whistles.
diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c
index 4bf584364d28..2f9a04ae725f 100644
--- a/drivers/input/mouse/sermouse.c
+++ b/drivers/input/mouse/sermouse.c
@@ -95,7 +95,7 @@ static void sermouse_process_msc(struct sermouse *sermouse, signed char data, st
input_sync(dev);
- if (++sermouse->count == (5 - ((sermouse->type == SERIO_SUN) << 1)))
+ if (++sermouse->count == 5)
sermouse->count = 0;
}
diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
index 13835039a2a7..cbbf3842da5b 100644
--- a/drivers/input/serio/i8042.h
+++ b/drivers/input/serio/i8042.h
@@ -21,7 +21,7 @@
#include "i8042-ip22io.h"
#elif defined(CONFIG_PPC)
#include "i8042-ppcio.h"
-#elif defined(CONFIG_SPARC32) || defined(CONFIG_SPARC64)
+#elif defined(CONFIG_SPARC)
#include "i8042-sparcio.h"
#elif defined(CONFIG_X86) || defined(CONFIG_IA64)
#include "i8042-x86ia64io.h"
diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c
index f8457ef48826..ca5b4a3b683e 100644
--- a/drivers/isdn/hisax/hfc_usb.c
+++ b/drivers/isdn/hisax/hfc_usb.c
@@ -1715,7 +1715,6 @@ hfc_usb_disconnect(struct usb_interface
/* our driver information structure */
/************************************/
static struct usb_driver hfc_drv = {
- .owner = THIS_MODULE,
.name = "hfc_usb",
.id_table = hfcusb_idtab,
.probe = hfc_usb_probe,
diff --git a/drivers/isdn/hisax/st5481_init.c b/drivers/isdn/hisax/st5481_init.c
index 8e192a3a3490..99cb0f3d59a1 100644
--- a/drivers/isdn/hisax/st5481_init.c
+++ b/drivers/isdn/hisax/st5481_init.c
@@ -180,7 +180,6 @@ static struct usb_device_id st5481_ids[] = {
MODULE_DEVICE_TABLE (usb, st5481_ids);
static struct usb_driver st5481_usb_driver = {
- .owner = THIS_MODULE,
.name = "st5481_usb",
.probe = probe_st5481,
.disconnect = disconnect_st5481,
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
index 3fc8cdd94c3d..190878eef990 100644
--- a/drivers/macintosh/therm_pm72.c
+++ b/drivers/macintosh/therm_pm72.c
@@ -923,7 +923,7 @@ static void do_monitor_cpu_combined(void)
if (temp_combi >= ((state0->mpu.tmax + 8) << 16)) {
printk(KERN_WARNING "Warning ! Temperature way above maximum (%d) !\n",
temp_combi >> 16);
- state0->overtemp = CPU_MAX_OVERTEMP;
+ state0->overtemp += CPU_MAX_OVERTEMP / 4;
} else if (temp_combi > (state0->mpu.tmax << 16))
state0->overtemp++;
else
@@ -933,7 +933,7 @@ static void do_monitor_cpu_combined(void)
if (state0->overtemp > 0) {
state0->rpm = state0->mpu.rmaxn_exhaust_fan;
state0->intake_rpm = intake = state0->mpu.rmaxn_intake_fan;
- pump = state0->pump_min;
+ pump = state0->pump_max;
goto do_set_fans;
}
@@ -998,7 +998,7 @@ static void do_monitor_cpu_split(struct cpu_pid_state *state)
printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum"
" (%d) !\n",
state->index, temp >> 16);
- state->overtemp = CPU_MAX_OVERTEMP;
+ state->overtemp += CPU_MAX_OVERTEMP / 4;
} else if (temp > (state->mpu.tmax << 16))
state->overtemp++;
else
@@ -1060,7 +1060,7 @@ static void do_monitor_cpu_rack(struct cpu_pid_state *state)
printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum"
" (%d) !\n",
state->index, temp >> 16);
- state->overtemp = CPU_MAX_OVERTEMP;
+ state->overtemp = CPU_MAX_OVERTEMP / 4;
} else if (temp > (state->mpu.tmax << 16))
state->overtemp++;
else
diff --git a/drivers/md/md.c b/drivers/md/md.c
index cd12fca73b0d..8175a2a222da 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1729,7 +1729,7 @@ level_show(mddev_t *mddev, char *page)
if (p == NULL && mddev->raid_disks == 0)
return 0;
if (mddev->level >= 0)
- return sprintf(page, "RAID-%d\n", mddev->level);
+ return sprintf(page, "raid%d\n", mddev->level);
else
return sprintf(page, "%s\n", p->name);
}
diff --git a/drivers/media/dvb/b2c2/flexcop-usb.c b/drivers/media/dvb/b2c2/flexcop-usb.c
index 0a78ba3737a5..a6c91db40ad6 100644
--- a/drivers/media/dvb/b2c2/flexcop-usb.c
+++ b/drivers/media/dvb/b2c2/flexcop-usb.c
@@ -544,7 +544,6 @@ static struct usb_device_id flexcop_usb_table [] = {
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver flexcop_usb_driver = {
- .owner = THIS_MODULE,
.name = "b2c2_flexcop_usb",
.probe = flexcop_usb_probe,
.disconnect = flexcop_usb_disconnect,
diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c
index 336fc284fa52..b996fb59b7e4 100644
--- a/drivers/media/dvb/cinergyT2/cinergyT2.c
+++ b/drivers/media/dvb/cinergyT2/cinergyT2.c
@@ -986,7 +986,6 @@ static const struct usb_device_id cinergyt2_table [] __devinitdata = {
MODULE_DEVICE_TABLE(usb, cinergyt2_table);
static struct usb_driver cinergyt2_driver = {
- .owner = THIS_MODULE,
.name = "cinergyT2",
.probe = cinergyt2_probe,
.disconnect = cinergyt2_disconnect,
diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c
index 8c7beffb045f..ce44aa6bbb83 100644
--- a/drivers/media/dvb/dvb-usb/a800.c
+++ b/drivers/media/dvb/dvb-usb/a800.c
@@ -144,7 +144,6 @@ static struct dvb_usb_properties a800_properties = {
};
static struct usb_driver a800_driver = {
- .owner = THIS_MODULE,
.name = "dvb_usb_a800",
.probe = a800_probe,
.disconnect = dvb_usb_device_exit,
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index 3fe383f4bb4c..d05fab01cccd 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -241,7 +241,6 @@ static struct dvb_usb_properties cxusb_properties = {
};
static struct usb_driver cxusb_driver = {
- .owner = THIS_MODULE,
.name = "dvb_usb_cxusb",
.probe = cxusb_probe,
.disconnect = dvb_usb_device_exit,
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c
index aa271a2496d5..52ac3e5adf5d 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mb.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c
@@ -373,7 +373,6 @@ static struct dvb_usb_properties artec_t1_usb2_properties = {
};
static struct usb_driver dibusb_driver = {
- .owner = THIS_MODULE,
.name = "dvb_usb_dibusb_mb",
.probe = dibusb_probe,
.disconnect = dvb_usb_device_exit,
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c
index 6a0912eab396..55802fba3c29 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mc.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c
@@ -82,7 +82,6 @@ static struct dvb_usb_properties dibusb_mc_properties = {
};
static struct usb_driver dibusb_mc_driver = {
- .owner = THIS_MODULE,
.name = "dvb_usb_dibusb_mc",
.probe = dibusb_mc_probe,
.disconnect = dvb_usb_device_exit,
diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c
index f98e306a5759..450417a9e64b 100644
--- a/drivers/media/dvb/dvb-usb/digitv.c
+++ b/drivers/media/dvb/dvb-usb/digitv.c
@@ -233,7 +233,6 @@ static struct dvb_usb_properties digitv_properties = {
};
static struct usb_driver digitv_driver = {
- .owner = THIS_MODULE,
.name = "dvb_usb_digitv",
.probe = digitv_probe,
.disconnect = dvb_usb_device_exit,
diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c
index b595476332cd..6e2bac873445 100644
--- a/drivers/media/dvb/dvb-usb/dtt200u.c
+++ b/drivers/media/dvb/dvb-usb/dtt200u.c
@@ -198,7 +198,6 @@ static struct dvb_usb_properties wt220u_properties = {
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver dtt200u_usb_driver = {
- .owner = THIS_MODULE,
.name = "dvb_usb_dtt200u",
.probe = dtt200u_usb_probe,
.disconnect = dvb_usb_device_exit,
diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
index 1841a66427bf..fac48fc7a4ac 100644
--- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c
+++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
@@ -202,7 +202,6 @@ static struct dvb_usb_properties nova_t_properties = {
};
static struct usb_driver nova_t_driver = {
- .owner = THIS_MODULE,
.name = "dvb_usb_nova_t_usb2",
.probe = nova_t_probe,
.disconnect = dvb_usb_device_exit,
diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c
index 6fd67657c269..14f1911c79bb 100644
--- a/drivers/media/dvb/dvb-usb/umt-010.c
+++ b/drivers/media/dvb/dvb-usb/umt-010.c
@@ -128,7 +128,6 @@ static struct dvb_usb_properties umt_properties = {
};
static struct usb_driver umt_driver = {
- .owner = THIS_MODULE,
.name = "dvb_usb_umt_010",
.probe = umt_probe,
.disconnect = dvb_usb_device_exit,
diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c
index de13c04e8e64..afa00fdb5ec0 100644
--- a/drivers/media/dvb/dvb-usb/vp702x.c
+++ b/drivers/media/dvb/dvb-usb/vp702x.c
@@ -256,7 +256,6 @@ static struct dvb_usb_properties vp702x_properties = {
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver vp702x_usb_driver = {
- .owner = THIS_MODULE,
.name = "dvb-usb-vp702x",
.probe = vp702x_usb_probe,
.disconnect = dvb_usb_device_exit,
diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c
index 75765e3a569c..3835235b68df 100644
--- a/drivers/media/dvb/dvb-usb/vp7045.c
+++ b/drivers/media/dvb/dvb-usb/vp7045.c
@@ -253,7 +253,6 @@ static struct dvb_usb_properties vp7045_properties = {
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver vp7045_usb_driver = {
- .owner = THIS_MODULE,
.name = "dvb_usb_vp7045",
.probe = vp7045_usb_probe,
.disconnect = dvb_usb_device_exit,
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index 992be0be6b1e..7dae91e5863c 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -176,6 +176,9 @@ static void init_av7110_av(struct av7110 *av7110)
}
}
+ if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000e)
+ av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, SpdifSwitch, 1, 0); // SPDIF on
+
ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right);
if (ret < 0)
printk("dvb-ttpci:cannot set volume :%d\n",ret);
diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h
index fedd20f9815d..2a5e87ba1052 100644
--- a/drivers/media/dvb/ttpci/av7110_hw.h
+++ b/drivers/media/dvb/ttpci/av7110_hw.h
@@ -143,7 +143,8 @@ enum av7110_audio_command {
MainSwitch,
ADSwitch,
SendDiSEqC,
- SetRegister
+ SetRegister,
+ SpdifSwitch
};
enum av7110_request_command {
diff --git a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c
index 9774e94d1e7d..1439cb752874 100644
--- a/drivers/media/video/cpia_usb.c
+++ b/drivers/media/video/cpia_usb.c
@@ -582,7 +582,6 @@ MODULE_LICENSE("GPL");
static struct usb_driver cpia_driver = {
- .owner = THIS_MODULE,
.name = "cpia",
.probe = cpia_probe,
.disconnect = cpia_disconnect,
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index aea3f038cff6..5b93723a1768 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -333,24 +333,30 @@ static int set_input(struct i2c_client *client, enum cx25840_input input)
static int set_v4lstd(struct i2c_client *client, v4l2_std_id std)
{
- u8 fmt;
-
- switch (std) {
- /* zero is autodetect */
- case 0: fmt = 0x0; break;
- /* default ntsc to ntsc-m */
- case V4L2_STD_NTSC:
- case V4L2_STD_NTSC_M: fmt = 0x1; break;
- case V4L2_STD_NTSC_M_JP: fmt = 0x2; break;
- case V4L2_STD_NTSC_443: fmt = 0x3; break;
- case V4L2_STD_PAL: fmt = 0x4; break;
- case V4L2_STD_PAL_M: fmt = 0x5; break;
- case V4L2_STD_PAL_N: fmt = 0x6; break;
- case V4L2_STD_PAL_Nc: fmt = 0x7; break;
- case V4L2_STD_PAL_60: fmt = 0x8; break;
- case V4L2_STD_SECAM: fmt = 0xc; break;
- default:
- return -ERANGE;
+ u8 fmt=0; /* zero is autodetect */
+
+ /* First tests should be against specific std */
+ if (std & V4L2_STD_NTSC_M_JP) {
+ fmt=0x2;
+ } else if (std & V4L2_STD_NTSC_443) {
+ fmt=0x3;
+ } else if (std & V4L2_STD_PAL_M) {
+ fmt=0x5;
+ } else if (std & V4L2_STD_PAL_N) {
+ fmt=0x6;
+ } else if (std & V4L2_STD_PAL_Nc) {
+ fmt=0x7;
+ } else if (std & V4L2_STD_PAL_60) {
+ fmt=0x8;
+ } else {
+ /* Then, test against generic ones */
+ if (std & V4L2_STD_NTSC) {
+ fmt=0x1;
+ } else if (std & V4L2_STD_PAL) {
+ fmt=0x4;
+ } else if (std & V4L2_STD_SECAM) {
+ fmt=0xc;
+ }
}
cx25840_and_or(client, 0x400, ~0xf, fmt);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index ec11619f8ea9..0cfe75416ec6 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -39,7 +39,7 @@ MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
#define em28xx_coredbg(fmt, arg...) do {\
if (core_debug) \
printk(KERN_INFO "%s %s :"fmt, \
- dev->name, __FUNCTION__, ##arg); } while (0)
+ dev->name, __FUNCTION__ , ##arg); } while (0)
static unsigned int reg_debug;
module_param(reg_debug,int,0644);
@@ -48,7 +48,7 @@ MODULE_PARM_DESC(reg_debug,"enable debug messages [URB reg]");
#define em28xx_regdbg(fmt, arg...) do {\
if (reg_debug) \
printk(KERN_INFO "%s %s :"fmt, \
- dev->name, __FUNCTION__, ##arg); } while (0)
+ dev->name, __FUNCTION__ , ##arg); } while (0)
static unsigned int isoc_debug;
module_param(isoc_debug,int,0644);
@@ -57,7 +57,7 @@ MODULE_PARM_DESC(isoc_debug,"enable debug messages [isoc transfers]");
#define em28xx_isocdbg(fmt, arg...) do {\
if (isoc_debug) \
printk(KERN_INFO "%s %s :"fmt, \
- dev->name, __FUNCTION__, ##arg); } while (0)
+ dev->name, __FUNCTION__ , ##arg); } while (0)
static int alt = EM28XX_PINOUT;
module_param(alt, int, 0644);
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index 29e21ad187cc..7f5603054f02 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -44,7 +44,7 @@ MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
printk(fmt, ##args); } while (0)
#define dprintk2(lvl,fmt, args...) if (i2c_debug>=lvl) do{ \
printk(KERN_DEBUG "%s at %s: " fmt, \
- dev->name, __FUNCTION__, ##args); } while (0)
+ dev->name, __FUNCTION__ , ##args); } while (0)
/*
* em2800_i2c_send_max4()
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 8ecaa0803e08..3a56120397ae 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -45,7 +45,7 @@
#define em28xx_videodbg(fmt, arg...) do {\
if (video_debug) \
printk(KERN_INFO "%s %s :"fmt, \
- dev->name, __FUNCTION__, ##arg); } while (0)
+ dev->name, __FUNCTION__ , ##arg); } while (0)
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
@@ -1884,7 +1884,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
}
static struct usb_driver em28xx_usb_driver = {
- .owner = THIS_MODULE,
.name = "em28xx",
.probe = em28xx_usb_probe,
.disconnect = em28xx_usb_disconnect,
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 1e2ee43db394..5c7a41ce69f3 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -392,18 +392,18 @@ extern const unsigned int em28xx_bcount;
/* printk macros */
#define em28xx_err(fmt, arg...) do {\
- printk(KERN_ERR fmt, ##arg); } while (0)
+ printk(KERN_ERR fmt , ##arg); } while (0)
#define em28xx_errdev(fmt, arg...) do {\
printk(KERN_ERR "%s: "fmt,\
- dev->name, ##arg); } while (0)
+ dev->name , ##arg); } while (0)
#define em28xx_info(fmt, arg...) do {\
printk(KERN_INFO "%s: "fmt,\
- dev->name, ##arg); } while (0)
+ dev->name , ##arg); } while (0)
#define em28xx_warn(fmt, arg...) do {\
printk(KERN_WARNING "%s: "fmt,\
- dev->name, ##arg); } while (0)
+ dev->name , ##arg); } while (0)
inline static int em28xx_audio_source(struct em28xx *dev, int input)
{
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
index 3428e1ed0032..c36f014f1fdf 100644
--- a/drivers/media/video/saa7127.c
+++ b/drivers/media/video/saa7127.c
@@ -389,7 +389,7 @@ static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_dat
static int saa7127_set_cc(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
{
struct saa7127_state *state = i2c_get_clientdata(client);
- u16 cc = data->data[0] << 8 | data->data[1];
+ u16 cc = data->data[1] << 8 | data->data[0];
int enable = (data->line != 0);
if (enable && (data->field != 0 || data->line != 21))
@@ -397,7 +397,7 @@ static int saa7127_set_cc(struct i2c_client *client, struct v4l2_sliced_vbi_data
if (state->cc_enable != enable) {
saa7127_dbg("Turn CC %s\n", enable ? "on" : "off");
saa7127_write(client, SAA7127_REG_CLOSED_CAPTION,
- (enable << 6) | 0x11);
+ (state->xds_enable << 7) | (enable << 6) | 0x11);
state->cc_enable = enable;
}
if (!enable)
@@ -423,7 +423,7 @@ static int saa7127_set_xds(struct i2c_client *client, struct v4l2_sliced_vbi_dat
if (state->xds_enable != enable) {
saa7127_dbg("Turn XDS %s\n", enable ? "on" : "off");
saa7127_write(client, SAA7127_REG_CLOSED_CAPTION,
- (enable << 7) | 0x11);
+ (enable << 7) | (state->cc_enable << 6) | 0x11);
state->xds_enable = enable;
}
if (!enable)
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
index c512c4411b38..8a5c3e71b37d 100644
--- a/drivers/media/video/saa7134/Kconfig
+++ b/drivers/media/video/saa7134/Kconfig
@@ -1,11 +1,10 @@
config VIDEO_SAA7134
tristate "Philips SAA7134 support"
- depends on VIDEO_DEV && PCI && I2C && SOUND && SND
+ depends on VIDEO_DEV && PCI && I2C
select VIDEO_BUF
select VIDEO_IR
select VIDEO_TUNER
select CRC32
- select SND_PCM_OSS
---help---
This is a video4linux driver for Philips SAA713x based
TV cards.
@@ -13,6 +12,29 @@ config VIDEO_SAA7134
To compile this driver as a module, choose M here: the
module will be called saa7134.
+config VIDEO_SAA7134_ALSA
+ tristate "Philips SAA7134 DMA audio support"
+ depends on VIDEO_SAA7134 && SND
+ select SND_PCM_OSS
+ ---help---
+ This is a video4linux driver for direct (DMA) audio in
+ Philips SAA713x based TV cards using ALSA
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7134-alsa.
+
+config VIDEO_SAA7134_OSS
+ tristate "Philips SAA7134 DMA audio support (OSS, DEPRECATED)"
+ depends on VIDEO_SAA7134 && SOUND_PRIME && !VIDEO_SAA7134_ALSA
+ ---help---
+ This is a video4linux driver for direct (DMA) audio in
+ Philips SAA713x based TV cards using OSS
+
+ This is deprecated in favor of the ALSA module
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7134-oss.
+
config VIDEO_SAA7134_DVB
tristate "DVB/ATSC Support for saa7134 based TV cards"
depends on VIDEO_SAA7134 && DVB_CORE
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
index 134f83a96218..1ba998424bbd 100644
--- a/drivers/media/video/saa7134/Makefile
+++ b/drivers/media/video/saa7134/Makefile
@@ -4,8 +4,11 @@ saa7134-objs := saa7134-cards.o saa7134-core.o saa7134-i2c.o \
saa7134-video.o saa7134-input.o
obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o saa7134-empress.o \
- saa6752hs.o saa7134-alsa.o \
- saa7134-oss.o
+ saa6752hs.o
+
+obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
+obj-$(CONFIG_VIDEO_SAA7134_OSS) += saa7134-oss.o
+
obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
EXTRA_CFLAGS += -I$(src)/..
diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c
index b24a26b065c2..ade05f75fdb0 100644
--- a/drivers/media/video/saa7134/saa7134-alsa.c
+++ b/drivers/media/video/saa7134/saa7134-alsa.c
@@ -60,7 +60,7 @@ module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for SAA7134 capture interface(s).");
#define dprintk(fmt, arg...) if (debug) \
- printk(KERN_DEBUG "%s/alsa: " fmt, dev->name, ## arg)
+ printk(KERN_DEBUG "%s/alsa: " fmt, dev->name , ##arg)
@@ -989,6 +989,14 @@ static int saa7134_alsa_init(void)
struct saa7134_dev *dev = NULL;
struct list_head *list;
+ if (!dmasound_init && !dmasound_exit) {
+ dmasound_init = alsa_device_init;
+ dmasound_exit = alsa_device_exit;
+ } else {
+ printk(KERN_WARNING "saa7134 ALSA: can't load, DMA sound handler already assigned (probably to OSS)\n");
+ return -EBUSY;
+ }
+
printk(KERN_INFO "saa7134 ALSA driver for DMA sound loaded\n");
list_for_each(list,&saa7134_devlist) {
@@ -1001,9 +1009,6 @@ static int saa7134_alsa_init(void)
}
}
- dmasound_init = alsa_device_init;
- dmasound_exit = alsa_device_exit;
-
if (dev == NULL)
printk(KERN_INFO "saa7134 ALSA: no saa7134 cards found\n");
@@ -1023,12 +1028,15 @@ static void saa7134_alsa_exit(void)
snd_card_free(snd_saa7134_cards[idx]);
}
+ dmasound_init = NULL;
+ dmasound_exit = NULL;
printk(KERN_INFO "saa7134 ALSA driver for DMA sound unloaded\n");
return;
}
-module_init(saa7134_alsa_init);
+/* We initialize this late, to make sure the sound system is up and running */
+late_initcall(saa7134_alsa_init);
module_exit(saa7134_alsa_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ricardo Cerqueira");
diff --git a/drivers/media/video/saa7134/saa7134-oss.c b/drivers/media/video/saa7134/saa7134-oss.c
index 513a699a6df2..8badd2a9cb2f 100644
--- a/drivers/media/video/saa7134/saa7134-oss.c
+++ b/drivers/media/video/saa7134/saa7134-oss.c
@@ -959,8 +959,17 @@ static int saa7134_oss_init(void)
struct saa7134_dev *dev = NULL;
struct list_head *list;
+ if (!dmasound_init && !dmasound_exit) {
+ dmasound_init = oss_device_init;
+ dmasound_exit = oss_device_exit;
+ } else {
+ printk(KERN_WARNING "saa7134 OSS: can't load, DMA sound handler already assigned (probably to ALSA)\n");
+ return -EBUSY;
+ }
+
printk(KERN_INFO "saa7134 OSS driver for DMA sound loaded\n");
+
list_for_each(list,&saa7134_devlist) {
dev = list_entry(list, struct saa7134_dev, devlist);
if (dev->dmasound.priv_data == NULL) {
@@ -974,9 +983,6 @@ static int saa7134_oss_init(void)
if (dev == NULL)
printk(KERN_INFO "saa7134 OSS: no saa7134 cards found\n");
- dmasound_init = oss_device_init;
- dmasound_exit = oss_device_exit;
-
return 0;
}
@@ -997,12 +1003,16 @@ static void saa7134_oss_exit(void)
}
+ dmasound_init = NULL;
+ dmasound_exit = NULL;
+
printk(KERN_INFO "saa7134 OSS driver for DMA sound unloaded\n");
return;
}
-module_init(saa7134_oss_init);
+/* We initialize this late, to make sure the sound system is up and running */
+late_initcall(saa7134_oss_init);
module_exit(saa7134_oss_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
index cd7cf1bd12b4..5ac235365dd8 100644
--- a/drivers/media/video/tveeprom.c
+++ b/drivers/media/video/tveeprom.c
@@ -206,7 +206,7 @@ hauppauge_tuner[] =
{ TUNER_ABSENT, "TCL 2002MI_3H"},
{ TUNER_TCL_2002N, "TCL 2002N 5H"},
/* 100-109 */
- { TUNER_ABSENT, "Philips FMD1216ME"},
+ { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"},
{ TUNER_TEA5767, "Philips TEA5768HL FM Radio"},
{ TUNER_ABSENT, "Panasonic ENV57H12D5"},
{ TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"},
diff --git a/drivers/message/i2o/pci.c b/drivers/message/i2o/pci.c
index 81ef306cb124..ee7075fa1ec3 100644
--- a/drivers/message/i2o/pci.c
+++ b/drivers/message/i2o/pci.c
@@ -303,6 +303,7 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev,
struct i2o_controller *c;
int rc;
struct pci_dev *i960 = NULL;
+ int pci_dev_busy = 0;
printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n");
@@ -395,6 +396,8 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev,
if ((rc = i2o_pci_alloc(c))) {
printk(KERN_ERR "%s: DMA / IO allocation for I2O controller "
" failed\n", c->name);
+ if (rc == -ENODEV)
+ pci_dev_busy = 1;
goto free_controller;
}
@@ -425,7 +428,8 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev,
i2o_iop_free(c);
disable:
- pci_disable_device(pdev);
+ if (!pci_dev_busy)
+ pci_disable_device(pdev);
return rc;
}
diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c
index d91fcf7c3178..abcf19116d70 100644
--- a/drivers/mmc/mmc_block.c
+++ b/drivers/mmc/mmc_block.c
@@ -359,7 +359,12 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
md->block_bits = card->csd.read_blkbits;
blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits);
- set_capacity(md->disk, card->csd.capacity);
+
+ /*
+ * The CSD capacity field is in units of read_blkbits.
+ * set_capacity takes units of 512 bytes.
+ */
+ set_capacity(md->disk, card->csd.capacity << (card->csd.read_blkbits - 9));
}
out:
return md;
@@ -373,7 +378,7 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
mmc_card_claim_host(card);
cmd.opcode = MMC_SET_BLOCKLEN;
- cmd.arg = 1 << card->csd.read_blkbits;
+ cmd.arg = 1 << md->block_bits;
cmd.flags = MMC_RSP_R1;
err = mmc_wait_for_cmd(card->host, &cmd, 5);
mmc_card_release_host(card);
@@ -412,10 +417,9 @@ static int mmc_blk_probe(struct mmc_card *card)
if (err)
goto out;
- printk(KERN_INFO "%s: %s %s %dKiB %s\n",
+ printk(KERN_INFO "%s: %s %s %luKiB %s\n",
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
- (card->csd.capacity << card->csd.read_blkbits) / 1024,
- mmc_blk_readonly(card)?"(ro)":"");
+ get_capacity(md->disk) >> 1, mmc_blk_readonly(card)?"(ro)":"");
mmc_set_drvdata(card, md);
add_disk(md->disk);
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 452ccd5037c3..b9b77cf39a18 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -62,7 +62,7 @@ config MTD_PHYSMAP_BANKWIDTH
config MTD_SUN_UFLASH
tristate "Sun Microsystems userflash support"
- depends on (SPARC32 || SPARC64) && MTD_CFI
+ depends on SPARC && MTD_CFI
help
This provides a 'mapping' driver which supports the way in
which user-programmable flash chips are connected on various
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c
index 48cce431f89f..45c077d0f063 100644
--- a/drivers/mtd/onenand/generic.c
+++ b/drivers/mtd/onenand/generic.c
@@ -12,9 +12,9 @@
* This is a device driver for the OneNAND flash for generic boards.
*/
-#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
#include <linux/mtd/partitions.h>
@@ -39,7 +39,7 @@ static int __devinit generic_onenand_probe(struct device *dev)
{
struct onenand_info *info;
struct platform_device *pdev = to_platform_device(dev);
- struct onenand_platform_data *pdata = pdev->dev.platform_data;
+ struct flash_platform_data *pdata = pdev->dev.platform_data;
struct resource *res = pdev->resource;
unsigned long size = res->end - res->start + 1;
int err;
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index f67d5d6eb9a6..a53a73fc2a5a 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -940,7 +940,7 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
u_char *eccbuf, struct nand_oobinfo *oobsel)
{
struct onenand_chip *this = mtd->priv;
- unsigned char buffer[MAX_ONENAND_PAGESIZE], *pbuf;
+ unsigned char *pbuf;
size_t total_len, len;
int i, written = 0;
int ret = 0;
@@ -975,7 +975,7 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
/* Loop until all keve's data has been written */
len = 0;
while (count) {
- pbuf = buffer;
+ pbuf = this->page_buf;
/*
* If the given tuple is >= pagesize then
* write it out from the iov
@@ -995,7 +995,7 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
int cnt = 0, thislen;
while (cnt < mtd->oobblock) {
thislen = min_t(int, mtd->oobblock - cnt, vecs->iov_len - len);
- memcpy(buffer + cnt, vecs->iov_base + len, thislen);
+ memcpy(this->page_buf + cnt, vecs->iov_base + len, thislen);
cnt += thislen;
len += thislen;
@@ -1296,6 +1296,12 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
/* Block lock scheme */
for (block = start; block < end; block++) {
+ /* Set block address */
+ value = onenand_block_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
+ /* Select DataRAM for DDP */
+ value = onenand_bufferram_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
/* Set start block address */
this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
/* Write unlock command */
@@ -1309,10 +1315,6 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
& ONENAND_CTRL_ONGO)
continue;
- /* Set block address for read block status */
- value = onenand_block_address(this, block);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
-
/* Check lock status */
status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
if (!(status & ONENAND_WP_US))
@@ -1346,7 +1348,6 @@ static void onenand_print_device_info(int device)
static const struct onenand_manufacturers onenand_manuf_ids[] = {
{ONENAND_MFR_SAMSUNG, "Samsung"},
- {ONENAND_MFR_UNKNOWN, "Unknown"}
};
/**
@@ -1357,17 +1358,22 @@ static const struct onenand_manufacturers onenand_manuf_ids[] = {
*/
static int onenand_check_maf(int manuf)
{
+ int size = ARRAY_SIZE(onenand_manuf_ids);
+ char *name;
int i;
- for (i = 0; onenand_manuf_ids[i].id; i++) {
+ for (i = 0; i < size; i++)
if (manuf == onenand_manuf_ids[i].id)
break;
- }
- printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n",
- onenand_manuf_ids[i].name, manuf);
+ if (i < size)
+ name = onenand_manuf_ids[i].name;
+ else
+ name = "Unknown";
+
+ printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
- return (i != ONENAND_MFR_UNKNOWN);
+ return (i == size);
}
/**
@@ -1513,6 +1519,18 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
this->read_bufferram = onenand_sync_read_bufferram;
}
+ /* Allocate buffers, if necessary */
+ if (!this->page_buf) {
+ size_t len;
+ len = mtd->oobblock + mtd->oobsize;
+ this->page_buf = kmalloc(len, GFP_KERNEL);
+ if (!this->page_buf) {
+ printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n");
+ return -ENOMEM;
+ }
+ this->options |= ONENAND_PAGEBUF_ALLOC;
+ }
+
this->state = FL_READY;
init_waitqueue_head(&this->wq);
spin_lock_init(&this->chip_lock);
@@ -1574,12 +1592,21 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
*/
void onenand_release(struct mtd_info *mtd)
{
+ struct onenand_chip *this = mtd->priv;
+
#ifdef CONFIG_MTD_PARTITIONS
/* Deregister partitions */
del_mtd_partitions (mtd);
#endif
/* Deregister the device */
del_mtd_device (mtd);
+
+ /* Free bad block table memory, if allocated */
+ if (this->bbm)
+ kfree(this->bbm);
+ /* Buffer allocated by onenand_scan */
+ if (this->options & ONENAND_PAGEBUF_ALLOC)
+ kfree(this->page_buf);
}
EXPORT_SYMBOL_GPL(onenand_scan);
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
index f40190f499e1..4510d3361eaa 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -118,10 +118,10 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
*/
static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
- unsigned char data_buf[MAX_ONENAND_PAGESIZE];
+ struct onenand_chip *this = mtd->priv;
bd->options &= ~NAND_BBT_SCANEMPTY;
- return create_bbt(mtd, data_buf, bd, -1);
+ return create_bbt(mtd, this->page_buf, bd, -1);
}
/**
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 525624fc03b4..c39344adecce 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -10,7 +10,7 @@
* trademarks of NVIDIA Corporation in the United States and other
* countries.
*
- * Copyright (C) 2003,4 Manfred Spraul
+ * Copyright (C) 2003,4,5 Manfred Spraul
* Copyright (C) 2004 Andrew de Quincey (wol support)
* Copyright (C) 2004 Carl-Daniel Hailfinger (invalid MAC handling, insane
* IRQ rate fixes, bigendian fixes, cleanups, verification)
@@ -100,6 +100,7 @@
* 0.45: 18 Sep 2005: Remove nv_stop/start_rx from every link check
* 0.46: 20 Oct 2005: Add irq optimization modes.
* 0.47: 26 Oct 2005: Add phyaddr 0 in phy scan.
+ * 0.48: 24 Dec 2005: Disable TSO, bugfix for pci_map_single
*
* Known bugs:
* We suspect that on some hardware no TX done interrupts are generated.
@@ -111,7 +112,7 @@
* DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
* superfluous timer interrupts from the nic.
*/
-#define FORCEDETH_VERSION "0.47"
+#define FORCEDETH_VERSION "0.48"
#define DRV_NAME "forcedeth"
#include <linux/module.h>
@@ -871,8 +872,8 @@ static int nv_alloc_rx(struct net_device *dev)
} else {
skb = np->rx_skbuff[nr];
}
- np->rx_dma[nr] = pci_map_single(np->pci_dev, skb->data, skb->len,
- PCI_DMA_FROMDEVICE);
+ np->rx_dma[nr] = pci_map_single(np->pci_dev, skb->data,
+ skb->end-skb->data, PCI_DMA_FROMDEVICE);
if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) {
np->rx_ring.orig[nr].PacketBuffer = cpu_to_le32(np->rx_dma[nr]);
wmb();
@@ -999,7 +1000,7 @@ static void nv_drain_rx(struct net_device *dev)
wmb();
if (np->rx_skbuff[i]) {
pci_unmap_single(np->pci_dev, np->rx_dma[i],
- np->rx_skbuff[i]->len,
+ np->rx_skbuff[i]->end-np->rx_skbuff[i]->data,
PCI_DMA_FROMDEVICE);
dev_kfree_skb(np->rx_skbuff[i]);
np->rx_skbuff[i] = NULL;
@@ -1334,7 +1335,7 @@ static void nv_rx_process(struct net_device *dev)
* the performance.
*/
pci_unmap_single(np->pci_dev, np->rx_dma[i],
- np->rx_skbuff[i]->len,
+ np->rx_skbuff[i]->end-np->rx_skbuff[i]->data,
PCI_DMA_FROMDEVICE);
{
@@ -2455,7 +2456,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
np->txrxctl_bits |= NVREG_TXRXCTL_RXCHECK;
dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG;
#ifdef NETIF_F_TSO
- dev->features |= NETIF_F_TSO;
+ /* disabled dev->features |= NETIF_F_TSO; */
#endif
}
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
index c22c0517883c..fa176ffb4ad5 100644
--- a/drivers/net/irda/irda-usb.c
+++ b/drivers/net/irda/irda-usb.c
@@ -1539,7 +1539,6 @@ static void irda_usb_disconnect(struct usb_interface *intf)
* USB device callbacks
*/
static struct usb_driver irda_driver = {
- .owner = THIS_MODULE,
.name = "irda-usb",
.probe = irda_usb_probe,
.disconnect = irda_usb_disconnect,
diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c
index 3961a754e920..31867e4b891b 100644
--- a/drivers/net/irda/stir4200.c
+++ b/drivers/net/irda/stir4200.c
@@ -1152,7 +1152,6 @@ static int stir_resume(struct usb_interface *intf)
* USB device callbacks
*/
static struct usb_driver irda_driver = {
- .owner = THIS_MODULE,
.name = "stir4200",
.probe = stir_probe,
.disconnect = stir_disconnect,
diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c
index f857ae94d261..b0c3b6ab6263 100644
--- a/drivers/net/ns83820.c
+++ b/drivers/net/ns83820.c
@@ -115,6 +115,7 @@
#include <linux/ethtool.h>
#include <linux/timer.h>
#include <linux/if_vlan.h>
+#include <linux/rtnetlink.h>
#include <asm/io.h>
#include <asm/uaccess.h>
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 16bebe7a7ce1..7da0e3dd5fe3 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -38,6 +38,10 @@
#include <asm/irq.h>
#include <asm/uaccess.h>
+MODULE_DESCRIPTION("PHY library");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
static struct phy_driver genphy_driver;
extern int mdio_bus_init(void);
extern void mdio_bus_exit(void);
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 50430f79f8cf..1c6d328165bb 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -524,9 +524,6 @@ static int get_filter(void __user *arg, struct sock_filter **p)
if (copy_from_user(&uprog, arg, sizeof(uprog)))
return -EFAULT;
- if (uprog.len > BPF_MAXINSNS)
- return -EINVAL;
-
if (!uprog.len) {
*p = NULL;
return 0;
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index a842ecc60a34..9369f811075d 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -85,7 +85,7 @@ static int pppoe_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
static int pppoe_xmit(struct ppp_channel *chan, struct sk_buff *skb);
static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb);
-static struct proto_ops pppoe_ops;
+static const struct proto_ops pppoe_ops;
static DEFINE_RWLOCK(pppoe_hash_lock);
static struct ppp_channel_ops pppoe_chan_ops;
@@ -383,8 +383,6 @@ static int pppoe_rcv(struct sk_buff *skb,
{
struct pppoe_hdr *ph;
struct pppox_sock *po;
- struct sock *sk;
- int ret;
if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
goto drop;
@@ -395,24 +393,8 @@ static int pppoe_rcv(struct sk_buff *skb,
ph = (struct pppoe_hdr *) skb->nh.raw;
po = get_item((unsigned long) ph->sid, eth_hdr(skb)->h_source);
- if (!po)
- goto drop;
-
- sk = sk_pppox(po);
- bh_lock_sock(sk);
-
- /* Socket state is unknown, must put skb into backlog. */
- if (sock_owned_by_user(sk) != 0) {
- sk_add_backlog(sk, skb);
- ret = NET_RX_SUCCESS;
- } else {
- ret = pppoe_rcv_core(sk, skb);
- }
-
- bh_unlock_sock(sk);
- sock_put(sk);
-
- return ret;
+ if (po != NULL)
+ return sk_receive_skb(sk_pppox(po), skb);
drop:
kfree_skb(skb);
out:
@@ -1081,9 +1063,7 @@ static int __init pppoe_proc_init(void)
static inline int pppoe_proc_init(void) { return 0; }
#endif /* CONFIG_PROC_FS */
-/* ->ioctl are set at pppox_create */
-
-static struct proto_ops pppoe_ops = {
+static const struct proto_ops pppoe_ops = {
.family = AF_PPPOX,
.owner = THIS_MODULE,
.release = pppoe_release,
@@ -1099,7 +1079,8 @@ static struct proto_ops pppoe_ops = {
.getsockopt = sock_no_getsockopt,
.sendmsg = pppoe_sendmsg,
.recvmsg = pppoe_recvmsg,
- .mmap = sock_no_mmap
+ .mmap = sock_no_mmap,
+ .ioctl = pppox_ioctl,
};
static struct pppox_proto pppoe_proto = {
diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c
index 0c1e114527fb..9315046b3f55 100644
--- a/drivers/net/pppox.c
+++ b/drivers/net/pppox.c
@@ -68,8 +68,7 @@ EXPORT_SYMBOL(register_pppox_proto);
EXPORT_SYMBOL(unregister_pppox_proto);
EXPORT_SYMBOL(pppox_unbind_sock);
-static int pppox_ioctl(struct socket* sock, unsigned int cmd,
- unsigned long arg)
+int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
struct pppox_sock *po = pppox_sk(sk);
@@ -105,6 +104,7 @@ static int pppox_ioctl(struct socket* sock, unsigned int cmd,
return rc;
}
+EXPORT_SYMBOL(pppox_ioctl);
static int pppox_create(struct socket *sock, int protocol)
{
@@ -119,11 +119,7 @@ static int pppox_create(struct socket *sock, int protocol)
goto out;
rc = pppox_protos[protocol]->create(sock);
- if (!rc) {
- /* We get to set the ioctl handler. */
- /* For everything else, pppox is just a shell. */
- sock->ops->ioctl = pppox_ioctl;
- }
+
module_put(pppox_protos[protocol]->owner);
out:
return rc;
diff --git a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c
index ae7343934758..e1a2d52cc1fe 100644
--- a/drivers/net/sk98lin/skge.c
+++ b/drivers/net/sk98lin/skge.c
@@ -107,6 +107,7 @@
#include "h/skversion.h"
+#include <linux/in.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index 00d683063c01..d8cc3aea032a 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -25,6 +25,7 @@
*/
#include <linux/config.h>
+#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c
index 081717d01374..28ce47a02408 100644
--- a/drivers/net/sungem.c
+++ b/drivers/net/sungem.c
@@ -2907,7 +2907,7 @@ static int __devinit gem_get_device_address(struct gem *gp)
return 0;
}
-static void __devexit gem_remove_one(struct pci_dev *pdev)
+static void gem_remove_one(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
@@ -3181,7 +3181,7 @@ static struct pci_driver gem_driver = {
.name = GEM_MODULE_NAME,
.id_table = gem_pci_tbl,
.probe = gem_init_one,
- .remove = __devexit_p(gem_remove_one),
+ .remove = gem_remove_one,
#ifdef CONFIG_PM
.suspend = gem_suspend,
.resume = gem_resume,
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index a23ed28a72b8..eb86b059809b 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -24,6 +24,7 @@
#include <linux/compiler.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/in.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/pci.h>
@@ -68,8 +69,8 @@
#define DRV_MODULE_NAME "tg3"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "3.45"
-#define DRV_MODULE_RELDATE "Dec 13, 2005"
+#define DRV_MODULE_VERSION "3.47"
+#define DRV_MODULE_RELDATE "Dec 28, 2005"
#define TG3_DEF_MAC_MODE 0
#define TG3_DEF_RX_MODE 0
@@ -341,6 +342,16 @@ static struct {
{ "interrupt test (offline)" },
};
+static void tg3_write32(struct tg3 *tp, u32 off, u32 val)
+{
+ writel(val, tp->regs + off);
+}
+
+static u32 tg3_read32(struct tg3 *tp, u32 off)
+{
+ return (readl(tp->regs + off));
+}
+
static void tg3_write_indirect_reg32(struct tg3 *tp, u32 off, u32 val)
{
unsigned long flags;
@@ -411,13 +422,29 @@ static u32 tg3_read_indirect_mbox(struct tg3 *tp, u32 off)
return val;
}
-static void _tw32_flush(struct tg3 *tp, u32 off, u32 val)
+/* usec_wait specifies the wait time in usec when writing to certain registers
+ * where it is unsafe to read back the register without some delay.
+ * GRC_LOCAL_CTRL is one example if the GPIOs are toggled to switch power.
+ * TG3PCI_CLOCK_CTRL is another example if the clock frequencies are changed.
+ */
+static void _tw32_flush(struct tg3 *tp, u32 off, u32 val, u32 usec_wait)
{
- tp->write32(tp, off, val);
- if (!(tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) &&
- !(tp->tg3_flags & TG3_FLAG_5701_REG_WRITE_BUG) &&
- !(tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND))
- tp->read32(tp, off); /* flush */
+ if ((tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) ||
+ (tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND))
+ /* Non-posted methods */
+ tp->write32(tp, off, val);
+ else {
+ /* Posted method */
+ tg3_write32(tp, off, val);
+ if (usec_wait)
+ udelay(usec_wait);
+ tp->read32(tp, off);
+ }
+ /* Wait again after the read for the posted method to guarantee that
+ * the wait time is met.
+ */
+ if (usec_wait)
+ udelay(usec_wait);
}
static inline void tw32_mailbox_flush(struct tg3 *tp, u32 off, u32 val)
@@ -438,16 +465,6 @@ static void tg3_write32_tx_mbox(struct tg3 *tp, u32 off, u32 val)
readl(mbox);
}
-static void tg3_write32(struct tg3 *tp, u32 off, u32 val)
-{
- writel(val, tp->regs + off);
-}
-
-static u32 tg3_read32(struct tg3 *tp, u32 off)
-{
- return (readl(tp->regs + off));
-}
-
#define tw32_mailbox(reg, val) tp->write32_mbox(tp, reg, val)
#define tw32_mailbox_f(reg, val) tw32_mailbox_flush(tp, (reg), (val))
#define tw32_rx_mbox(reg, val) tp->write32_rx_mbox(tp, reg, val)
@@ -455,7 +472,8 @@ static u32 tg3_read32(struct tg3 *tp, u32 off)
#define tr32_mailbox(reg) tp->read32_mbox(tp, reg)
#define tw32(reg,val) tp->write32(tp, reg, val)
-#define tw32_f(reg,val) _tw32_flush(tp,(reg),(val))
+#define tw32_f(reg,val) _tw32_flush(tp,(reg),(val), 0)
+#define tw32_wait_f(reg,val,us) _tw32_flush(tp,(reg),(val), (us))
#define tr32(reg) tp->read32(tp, reg)
static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val)
@@ -595,21 +613,19 @@ static void tg3_switch_clocks(struct tg3 *tp)
if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) {
if (orig_clock_ctrl & CLOCK_CTRL_625_CORE) {
- tw32_f(TG3PCI_CLOCK_CTRL,
- clock_ctrl | CLOCK_CTRL_625_CORE);
- udelay(40);
+ tw32_wait_f(TG3PCI_CLOCK_CTRL,
+ clock_ctrl | CLOCK_CTRL_625_CORE, 40);
}
} else if ((orig_clock_ctrl & CLOCK_CTRL_44MHZ_CORE) != 0) {
- tw32_f(TG3PCI_CLOCK_CTRL,
- clock_ctrl |
- (CLOCK_CTRL_44MHZ_CORE | CLOCK_CTRL_ALTCLK));
- udelay(40);
- tw32_f(TG3PCI_CLOCK_CTRL,
- clock_ctrl | (CLOCK_CTRL_ALTCLK));
- udelay(40);
+ tw32_wait_f(TG3PCI_CLOCK_CTRL,
+ clock_ctrl |
+ (CLOCK_CTRL_44MHZ_CORE | CLOCK_CTRL_ALTCLK),
+ 40);
+ tw32_wait_f(TG3PCI_CLOCK_CTRL,
+ clock_ctrl | (CLOCK_CTRL_ALTCLK),
+ 40);
}
- tw32_f(TG3PCI_CLOCK_CTRL, clock_ctrl);
- udelay(40);
+ tw32_wait_f(TG3PCI_CLOCK_CTRL, clock_ctrl, 40);
}
#define PHY_BUSY_LOOPS 5000
@@ -1017,39 +1033,50 @@ static void tg3_frob_aux_power(struct tg3 *tp)
if ((tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT) != 0)
return;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) {
- tp_peer = pci_get_drvdata(tp->pdev_peer);
- if (!tp_peer)
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714)) {
+ struct net_device *dev_peer;
+
+ dev_peer = pci_get_drvdata(tp->pdev_peer);
+ if (!dev_peer)
BUG();
+ tp_peer = netdev_priv(dev_peer);
}
-
if ((tp->tg3_flags & TG3_FLAG_WOL_ENABLE) != 0 ||
(tp->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0 ||
(tp_peer->tg3_flags & TG3_FLAG_WOL_ENABLE) != 0 ||
(tp_peer->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0) {
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) {
- tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- (GRC_LCLCTRL_GPIO_OE0 |
- GRC_LCLCTRL_GPIO_OE1 |
- GRC_LCLCTRL_GPIO_OE2 |
- GRC_LCLCTRL_GPIO_OUTPUT0 |
- GRC_LCLCTRL_GPIO_OUTPUT1));
- udelay(100);
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ (GRC_LCLCTRL_GPIO_OE0 |
+ GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OE2 |
+ GRC_LCLCTRL_GPIO_OUTPUT0 |
+ GRC_LCLCTRL_GPIO_OUTPUT1),
+ 100);
} else {
u32 no_gpio2;
- u32 grc_local_ctrl;
+ u32 grc_local_ctrl = 0;
if (tp_peer != tp &&
(tp_peer->tg3_flags & TG3_FLAG_INIT_COMPLETE) != 0)
return;
+ /* Workaround to prevent overdrawing Amps. */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) ==
+ ASIC_REV_5714) {
+ grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE3;
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ grc_local_ctrl, 100);
+ }
+
/* On 5753 and variants, GPIO2 cannot be used. */
no_gpio2 = tp->nic_sram_data_cfg &
NIC_SRAM_DATA_CFG_NO_GPIO2;
- grc_local_ctrl = GRC_LCLCTRL_GPIO_OE0 |
+ grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE0 |
GRC_LCLCTRL_GPIO_OE1 |
GRC_LCLCTRL_GPIO_OE2 |
GRC_LCLCTRL_GPIO_OUTPUT1 |
@@ -1058,21 +1085,18 @@ static void tg3_frob_aux_power(struct tg3 *tp)
grc_local_ctrl &= ~(GRC_LCLCTRL_GPIO_OE2 |
GRC_LCLCTRL_GPIO_OUTPUT2);
}
- tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- grc_local_ctrl);
- udelay(100);
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ grc_local_ctrl, 100);
grc_local_ctrl |= GRC_LCLCTRL_GPIO_OUTPUT0;
- tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- grc_local_ctrl);
- udelay(100);
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ grc_local_ctrl, 100);
if (!no_gpio2) {
grc_local_ctrl &= ~GRC_LCLCTRL_GPIO_OUTPUT2;
- tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- grc_local_ctrl);
- udelay(100);
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ grc_local_ctrl, 100);
}
}
} else {
@@ -1082,19 +1106,16 @@ static void tg3_frob_aux_power(struct tg3 *tp)
(tp_peer->tg3_flags & TG3_FLAG_INIT_COMPLETE) != 0)
return;
- tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- (GRC_LCLCTRL_GPIO_OE1 |
- GRC_LCLCTRL_GPIO_OUTPUT1));
- udelay(100);
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ (GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OUTPUT1), 100);
- tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- (GRC_LCLCTRL_GPIO_OE1));
- udelay(100);
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ GRC_LCLCTRL_GPIO_OE1, 100);
- tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
- (GRC_LCLCTRL_GPIO_OE1 |
- GRC_LCLCTRL_GPIO_OUTPUT1));
- udelay(100);
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl |
+ (GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OUTPUT1), 100);
}
}
}
@@ -1137,10 +1158,8 @@ static int tg3_set_power_state(struct tg3 *tp, int state)
udelay(100); /* Delay after power state change */
/* Switch out of Vaux if it is not a LOM */
- if (!(tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT)) {
- tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl);
- udelay(100);
- }
+ if (!(tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT))
+ tw32_wait_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl, 100);
return 0;
@@ -1239,10 +1258,8 @@ static int tg3_set_power_state(struct tg3 *tp, int state)
base_val |= (CLOCK_CTRL_RXCLK_DISABLE |
CLOCK_CTRL_TXCLK_DISABLE);
- tw32_f(TG3PCI_CLOCK_CTRL, base_val |
- CLOCK_CTRL_ALTCLK |
- CLOCK_CTRL_PWRDOWN_PLL133);
- udelay(40);
+ tw32_wait_f(TG3PCI_CLOCK_CTRL, base_val | CLOCK_CTRL_ALTCLK |
+ CLOCK_CTRL_PWRDOWN_PLL133, 40);
} else if (tp->tg3_flags2 & TG3_FLG2_5780_CLASS) {
/* do nothing */
} else if (!((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) &&
@@ -1263,11 +1280,11 @@ static int tg3_set_power_state(struct tg3 *tp, int state)
newbits2 = newbits1 | CLOCK_CTRL_44MHZ_CORE;
}
- tw32_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl | newbits1);
- udelay(40);
+ tw32_wait_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl | newbits1,
+ 40);
- tw32_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl | newbits2);
- udelay(40);
+ tw32_wait_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl | newbits2,
+ 40);
if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) {
u32 newbits3;
@@ -1281,9 +1298,8 @@ static int tg3_set_power_state(struct tg3 *tp, int state)
newbits3 = CLOCK_CTRL_44MHZ_CORE;
}
- tw32_f(TG3PCI_CLOCK_CTRL,
- tp->pci_clock_ctrl | newbits3);
- udelay(40);
+ tw32_wait_f(TG3PCI_CLOCK_CTRL,
+ tp->pci_clock_ctrl | newbits3, 40);
}
}
@@ -1294,7 +1310,8 @@ static int tg3_set_power_state(struct tg3 *tp, int state)
tg3_writephy(tp, MII_TG3_EXT_CTRL,
MII_TG3_EXT_CTRL_FORCE_LED_OFF);
tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x01b2);
- tg3_writephy(tp, MII_BMCR, BMCR_PDOWN);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700)
+ tg3_writephy(tp, MII_BMCR, BMCR_PDOWN);
}
}
@@ -3634,7 +3651,7 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
TXD_FLAG_CPU_POST_DMA);
skb->nh.iph->check = 0;
- skb->nh.iph->tot_len = ntohs(mss + ip_tcp_len + tcp_opt_len);
+ skb->nh.iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len);
if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) {
skb->h.th->check = 0;
base_flags &= ~TXD_FLAG_TCPUDP_CSUM;
@@ -7135,8 +7152,13 @@ do { p = (u32 *)(orig_p + (reg)); \
GET_REG32_LOOP(BUFMGR_MODE, 0x58);
GET_REG32_LOOP(RDMAC_MODE, 0x08);
GET_REG32_LOOP(WDMAC_MODE, 0x08);
- GET_REG32_LOOP(RX_CPU_BASE, 0x280);
- GET_REG32_LOOP(TX_CPU_BASE, 0x280);
+ GET_REG32_1(RX_CPU_MODE);
+ GET_REG32_1(RX_CPU_STATE);
+ GET_REG32_1(RX_CPU_PGMCTR);
+ GET_REG32_1(RX_CPU_HWBKPT);
+ GET_REG32_1(TX_CPU_MODE);
+ GET_REG32_1(TX_CPU_STATE);
+ GET_REG32_1(TX_CPU_PGMCTR);
GET_REG32_LOOP(GRCMBOX_INTERRUPT_0, 0x110);
GET_REG32_LOOP(FTQ_RESET, 0x120);
GET_REG32_LOOP(MSGINT_MODE, 0x0c);
@@ -7959,13 +7981,12 @@ static int tg3_test_memory(struct tg3 *tp)
u32 offset;
u32 len;
} mem_tbl_570x[] = {
- { 0x00000000, 0x01000},
+ { 0x00000000, 0x00b50},
{ 0x00002000, 0x1c000},
{ 0xffffffff, 0x00000}
}, mem_tbl_5705[] = {
{ 0x00000100, 0x0000c},
{ 0x00000200, 0x00008},
- { 0x00000b50, 0x00400},
{ 0x00004000, 0x00800},
{ 0x00006000, 0x01000},
{ 0x00008000, 0x02000},
@@ -10466,7 +10487,7 @@ static char * __devinit tg3_bus_string(struct tg3 *tp, char *str)
return str;
}
-static struct pci_dev * __devinit tg3_find_5704_peer(struct tg3 *tp)
+static struct pci_dev * __devinit tg3_find_peer(struct tg3 *tp)
{
struct pci_dev *peer;
unsigned int func, devnr = tp->pdev->devfn & ~7;
@@ -10719,8 +10740,9 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
tp->rx_pending = 63;
}
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704)
- tp->pdev_peer = tg3_find_5704_peer(tp);
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714))
+ tp->pdev_peer = tg3_find_peer(tp);
err = tg3_get_device_address(tp);
if (err) {
diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h
index 94dbcf3537ec..890e1635996b 100644
--- a/drivers/net/tg3.h
+++ b/drivers/net/tg3.h
@@ -1124,7 +1124,14 @@
/* 0x280 --> 0x400 unused */
#define RX_CPU_BASE 0x00005000
+#define RX_CPU_MODE 0x00005000
+#define RX_CPU_STATE 0x00005004
+#define RX_CPU_PGMCTR 0x0000501c
+#define RX_CPU_HWBKPT 0x00005034
#define TX_CPU_BASE 0x00005400
+#define TX_CPU_MODE 0x00005400
+#define TX_CPU_STATE 0x00005404
+#define TX_CPU_PGMCTR 0x0000541c
/* Mailboxes */
#define GRCMBOX_INTERRUPT_0 0x00005800 /* 64-bit */
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
index 5e7c7e944c9d..64f6d1f25753 100644
--- a/drivers/net/wireless/ipw2200.c
+++ b/drivers/net/wireless/ipw2200.c
@@ -7456,8 +7456,7 @@ static void ipw_handle_data_packet(struct ipw_priv *priv,
/* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
hdr = (struct ieee80211_hdr_4addr *)rxb->skb->data;
if (priv->ieee->iw_mode != IW_MODE_MONITOR &&
- ((is_multicast_ether_addr(hdr->addr1) ||
- is_broadcast_ether_addr(hdr->addr1)) ?
+ (is_multicast_ether_addr(hdr->addr1) ?
!priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt))
ipw_rebuild_decrypted_skb(priv, rxb->skb);
@@ -7648,8 +7647,7 @@ static inline int is_network_packet(struct ipw_priv *priv,
return 0;
/* {broad,multi}cast packets to our BSSID go through */
- if (is_multicast_ether_addr(header->addr1) ||
- is_broadcast_ether_addr(header->addr1))
+ if (is_multicast_ether_addr(header->addr1))
return !memcmp(header->addr3, priv->bssid, ETH_ALEN);
/* packets to our adapter go through */
@@ -7662,8 +7660,7 @@ static inline int is_network_packet(struct ipw_priv *priv,
return 0;
/* {broad,multi}cast packets to our BSS go through */
- if (is_multicast_ether_addr(header->addr1) ||
- is_broadcast_ether_addr(header->addr1))
+ if (is_multicast_ether_addr(header->addr1))
return !memcmp(header->addr2, priv->bssid, ETH_ALEN);
/* packets to our adapter go through */
@@ -9657,8 +9654,7 @@ static inline int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
switch (priv->ieee->iw_mode) {
case IW_MODE_ADHOC:
hdr_len = IEEE80211_3ADDR_LEN;
- unicast = !(is_multicast_ether_addr(hdr->addr1) ||
- is_broadcast_ether_addr(hdr->addr1));
+ unicast = !is_multicast_ether_addr(hdr->addr1);
id = ipw_find_station(priv, hdr->addr1);
if (id == IPW_INVALID_STATION) {
id = ipw_add_station(priv, hdr->addr1);
@@ -9673,8 +9669,7 @@ static inline int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
case IW_MODE_INFRA:
default:
- unicast = !(is_multicast_ether_addr(hdr->addr3) ||
- is_broadcast_ether_addr(hdr->addr3));
+ unicast = !is_multicast_ether_addr(hdr->addr3);
hdr_len = IEEE80211_3ADDR_LEN;
id = 0;
break;
diff --git a/drivers/net/wireless/orinoco_nortel.c b/drivers/net/wireless/orinoco_nortel.c
index d8afd51ff8a5..d1a670b35338 100644
--- a/drivers/net/wireless/orinoco_nortel.c
+++ b/drivers/net/wireless/orinoco_nortel.c
@@ -1,6 +1,8 @@
/* orinoco_nortel.c
*
* Driver for Prism II devices which would usually be driven by orinoco_cs,
+ * but are connected to the PCI bus by a PCI-to-PCMCIA adapter used in
+ * Nortel emobility, Symbol LA-4113 and Symbol LA-4123.
* but are connected to the PCI bus by a Nortel PCI-PCMCIA-Adapter.
*
* Copyright (C) 2002 Tobias Hoffmann
@@ -165,7 +167,7 @@ static int nortel_pci_init_one(struct pci_dev *pdev,
goto fail_resources;
}
- iomem = pci_iomap(pdev, 3, 0);
+ iomem = pci_iomap(pdev, 2, 0);
if (!iomem) {
err = -ENOMEM;
goto fail_map_io;
@@ -265,6 +267,8 @@ static void __devexit nortel_pci_remove_one(struct pci_dev *pdev)
static struct pci_device_id nortel_pci_id_table[] = {
/* Nortel emobility PCI */
{0x126c, 0x8030, PCI_ANY_ID, PCI_ANY_ID,},
+ /* Symbol LA-4123 PCI */
+ {0x1562, 0x0001, PCI_ANY_ID, PCI_ANY_ID,},
{0,},
};
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 716df015f8d0..6707df968934 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -6,6 +6,9 @@ obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o
obj-$(CONFIG_PROC_FS) += proc.o
+# Build PCI Express stuff if needed
+obj-$(CONFIG_PCIEPORTBUS) += pcie/
+
obj-$(CONFIG_HOTPLUG) += hotplug.o
# Build the PCI Hotplug drivers if we were asked to
@@ -40,7 +43,3 @@ endif
ifeq ($(CONFIG_PCI_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
-
-# Build PCI Express stuff if needed
-obj-$(CONFIG_PCIEPORTBUS) += pcie/
-
diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c
index 011915d5e243..f94f1f25eec6 100644
--- a/drivers/s390/net/qeth_eddp.c
+++ b/drivers/s390/net/qeth_eddp.c
@@ -62,7 +62,8 @@ qeth_eddp_free_context(struct qeth_eddp_context *ctx)
for (i = 0; i < ctx->num_pages; ++i)
free_page((unsigned long)ctx->pages[i]);
kfree(ctx->pages);
- kfree(ctx->elements);
+ if (ctx->elements != NULL)
+ kfree(ctx->elements);
kfree(ctx);
}
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c
index 99cceb242ec4..f8f55cc468ba 100644
--- a/drivers/s390/net/qeth_main.c
+++ b/drivers/s390/net/qeth_main.c
@@ -1,6 +1,6 @@
/*
*
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.242 $)
+ * linux/drivers/s390/net/qeth_main.c ($Revision: 1.251 $)
*
* Linux on zSeries OSA Express and HiperSockets support
*
@@ -12,7 +12,7 @@
* Frank Pavlic (fpavlic@de.ibm.com) and
* Thomas Spatzier <tspat@de.ibm.com>
*
- * $Revision: 1.242 $ $Date: 2005/05/04 20:19:18 $
+ * $Revision: 1.251 $ $Date: 2005/05/04 20:19:18 $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -72,7 +72,7 @@
#include "qeth_eddp.h"
#include "qeth_tso.h"
-#define VERSION_QETH_C "$Revision: 1.242 $"
+#define VERSION_QETH_C "$Revision: 1.251 $"
static const char *version = "qeth S/390 OSA-Express driver";
/**
@@ -518,7 +518,8 @@ __qeth_set_offline(struct ccwgroup_device *cgdev, int recovery_mode)
QETH_DBF_TEXT(setup, 3, "setoffl");
QETH_DBF_HEX(setup, 3, &card, sizeof(void *));
-
+
+ netif_carrier_off(card->dev);
recover_flag = card->state;
if (qeth_stop_card(card, recovery_mode) == -ERESTARTSYS){
PRINT_WARN("Stopping card %s interrupted by user!\n",
@@ -1020,7 +1021,6 @@ void
qeth_schedule_recovery(struct qeth_card *card)
{
QETH_DBF_TEXT(trace,2,"startrec");
-
if (qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD) == 0)
schedule_work(&card->kernel_thread_starter);
}
@@ -1710,7 +1710,6 @@ qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
"IP address reset.\n",
QETH_CARD_IFNAME(card),
card->info.chpid);
- netif_carrier_on(card->dev);
qeth_schedule_recovery(card);
return NULL;
case IPA_CMD_MODCCID:
@@ -1959,7 +1958,7 @@ qeth_osn_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
{
u16 s1, s2;
-QETH_DBF_TEXT(trace,4,"osndipa");
+ QETH_DBF_TEXT(trace,4,"osndipa");
qeth_prepare_ipa_cmd(card, iob, QETH_PROT_OSN2);
s1 = (u16)(IPA_PDU_HEADER_SIZE + data_len);
@@ -2203,24 +2202,21 @@ qeth_ulp_setup(struct qeth_card *card)
}
static inline int
-qeth_check_for_inbound_error(struct qeth_qdio_buffer *buf,
- unsigned int qdio_error,
- unsigned int siga_error)
+qeth_check_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,
+ unsigned int siga_error, const char *dbftext)
{
- int rc = 0;
-
if (qdio_error || siga_error) {
- QETH_DBF_TEXT(trace, 2, "qdinerr");
- QETH_DBF_TEXT(qerr, 2, "qdinerr");
+ QETH_DBF_TEXT(trace, 2, dbftext);
+ QETH_DBF_TEXT(qerr, 2, dbftext);
QETH_DBF_TEXT_(qerr, 2, " F15=%02X",
- buf->buffer->element[15].flags & 0xff);
+ buf->element[15].flags & 0xff);
QETH_DBF_TEXT_(qerr, 2, " F14=%02X",
- buf->buffer->element[14].flags & 0xff);
+ buf->element[14].flags & 0xff);
QETH_DBF_TEXT_(qerr, 2, " qerr=%X", qdio_error);
QETH_DBF_TEXT_(qerr, 2, " serr=%X", siga_error);
- rc = 1;
+ return 1;
}
- return rc;
+ return 0;
}
static inline struct sk_buff *
@@ -2769,8 +2765,9 @@ qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status,
for (i = first_element; i < (first_element + count); ++i) {
index = i % QDIO_MAX_BUFFERS_PER_Q;
buffer = &card->qdio.in_q->bufs[index];
- if (!((status == QDIO_STATUS_LOOK_FOR_ERROR) &&
- qeth_check_for_inbound_error(buffer, qdio_err, siga_err)))
+ if (!((status & QDIO_STATUS_LOOK_FOR_ERROR) &&
+ qeth_check_qdio_errors(buffer->buffer,
+ qdio_err, siga_err,"qinerr")))
qeth_process_inbound_buffer(card, buffer, index);
/* clear buffer and give back to hardware */
qeth_put_buffer_pool_entry(card, buffer->pool_entry);
@@ -2785,12 +2782,13 @@ qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status,
static inline int
qeth_handle_send_error(struct qeth_card *card,
struct qeth_qdio_out_buffer *buffer,
- int qdio_err, int siga_err)
+ unsigned int qdio_err, unsigned int siga_err)
{
int sbalf15 = buffer->buffer->element[15].flags & 0xff;
int cc = siga_err & 3;
QETH_DBF_TEXT(trace, 6, "hdsnderr");
+ qeth_check_qdio_errors(buffer->buffer, qdio_err, siga_err, "qouterr");
switch (cc) {
case 0:
if (qdio_err){
@@ -3047,7 +3045,8 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
for(i = first_element; i < (first_element + count); ++i){
buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
/*we only handle the KICK_IT error by doing a recovery */
- if (qeth_handle_send_error(card, buffer, qdio_error, siga_error)
+ if (qeth_handle_send_error(card, buffer,
+ qdio_error, siga_error)
== QETH_SEND_ERROR_KICK_IT){
netif_stop_queue(card->dev);
qeth_schedule_recovery(card);
@@ -3289,7 +3288,6 @@ qeth_init_qdio_info(struct qeth_card *card)
card->qdio.in_buf_pool.buf_count = card->qdio.init_pool.buf_count;
INIT_LIST_HEAD(&card->qdio.in_buf_pool.entry_list);
INIT_LIST_HEAD(&card->qdio.init_pool.entry_list);
- /* outbound */
}
static int
@@ -3731,6 +3729,9 @@ qeth_verify_vlan_dev(struct net_device *dev, struct qeth_card *card)
break;
}
}
+ if (rc && !(VLAN_DEV_INFO(dev)->real_dev->priv == (void *)card))
+ return 0;
+
#endif
return rc;
}
@@ -3807,10 +3808,8 @@ qeth_open(struct net_device *dev)
card->data.state = CH_STATE_UP;
card->state = CARD_STATE_UP;
- if (!card->lan_online){
- if (netif_carrier_ok(dev))
- netif_carrier_off(dev);
- }
+ if (!card->lan_online && netif_carrier_ok(dev))
+ netif_carrier_off(dev);
return 0;
}
@@ -5870,10 +5869,8 @@ qeth_add_multicast_ipv6(struct qeth_card *card)
struct inet6_dev *in6_dev;
QETH_DBF_TEXT(trace,4,"chkmcv6");
- if ((card->options.layer2 == 0) &&
- (!qeth_is_supported(card, IPA_IPV6)) )
+ if (!qeth_is_supported(card, IPA_IPV6))
return ;
-
in6_dev = in6_dev_get(card->dev);
if (in6_dev == NULL)
return;
@@ -7936,8 +7933,8 @@ __qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode)
QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
goto out_remove;
}
-/*maybe it was set offline without ifconfig down
- * we can also use this state for recovery purposes*/
+ netif_carrier_on(card->dev);
+
qeth_set_allowed_threads(card, 0xffffffff, 0);
if (recover_flag == CARD_STATE_RECOVER)
qeth_start_again(card, recovery_mode);
diff --git a/drivers/s390/net/qeth_mpc.c b/drivers/s390/net/qeth_mpc.c
index f0a080a9e515..5f8754addc14 100644
--- a/drivers/s390/net/qeth_mpc.c
+++ b/drivers/s390/net/qeth_mpc.c
@@ -11,7 +11,7 @@
#include <asm/cio.h>
#include "qeth_mpc.h"
-const char *VERSION_QETH_MPC_C = "$Revision: 1.12 $";
+const char *VERSION_QETH_MPC_C = "$Revision: 1.13 $";
unsigned char IDX_ACTIVATE_READ[]={
0x00,0x00,0x80,0x00, 0x00,0x00,0x00,0x00,
diff --git a/drivers/s390/net/qeth_mpc.h b/drivers/s390/net/qeth_mpc.h
index 5f71486e708c..864cec5f6c62 100644
--- a/drivers/s390/net/qeth_mpc.h
+++ b/drivers/s390/net/qeth_mpc.h
@@ -14,14 +14,14 @@
#include <asm/qeth.h>
-#define VERSION_QETH_MPC_H "$Revision: 1.44 $"
+#define VERSION_QETH_MPC_H "$Revision: 1.46 $"
extern const char *VERSION_QETH_MPC_C;
#define IPA_PDU_HEADER_SIZE 0x40
#define QETH_IPA_PDU_LEN_TOTAL(buffer) (buffer+0x0e)
#define QETH_IPA_PDU_LEN_PDU1(buffer) (buffer+0x26)
-#define QETH_IPA_PDU_LEN_PDU2(buffer) (buffer+0x2a)
+#define QETH_IPA_PDU_LEN_PDU2(buffer) (buffer+0x29)
#define QETH_IPA_PDU_LEN_PDU3(buffer) (buffer+0x3a)
extern unsigned char IPA_PDU_HEADER[];
diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c
index f2ccfea8fdb8..7bf35098831e 100644
--- a/drivers/s390/net/qeth_proc.c
+++ b/drivers/s390/net/qeth_proc.c
@@ -1,6 +1,6 @@
/*
*
- * linux/drivers/s390/net/qeth_fs.c ($Revision: 1.13 $)
+ * linux/drivers/s390/net/qeth_fs.c ($Revision: 1.16 $)
*
* Linux on zSeries OSA Express and HiperSockets support
* This file contains code related to procfs.
@@ -21,7 +21,7 @@
#include "qeth_mpc.h"
#include "qeth_fs.h"
-const char *VERSION_QETH_PROC_C = "$Revision: 1.13 $";
+const char *VERSION_QETH_PROC_C = "$Revision: 1.16 $";
/***** /proc/qeth *****/
#define QETH_PROCFILE_NAME "qeth"
@@ -30,30 +30,26 @@ static struct proc_dir_entry *qeth_procfile;
static int
qeth_procfile_seq_match(struct device *dev, void *data)
{
- return 1;
+ return(dev ? 1 : 0);
}
static void *
qeth_procfile_seq_start(struct seq_file *s, loff_t *offset)
{
- struct device *dev;
- loff_t nr;
-
+ struct device *dev = NULL;
+ loff_t nr = 0;
+
down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
-
- nr = *offset;
- if (nr == 0)
+ if (*offset == 0)
return SEQ_START_TOKEN;
-
- dev = driver_find_device(&qeth_ccwgroup_driver.driver, NULL,
- NULL, qeth_procfile_seq_match);
-
- /* get card at pos *offset */
- nr = *offset;
- while (nr-- > 1 && dev)
+ while (1) {
dev = driver_find_device(&qeth_ccwgroup_driver.driver, dev,
NULL, qeth_procfile_seq_match);
- return (void *) dev;
+ if (++nr == *offset)
+ break;
+ put_device(dev);
+ }
+ return dev;
}
static void
@@ -66,19 +62,14 @@ static void *
qeth_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset)
{
struct device *prev, *next;
-
- if (it == SEQ_START_TOKEN) {
- next = driver_find_device(&qeth_ccwgroup_driver.driver,
- NULL, NULL, qeth_procfile_seq_match);
- if (next)
- (*offset)++;
- return (void *) next;
- }
- prev = (struct device *) it;
+
+ if (it == SEQ_START_TOKEN)
+ prev = NULL;
+ else
+ prev = (struct device *) it;
next = driver_find_device(&qeth_ccwgroup_driver.driver,
prev, NULL, qeth_procfile_seq_match);
- if (next)
- (*offset)++;
+ (*offset)++;
return (void *) next;
}
@@ -87,7 +78,7 @@ qeth_get_router_str(struct qeth_card *card, int ipv)
{
int routing_type = 0;
- if (ipv == 4){
+ if (ipv == 4) {
routing_type = card->options.route4.type;
} else {
#ifdef CONFIG_QETH_IPV6
@@ -154,6 +145,7 @@ qeth_procfile_seq_show(struct seq_file *s, void *it)
card->qdio.in_buf_pool.buf_count);
else
seq_printf(s, " +++ LAN OFFLINE +++\n");
+ put_device(device);
}
return 0;
}
@@ -184,51 +176,16 @@ static struct file_operations qeth_procfile_fops = {
static struct proc_dir_entry *qeth_perf_procfile;
#ifdef CONFIG_QETH_PERF_STATS
-
-static void *
-qeth_perf_procfile_seq_start(struct seq_file *s, loff_t *offset)
-{
- struct device *dev = NULL;
- int nr;
-
- down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
- /* get card at pos *offset */
- dev = driver_find_device(&qeth_ccwgroup_driver.driver, NULL, NULL,
- qeth_procfile_seq_match);
-
- /* get card at pos *offset */
- nr = *offset;
- while (nr-- > 1 && dev)
- dev = driver_find_device(&qeth_ccwgroup_driver.driver, dev,
- NULL, qeth_procfile_seq_match);
- return (void *) dev;
-}
-
-static void
-qeth_perf_procfile_seq_stop(struct seq_file *s, void* it)
-{
- up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
-}
-
-static void *
-qeth_perf_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset)
-{
- struct device *prev, *next;
-
- prev = (struct device *) it;
- next = driver_find_device(&qeth_ccwgroup_driver.driver, prev,
- NULL, qeth_procfile_seq_match);
- if (next)
- (*offset)++;
- return (void *) next;
-}
-
static int
qeth_perf_procfile_seq_show(struct seq_file *s, void *it)
{
struct device *device;
struct qeth_card *card;
+
+ if (it == SEQ_START_TOKEN)
+ return 0;
+
device = (struct device *) it;
card = device->driver_data;
seq_printf(s, "For card with devnos %s/%s/%s (%s):\n",
@@ -295,13 +252,14 @@ qeth_perf_procfile_seq_show(struct seq_file *s, void *it)
card->perf_stats.outbound_do_qdio_time,
card->perf_stats.outbound_do_qdio_cnt
);
+ put_device(device);
return 0;
}
static struct seq_operations qeth_perf_procfile_seq_ops = {
- .start = qeth_perf_procfile_seq_start,
- .stop = qeth_perf_procfile_seq_stop,
- .next = qeth_perf_procfile_seq_next,
+ .start = qeth_procfile_seq_start,
+ .stop = qeth_procfile_seq_stop,
+ .next = qeth_procfile_seq_next,
.show = qeth_perf_procfile_seq_show,
};
@@ -324,93 +282,6 @@ static struct file_operations qeth_perf_procfile_fops = {
#define qeth_perf_procfile_created 1
#endif /* CONFIG_QETH_PERF_STATS */
-/***** /proc/qeth_ipa_takeover *****/
-#define QETH_IPATO_PROCFILE_NAME "qeth_ipa_takeover"
-static struct proc_dir_entry *qeth_ipato_procfile;
-
-static void *
-qeth_ipato_procfile_seq_start(struct seq_file *s, loff_t *offset)
-{
- struct device *dev;
- loff_t nr;
-
- down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
- /* TODO: finish this */
- /*
- * maybe SEQ_SATRT_TOKEN can be returned for offset 0
- * output driver settings then;
- * else output setting for respective card
- */
-
- dev = driver_find_device(&qeth_ccwgroup_driver.driver, NULL, NULL,
- qeth_procfile_seq_match);
-
- /* get card at pos *offset */
- nr = *offset;
- while (nr-- > 1 && dev)
- dev = driver_find_device(&qeth_ccwgroup_driver.driver, dev,
- NULL, qeth_procfile_seq_match);
- return (void *) dev;
-}
-
-static void
-qeth_ipato_procfile_seq_stop(struct seq_file *s, void* it)
-{
- up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
-}
-
-static void *
-qeth_ipato_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset)
-{
- struct device *prev, *next;
-
- prev = (struct device *) it;
- next = driver_find_device(&qeth_ccwgroup_driver.driver, prev,
- NULL, qeth_procfile_seq_match);
- if (next)
- (*offset)++;
- return (void *) next;
-}
-
-static int
-qeth_ipato_procfile_seq_show(struct seq_file *s, void *it)
-{
- struct device *device;
- struct qeth_card *card;
-
- /* TODO: finish this */
- /*
- * maybe SEQ_SATRT_TOKEN can be returned for offset 0
- * output driver settings then;
- * else output setting for respective card
- */
- device = (struct device *) it;
- card = device->driver_data;
-
- return 0;
-}
-
-static struct seq_operations qeth_ipato_procfile_seq_ops = {
- .start = qeth_ipato_procfile_seq_start,
- .stop = qeth_ipato_procfile_seq_stop,
- .next = qeth_ipato_procfile_seq_next,
- .show = qeth_ipato_procfile_seq_show,
-};
-
-static int
-qeth_ipato_procfile_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &qeth_ipato_procfile_seq_ops);
-}
-
-static struct file_operations qeth_ipato_procfile_fops = {
- .owner = THIS_MODULE,
- .open = qeth_ipato_procfile_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-
int __init
qeth_create_procfs_entries(void)
{
@@ -426,13 +297,7 @@ qeth_create_procfs_entries(void)
qeth_perf_procfile->proc_fops = &qeth_perf_procfile_fops;
#endif /* CONFIG_QETH_PERF_STATS */
- qeth_ipato_procfile = create_proc_entry(QETH_IPATO_PROCFILE_NAME,
- S_IFREG | 0444, NULL);
- if (qeth_ipato_procfile)
- qeth_ipato_procfile->proc_fops = &qeth_ipato_procfile_fops;
-
if (qeth_procfile &&
- qeth_ipato_procfile &&
qeth_perf_procfile_created)
return 0;
else
@@ -446,62 +311,5 @@ qeth_remove_procfs_entries(void)
remove_proc_entry(QETH_PROCFILE_NAME, NULL);
if (qeth_perf_procfile)
remove_proc_entry(QETH_PERF_PROCFILE_NAME, NULL);
- if (qeth_ipato_procfile)
- remove_proc_entry(QETH_IPATO_PROCFILE_NAME, NULL);
}
-
-/* ONLY FOR DEVELOPMENT! -> make it as module */
-/*
-static void
-qeth_create_sysfs_entries(void)
-{
- struct device *dev;
-
- down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
-
- list_for_each_entry(dev, &qeth_ccwgroup_driver.driver.devices,
- driver_list)
- qeth_create_device_attributes(dev);
-
- up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
-}
-
-static void
-qeth_remove_sysfs_entries(void)
-{
- struct device *dev;
-
- down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
-
- list_for_each_entry(dev, &qeth_ccwgroup_driver.driver.devices,
- driver_list)
- qeth_remove_device_attributes(dev);
-
- up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
-}
-
-static int __init
-qeth_fs_init(void)
-{
- printk(KERN_INFO "qeth_fs_init\n");
- qeth_create_procfs_entries();
- qeth_create_sysfs_entries();
-
- return 0;
-}
-
-static void __exit
-qeth_fs_exit(void)
-{
- printk(KERN_INFO "qeth_fs_exit\n");
- qeth_remove_procfs_entries();
- qeth_remove_sysfs_entries();
-}
-
-
-module_init(qeth_fs_init);
-module_exit(qeth_fs_exit);
-
-MODULE_LICENSE("GPL");
-*/
diff --git a/drivers/s390/net/qeth_sys.c b/drivers/s390/net/qeth_sys.c
index ddd6019ba092..0ea185f70f75 100644
--- a/drivers/s390/net/qeth_sys.c
+++ b/drivers/s390/net/qeth_sys.c
@@ -1,6 +1,6 @@
/*
*
- * linux/drivers/s390/net/qeth_sys.c ($Revision: 1.58 $)
+ * linux/drivers/s390/net/qeth_sys.c ($Revision: 1.60 $)
*
* Linux on zSeries OSA Express and HiperSockets support
* This file contains code related to sysfs.
@@ -20,7 +20,7 @@
#include "qeth_mpc.h"
#include "qeth_fs.h"
-const char *VERSION_QETH_SYS_C = "$Revision: 1.58 $";
+const char *VERSION_QETH_SYS_C = "$Revision: 1.60 $";
/*****************************************************************************/
/* */
@@ -160,7 +160,7 @@ qeth_dev_portname_store(struct device *dev, struct device_attribute *attr, const
return -EPERM;
tmp = strsep((char **) &buf, "\n");
- if ((strlen(tmp) > 8) || (strlen(tmp) < 2))
+ if ((strlen(tmp) > 8) || (strlen(tmp) == 0))
return -EINVAL;
card->info.portname[0] = strlen(tmp);
diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h
index e245af3c4cbd..3c50b6f24f51 100644
--- a/drivers/s390/net/qeth_tso.h
+++ b/drivers/s390/net/qeth_tso.h
@@ -1,5 +1,5 @@
/*
- * linux/drivers/s390/net/qeth_tso.h ($Revision: 1.7 $)
+ * linux/drivers/s390/net/qeth_tso.h ($Revision: 1.8 $)
*
* Header file for qeth TCP Segmentation Offload support.
*
@@ -7,7 +7,7 @@
*
* Author(s): Frank Pavlic <fpavlic@de.ibm.com>
*
- * $Revision: 1.7 $ $Date: 2005/05/04 20:19:18 $
+ * $Revision: 1.8 $ $Date: 2005/05/04 20:19:18 $
*
*/
#ifndef __QETH_TSO_H__
diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c
index 418fc7b896ac..6252b9ddc01e 100644
--- a/drivers/scsi/dpt_i2o.c
+++ b/drivers/scsi/dpt_i2o.c
@@ -660,7 +660,12 @@ static int adpt_abort(struct scsi_cmnd * cmd)
msg[2] = 0;
msg[3]= 0;
msg[4] = (u32)cmd;
- if( (rcode = adpt_i2o_post_wait(pHba, msg, sizeof(msg), FOREVER)) != 0){
+ if (pHba->host)
+ spin_lock_irq(pHba->host->host_lock);
+ rcode = adpt_i2o_post_wait(pHba, msg, sizeof(msg), FOREVER);
+ if (pHba->host)
+ spin_unlock_irq(pHba->host->host_lock);
+ if (rcode != 0) {
if(rcode == -EOPNOTSUPP ){
printk(KERN_INFO"%s: Abort cmd not supported\n",pHba->name);
return FAILED;
@@ -697,10 +702,15 @@ static int adpt_device_reset(struct scsi_cmnd* cmd)
msg[2] = 0;
msg[3] = 0;
+ if (pHba->host)
+ spin_lock_irq(pHba->host->host_lock);
old_state = d->state;
d->state |= DPTI_DEV_RESET;
- if( (rcode = adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER)) ){
- d->state = old_state;
+ rcode = adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER);
+ d->state = old_state;
+ if (pHba->host)
+ spin_unlock_irq(pHba->host->host_lock);
+ if (rcode != 0) {
if(rcode == -EOPNOTSUPP ){
printk(KERN_INFO"%s: Device reset not supported\n",pHba->name);
return FAILED;
@@ -708,7 +718,6 @@ static int adpt_device_reset(struct scsi_cmnd* cmd)
printk(KERN_INFO"%s: Device reset failed\n",pHba->name);
return FAILED;
} else {
- d->state = old_state;
printk(KERN_INFO"%s: Device reset successful\n",pHba->name);
return SUCCESS;
}
@@ -721,6 +730,7 @@ static int adpt_bus_reset(struct scsi_cmnd* cmd)
{
adpt_hba* pHba;
u32 msg[4];
+ u32 rcode;
pHba = (adpt_hba*)cmd->device->host->hostdata[0];
memset(msg, 0, sizeof(msg));
@@ -729,7 +739,12 @@ static int adpt_bus_reset(struct scsi_cmnd* cmd)
msg[1] = (I2O_HBA_BUS_RESET<<24|HOST_TID<<12|pHba->channel[cmd->device->channel].tid);
msg[2] = 0;
msg[3] = 0;
- if(adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER) ){
+ if (pHba->host)
+ spin_lock_irq(pHba->host->host_lock);
+ rcode = adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER);
+ if (pHba->host)
+ spin_unlock_irq(pHba->host->host_lock);
+ if (rcode != 0) {
printk(KERN_WARNING"%s: Bus reset failed.\n",pHba->name);
return FAILED;
} else {
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
index 72ddba98f8fb..2282c04fee46 100644
--- a/drivers/scsi/libata-scsi.c
+++ b/drivers/scsi/libata-scsi.c
@@ -2044,7 +2044,7 @@ static int atapi_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask)
else {
u8 *scsicmd = cmd->cmnd;
- if (scsicmd[0] == INQUIRY) {
+ if ((scsicmd[0] == INQUIRY) && ((scsicmd[1] & 0x03) == 0)) {
u8 *buf = NULL;
unsigned int buflen;
@@ -2058,9 +2058,6 @@ static int atapi_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask)
* device. 2) Ensure response data format / ATAPI information
* are always correct.
*/
- /* FIXME: do we ever override EVPD pages and the like, with
- * this code?
- */
if (buf[2] == 0) {
buf[2] = 0x5;
buf[3] = 0x32;
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 950b087e4ca2..05ebb9cef961 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -400,6 +400,36 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
return found_target;
}
+struct work_queue_wrapper {
+ struct work_struct work;
+ struct scsi_target *starget;
+};
+
+static void scsi_target_reap_work(void *data) {
+ struct work_queue_wrapper *wqw = (struct work_queue_wrapper *)data;
+ struct scsi_target *starget = wqw->starget;
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ unsigned long flags;
+
+ kfree(wqw);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+
+ if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
+ list_del_init(&starget->siblings);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ transport_remove_device(&starget->dev);
+ device_del(&starget->dev);
+ transport_destroy_device(&starget->dev);
+ put_device(&starget->dev);
+ return;
+
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ return;
+}
+
/**
* scsi_target_reap - check to see if target is in use and destroy if not
*
@@ -411,19 +441,18 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
*/
void scsi_target_reap(struct scsi_target *starget)
{
- struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
- unsigned long flags;
- spin_lock_irqsave(shost->host_lock, flags);
+ struct work_queue_wrapper *wqw =
+ kzalloc(sizeof(struct work_queue_wrapper), GFP_ATOMIC);
- if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
- list_del_init(&starget->siblings);
- spin_unlock_irqrestore(shost->host_lock, flags);
- device_del(&starget->dev);
- transport_unregister_device(&starget->dev);
- put_device(&starget->dev);
+ if (!wqw) {
+ starget_printk(KERN_ERR, starget,
+ "Failed to allocate memory in scsi_reap_target()\n");
return;
}
- spin_unlock_irqrestore(shost->host_lock, flags);
+
+ INIT_WORK(&wqw->work, scsi_target_reap_work, wqw);
+ wqw->starget = starget;
+ schedule_work(&wqw->work);
}
/**
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index cd95d2ae7b77..685b997306cf 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -105,6 +105,7 @@ static struct {
{ FC_PORTSTATE_LINKDOWN, "Linkdown" },
{ FC_PORTSTATE_ERROR, "Error" },
{ FC_PORTSTATE_LOOPBACK, "Loopback" },
+ { FC_PORTSTATE_DELETED, "Deleted" },
};
fc_enum_name_search(port_state, fc_port_state, fc_port_state_names)
#define FC_PORTSTATE_MAX_NAMELEN 20
@@ -211,6 +212,7 @@ fc_bitfield_name_search(remote_port_roles, fc_remote_port_role_names)
#define FC_MGMTSRVR_PORTID 0x00000a
+static void fc_shost_remove_rports(void *data);
static void fc_timeout_deleted_rport(void *data);
static void fc_scsi_scan_rport(void *data);
static void fc_rport_terminate(struct fc_rport *rport);
@@ -318,6 +320,8 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev,
fc_host_next_rport_number(shost) = 0;
fc_host_next_target_id(shost) = 0;
+ fc_host_flags(shost) = 0;
+ INIT_WORK(&fc_host_rport_del_work(shost), fc_shost_remove_rports, shost);
return 0;
}
@@ -387,6 +391,7 @@ show_fc_rport_##field (struct class_device *cdev, char *buf) \
struct fc_internal *i = to_fc_internal(shost->transportt); \
if ((i->f->get_rport_##field) && \
!((rport->port_state == FC_PORTSTATE_BLOCKED) || \
+ (rport->port_state == FC_PORTSTATE_DELETED) || \
(rport->port_state == FC_PORTSTATE_NOTPRESENT))) \
i->f->get_rport_##field(rport); \
return snprintf(buf, sz, format_string, cast rport->field); \
@@ -402,6 +407,7 @@ store_fc_rport_##field(struct class_device *cdev, const char *buf, \
struct Scsi_Host *shost = rport_to_shost(rport); \
struct fc_internal *i = to_fc_internal(shost->transportt); \
if ((rport->port_state == FC_PORTSTATE_BLOCKED) || \
+ (rport->port_state == FC_PORTSTATE_DELETED) || \
(rport->port_state == FC_PORTSTATE_NOTPRESENT)) \
return -EBUSY; \
val = simple_strtoul(buf, NULL, 0); \
@@ -519,6 +525,7 @@ store_fc_rport_dev_loss_tmo(struct class_device *cdev, const char *buf,
struct Scsi_Host *shost = rport_to_shost(rport);
struct fc_internal *i = to_fc_internal(shost->transportt);
if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
+ (rport->port_state == FC_PORTSTATE_DELETED) ||
(rport->port_state == FC_PORTSTATE_NOTPRESENT))
return -EBUSY;
val = simple_strtoul(buf, NULL, 0);
@@ -1769,7 +1776,7 @@ fc_timeout_deleted_rport(void *data)
rport->maxframe_size = -1;
rport->supported_classes = FC_COS_UNSPECIFIED;
rport->roles = FC_RPORT_ROLE_UNKNOWN;
- rport->port_state = FC_PORTSTATE_NOTPRESENT;
+ rport->port_state = FC_PORTSTATE_DELETED;
/* remove the identifiers that aren't used in the consisting binding */
switch (fc_host_tgtid_bind_type(shost)) {
@@ -1789,14 +1796,23 @@ fc_timeout_deleted_rport(void *data)
break;
}
- spin_unlock_irqrestore(shost->host_lock, flags);
-
/*
* As this only occurs if the remote port (scsi target)
* went away and didn't come back - we'll remove
* all attached scsi devices.
+ *
+ * We'll schedule the shost work item to perform the actual removal
+ * to avoid recursion in the different flush calls if we perform
+ * the removal in each target - and there are lots of targets
+ * whose timeouts fire at the same time.
*/
- fc_rport_tgt_remove(rport);
+
+ if ( !(fc_host_flags(shost) & FC_SHOST_RPORT_DEL_SCHEDULED)) {
+ fc_host_flags(shost) |= FC_SHOST_RPORT_DEL_SCHEDULED;
+ scsi_queue_work(shost, &fc_host_rport_del_work(shost));
+ }
+
+ spin_unlock_irqrestore(shost->host_lock, flags);
}
/**
@@ -1818,6 +1834,41 @@ fc_scsi_scan_rport(void *data)
}
+/**
+ * fc_shost_remove_rports - called to remove all rports that are marked
+ * as in a deleted (not connected) state.
+ *
+ * @data: shost whose rports are to be looked at
+ **/
+static void
+fc_shost_remove_rports(void *data)
+{
+ struct Scsi_Host *shost = (struct Scsi_Host *)data;
+ struct fc_rport *rport, *next_rport;
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ while (fc_host_flags(shost) & FC_SHOST_RPORT_DEL_SCHEDULED) {
+
+ fc_host_flags(shost) &= ~FC_SHOST_RPORT_DEL_SCHEDULED;
+
+restart_search:
+ list_for_each_entry_safe(rport, next_rport,
+ &fc_host_rport_bindings(shost), peers) {
+ if (rport->port_state == FC_PORTSTATE_DELETED) {
+ rport->port_state = FC_PORTSTATE_NOTPRESENT;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ fc_rport_tgt_remove(rport);
+ spin_lock_irqsave(shost->host_lock, flags);
+ goto restart_search;
+ }
+ }
+
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
+
MODULE_AUTHOR("Martin Hicks");
MODULE_DESCRIPTION("FC Transport Attributes");
MODULE_LICENSE("GPL");
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index ad47c1b84c3f..812bae62c8ec 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -10,7 +10,7 @@ menu "Serial drivers"
# The new 8250/16550 serial drivers
config SERIAL_8250
tristate "8250/16550 and compatible serial support"
- depends on (BROKEN || !(SPARC64 || SPARC32))
+ depends on (BROKEN || !SPARC)
select SERIAL_CORE
---help---
This selects whether you want to include the driver for the standard
@@ -469,14 +469,14 @@ config SERIAL_IMX_CONSOLE
config SERIAL_SUNCORE
bool
- depends on SPARC32 || SPARC64
+ depends on SPARC
select SERIAL_CORE
select SERIAL_CORE_CONSOLE
default y
config SERIAL_SUNZILOG
tristate "Sun Zilog8530 serial support"
- depends on SPARC32 || SPARC64
+ depends on SPARC
help
This driver supports the Zilog8530 serial ports found on many Sparc
systems. Say Y or M if you want to be able to these serial ports.
@@ -491,7 +491,7 @@ config SERIAL_SUNZILOG_CONSOLE
config SERIAL_SUNSU
tristate "Sun SU serial support"
- depends on (SPARC32 || SPARC64) && PCI
+ depends on SPARC && PCI
help
This driver supports the 8250 serial ports that run the keyboard and
mouse on (PCI) UltraSPARC systems. Say Y or M if you want to be able
@@ -547,7 +547,7 @@ config PDC_CONSOLE
config SERIAL_SUNSAB
tristate "Sun Siemens SAB82532 serial support"
- depends on (SPARC32 || SPARC64) && PCI
+ depends on SPARC && PCI
help
This driver supports the Siemens SAB82532 DUSCC serial ports on newer
(PCI) UltraSPARC systems. Say Y or M if you want to be able to these
diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c
index 89d7bd3eaee3..d84476ee6592 100644
--- a/drivers/serial/amba-pl011.c
+++ b/drivers/serial/amba-pl011.c
@@ -160,7 +160,7 @@ pl011_rx_chars(struct uart_amba_port *uap)
flag = TTY_FRAME;
}
- if (uart_handle_sysrq_char(&uap->port, ch, regs))
+ if (uart_handle_sysrq_char(&uap->port, ch & 255, regs))
goto ignore_char;
uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag);
diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c
index ff5e6309d682..cc998b99a19f 100644
--- a/drivers/serial/pxa.c
+++ b/drivers/serial/pxa.c
@@ -361,7 +361,7 @@ static int serial_pxa_startup(struct uart_port *port)
if (port->line == 3) /* HWUART */
up->mcr |= UART_MCR_AFE;
else
- up->mcr = 0;
+ up->mcr = 0;
/*
* Allocate the IRQ
@@ -641,7 +641,7 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
int i;
/*
- * First save the UER then disable the interrupts
+ * First save the IER then disable the interrupts
*/
ier = serial_in(up, UART_IER);
serial_out(up, UART_IER, UART_IER_UUE);
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index a50c2bc506f2..3639c3f8d357 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_USB_MIDI) += class/
obj-$(CONFIG_USB_PRINTER) += class/
obj-$(CONFIG_USB_STORAGE) += storage/
+obj-$(CONFIG_USB) += storage/
obj-$(CONFIG_USB_AIPTEK) += input/
obj-$(CONFIG_USB_ATI_REMOTE) += input/
diff --git a/drivers/usb/atm/Kconfig b/drivers/usb/atm/Kconfig
index f429862e0974..550ddfa71a43 100644
--- a/drivers/usb/atm/Kconfig
+++ b/drivers/usb/atm/Kconfig
@@ -44,6 +44,19 @@ config USB_CXACRU
To compile this driver as a module, choose M here: the
module will be called cxacru.
+config USB_UEAGLEATM
+ tristate "ADI 930 and eagle USB DSL modem"
+ depends on USB_ATM
+ select FW_LOADER
+ help
+ Say Y here if you have an ADSL USB modem based on the ADI 930
+ or eagle chipset. In order to use your modem you will need to
+ install firmwares and CMV (Command Management Variables); see
+ <https://gna.org/projects/ueagleatm/> for details.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ueagle-atm.
+
config USB_XUSBATM
tristate "Other USB DSL modem support"
depends on USB_ATM
diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile
index 85099718c683..4c4a776ab1cd 100644
--- a/drivers/usb/atm/Makefile
+++ b/drivers/usb/atm/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_USB_CXACRU) += cxacru.o
obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
+obj-$(CONFIG_USB_UEAGLEATM) += ueagle-atm.o
obj-$(CONFIG_USB_ATM) += usbatm.o
obj-$(CONFIG_USB_XUSBATM) += xusbatm.o
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 9d59dc62e6d2..af0a41e7870e 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -853,7 +853,6 @@ static int cxacru_usb_probe(struct usb_interface *intf, const struct usb_device_
}
static struct usb_driver cxacru_usb_driver = {
- .owner = THIS_MODULE,
.name = cxacru_driver_name,
.probe = cxacru_usb_probe,
.disconnect = usbatm_usb_disconnect,
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
index d0cbbb7f0385..b28336148658 100644
--- a/drivers/usb/atm/speedtch.c
+++ b/drivers/usb/atm/speedtch.c
@@ -659,7 +659,6 @@ MODULE_DEVICE_TABLE(usb, speedtch_usb_ids);
static int speedtch_usb_probe(struct usb_interface *, const struct usb_device_id *);
static struct usb_driver speedtch_usb_driver = {
- .owner = THIS_MODULE,
.name = speedtch_driver_name,
.probe = speedtch_usb_probe,
.disconnect = usbatm_usb_disconnect,
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
new file mode 100644
index 000000000000..7d2a679989ed
--- /dev/null
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -0,0 +1,1820 @@
+/*-
+ * Copyright (c) 2003, 2004
+ * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
+ *
+ * Copyright (c) 2005 Matthieu Castet <castet.matthieu@free.fr>
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * GPL license :
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * HISTORY : some part of the code was base on ueagle 1.3 BSD driver,
+ * Damien Bergamini agree to put his code under a DUAL GPL/BSD license.
+ *
+ * The rest of the code was was rewritten from scratch.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/ctype.h>
+#include <linux/kthread.h>
+#include <linux/version.h>
+#include <asm/unaligned.h>
+
+#include "usbatm.h"
+
+#define EAGLEUSBVERSION "ueagle 1.1"
+
+
+/*
+ * Debug macros
+ */
+#define uea_dbg(usb_dev, format, args...) \
+ do { \
+ if (debug >= 1) \
+ dev_dbg(&(usb_dev)->dev, \
+ "[ueagle-atm dbg] %s: " format, \
+ __FUNCTION__, ##args); \
+ } while (0)
+
+#define uea_vdbg(usb_dev, format, args...) \
+ do { \
+ if (debug >= 2) \
+ dev_dbg(&(usb_dev)->dev, \
+ "[ueagle-atm vdbg] " format, ##args); \
+ } while (0)
+
+#define uea_enters(usb_dev) \
+ uea_vdbg(usb_dev, "entering %s\n", __FUNCTION__)
+
+#define uea_leaves(usb_dev) \
+ uea_vdbg(usb_dev, "leaving %s\n", __FUNCTION__)
+
+#define uea_err(usb_dev, format,args...) \
+ dev_err(&(usb_dev)->dev ,"[UEAGLE-ATM] " format , ##args)
+
+#define uea_warn(usb_dev, format,args...) \
+ dev_warn(&(usb_dev)->dev ,"[Ueagle-atm] " format, ##args)
+
+#define uea_info(usb_dev, format,args...) \
+ dev_info(&(usb_dev)->dev ,"[ueagle-atm] " format, ##args)
+
+struct uea_cmvs {
+ u32 address;
+ u16 offset;
+ u32 data;
+} __attribute__ ((packed));
+
+struct uea_softc {
+ struct usb_device *usb_dev;
+ struct usbatm_data *usbatm;
+
+ int modem_index;
+ unsigned int driver_info;
+
+ int booting;
+ int reset;
+
+ wait_queue_head_t sync_q;
+
+ struct task_struct *kthread;
+ u32 data;
+ wait_queue_head_t cmv_ack_wait;
+ int cmv_ack;
+
+ struct work_struct task;
+ u16 pageno;
+ u16 ovl;
+
+ const struct firmware *dsp_firm;
+ struct urb *urb_int;
+
+ u8 cmv_function;
+ u16 cmv_idx;
+ u32 cmv_address;
+ u16 cmv_offset;
+
+ /* keep in sync with eaglectl */
+ struct uea_stats {
+ struct {
+ u32 state;
+ u32 flags;
+ u32 mflags;
+ u32 vidcpe;
+ u32 vidco;
+ u32 dsrate;
+ u32 usrate;
+ u32 dsunc;
+ u32 usunc;
+ u32 dscorr;
+ u32 uscorr;
+ u32 txflow;
+ u32 rxflow;
+ u32 usattenuation;
+ u32 dsattenuation;
+ u32 dsmargin;
+ u32 usmargin;
+ u32 firmid;
+ } phy;
+ } stats;
+};
+
+/*
+ * Elsa IDs
+ */
+#define ELSA_VID 0x05CC
+#define ELSA_PID_PSTFIRM 0x3350
+#define ELSA_PID_PREFIRM 0x3351
+
+/*
+ * Sagem USB IDs
+ */
+#define EAGLE_VID 0x1110
+#define EAGLE_I_PID_PREFIRM 0x9010 /* Eagle I */
+#define EAGLE_I_PID_PSTFIRM 0x900F /* Eagle I */
+
+#define EAGLE_IIC_PID_PREFIRM 0x9024 /* Eagle IIC */
+#define EAGLE_IIC_PID_PSTFIRM 0x9023 /* Eagle IIC */
+
+#define EAGLE_II_PID_PREFIRM 0x9022 /* Eagle II */
+#define EAGLE_II_PID_PSTFIRM 0x9021 /* Eagle II */
+
+/*
+ * Eagle III Pid
+ */
+#define EAGLE_III_PID_PREFIRM 0x9032 /* Eagle III */
+#define EAGLE_III_PID_PSTFIRM 0x9031 /* Eagle III */
+
+/*
+ * USR USB IDs
+ */
+#define USR_VID 0x0BAF
+#define MILLER_A_PID_PREFIRM 0x00F2
+#define MILLER_A_PID_PSTFIRM 0x00F1
+#define MILLER_B_PID_PREFIRM 0x00FA
+#define MILLER_B_PID_PSTFIRM 0x00F9
+#define HEINEKEN_A_PID_PREFIRM 0x00F6
+#define HEINEKEN_A_PID_PSTFIRM 0x00F5
+#define HEINEKEN_B_PID_PREFIRM 0x00F8
+#define HEINEKEN_B_PID_PSTFIRM 0x00F7
+
+#define PREFIRM 0
+#define PSTFIRM (1<<7)
+enum {
+ ADI930 = 0,
+ EAGLE_I,
+ EAGLE_II,
+ EAGLE_III
+};
+
+/* macros for both struct usb_device_id and struct uea_softc */
+#define UEA_IS_PREFIRM(x) \
+ (!((x)->driver_info & PSTFIRM))
+#define UEA_CHIP_VERSION(x) \
+ ((x)->driver_info & 0xf)
+
+#define IS_ISDN(sc) \
+ (le16_to_cpu(sc->usb_dev->descriptor.bcdDevice) & 0x80)
+
+#define INS_TO_USBDEV(ins) ins->usb_dev
+
+#define GET_STATUS(data) \
+ ((data >> 8) & 0xf)
+#define IS_OPERATIONAL(sc) \
+ (GET_STATUS(sc->stats.phy.state) == 2)
+
+/*
+ * Set of macros to handle unaligned data in the firmware blob.
+ * The FW_GET_BYTE() macro is provided only for consistency.
+ */
+
+#define FW_GET_BYTE(p) *((__u8 *) (p))
+#define FW_GET_WORD(p) le16_to_cpu(get_unaligned((__le16 *) (p)))
+#define FW_GET_LONG(p) le32_to_cpu(get_unaligned((__le32 *) (p)))
+
+#define FW_DIR "ueagle-atm/"
+#define NB_MODEM 4
+
+#define BULK_TIMEOUT 300
+#define CTRL_TIMEOUT 1000
+
+#define ACK_TIMEOUT msecs_to_jiffies(1500)
+
+#define UEA_INTR_IFACE_NO 0
+#define UEA_US_IFACE_NO 1
+#define UEA_DS_IFACE_NO 2
+
+#define FASTEST_ISO_INTF 8
+
+#define UEA_BULK_DATA_PIPE 0x02
+#define UEA_IDMA_PIPE 0x04
+#define UEA_INTR_PIPE 0x04
+#define UEA_ISO_DATA_PIPE 0x08
+
+#define UEA_SET_BLOCK 0x0001
+#define UEA_SET_MODE 0x0003
+#define UEA_SET_2183_DATA 0x0004
+#define UEA_SET_TIMEOUT 0x0011
+
+#define UEA_LOOPBACK_OFF 0x0002
+#define UEA_LOOPBACK_ON 0x0003
+#define UEA_BOOT_IDMA 0x0006
+#define UEA_START_RESET 0x0007
+#define UEA_END_RESET 0x0008
+
+#define UEA_SWAP_MAILBOX (0x3fcd | 0x4000)
+#define UEA_MPTX_START (0x3fce | 0x4000)
+#define UEA_MPTX_MAILBOX (0x3fd6 | 0x4000)
+#define UEA_MPRX_MAILBOX (0x3fdf | 0x4000)
+
+/* structure describing a block within a DSP page */
+struct block_info {
+ __le16 wHdr;
+#define UEA_BIHDR 0xabcd
+ __le16 wAddress;
+ __le16 wSize;
+ __le16 wOvlOffset;
+ __le16 wOvl; /* overlay */
+ __le16 wLast;
+} __attribute__ ((packed));
+#define BLOCK_INFO_SIZE 12
+
+/* structure representing a CMV (Configuration and Management Variable) */
+struct cmv {
+ __le16 wPreamble;
+#define PREAMBLE 0x535c
+ __u8 bDirection;
+#define MODEMTOHOST 0x01
+#define HOSTTOMODEM 0x10
+ __u8 bFunction;
+#define FUNCTION_TYPE(f) ((f) >> 4)
+#define MEMACCESS 0x1
+#define ADSLDIRECTIVE 0x7
+
+#define FUNCTION_SUBTYPE(f) ((f) & 0x0f)
+/* for MEMACCESS */
+#define REQUESTREAD 0x0
+#define REQUESTWRITE 0x1
+#define REPLYREAD 0x2
+#define REPLYWRITE 0x3
+/* for ADSLDIRECTIVE */
+#define KERNELREADY 0x0
+#define MODEMREADY 0x1
+
+#define MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf))
+ __le16 wIndex;
+ __le32 dwSymbolicAddress;
+#define MAKESA(a, b, c, d) \
+ (((c) & 0xff) << 24 | \
+ ((d) & 0xff) << 16 | \
+ ((a) & 0xff) << 8 | \
+ ((b) & 0xff))
+
+#define SA_CNTL MAKESA('C', 'N', 'T', 'L')
+#define SA_DIAG MAKESA('D', 'I', 'A', 'G')
+#define SA_INFO MAKESA('I', 'N', 'F', 'O')
+#define SA_OPTN MAKESA('O', 'P', 'T', 'N')
+#define SA_RATE MAKESA('R', 'A', 'T', 'E')
+#define SA_STAT MAKESA('S', 'T', 'A', 'T')
+ __le16 wOffsetAddress;
+ __le32 dwData;
+} __attribute__ ((packed));
+#define CMV_SIZE 16
+
+/* structure representing swap information */
+struct swap_info {
+ __u8 bSwapPageNo;
+ __u8 bOvl; /* overlay */
+} __attribute__ ((packed));
+
+/* structure representing interrupt data */
+struct intr_pkt {
+ __u8 bType;
+ __u8 bNotification;
+ __le16 wValue;
+ __le16 wIndex;
+ __le16 wLength;
+ __le16 wInterrupt;
+#define INT_LOADSWAPPAGE 0x0001
+#define INT_INCOMINGCMV 0x0002
+ union {
+ struct {
+ struct swap_info swapinfo;
+ __le16 wDataSize;
+ } __attribute__ ((packed)) s1;
+
+ struct {
+ struct cmv cmv;
+ __le16 wDataSize;
+ } __attribute__ ((packed)) s2;
+ } __attribute__ ((packed)) u;
+#define bSwapPageNo u.s1.swapinfo.bSwapPageNo
+#define bOvl u.s1.swapinfo.bOvl
+} __attribute__ ((packed));
+#define INTR_PKT_SIZE 28
+
+static struct usb_driver uea_driver;
+static DECLARE_MUTEX(uea_semaphore);
+static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III"};
+
+static int modem_index;
+static unsigned int debug;
+static int sync_wait[NB_MODEM];
+static char *cmv_file[NB_MODEM];
+
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "module debug level (0=off,1=on,2=verbose)");
+module_param_array(sync_wait, bool, NULL, 0644);
+MODULE_PARM_DESC(sync_wait, "wait the synchronisation before starting ATM");
+module_param_array(cmv_file, charp, NULL, 0644);
+MODULE_PARM_DESC(cmv_file,
+ "file name with configuration and management variables");
+
+#define UPDATE_ATM_STAT(type, val) \
+ do { \
+ if (sc->usbatm->atm_dev) \
+ sc->usbatm->atm_dev->type = val; \
+ } while (0)
+
+/* Firmware loading */
+#define LOAD_INTERNAL 0xA0
+#define F8051_USBCS 0x7f92
+
+/**
+ * uea_send_modem_cmd - Send a command for pre-firmware devices.
+ */
+static int uea_send_modem_cmd(struct usb_device *usb,
+ u16 addr, u16 size, u8 * buff)
+{
+ int ret = -ENOMEM;
+ u8 *xfer_buff;
+
+ xfer_buff = kmalloc(size, GFP_KERNEL);
+ if (xfer_buff) {
+ memcpy(xfer_buff, buff, size);
+ ret = usb_control_msg(usb,
+ usb_sndctrlpipe(usb, 0),
+ LOAD_INTERNAL,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, addr, 0, xfer_buff,
+ size, CTRL_TIMEOUT);
+ kfree(xfer_buff);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return (ret == size) ? 0 : -EIO;
+}
+
+static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *context)
+{
+ struct usb_device *usb = context;
+ u8 *pfw, value;
+ u32 crc = 0;
+ int ret, size;
+
+ uea_enters(usb);
+ if (!fw_entry) {
+ uea_err(usb, "firmware is not available\n");
+ goto err;
+ }
+
+ pfw = fw_entry->data;
+ size = fw_entry->size;
+ if (size < 4)
+ goto err_fw_corrupted;
+
+ crc = FW_GET_LONG(pfw);
+ pfw += 4;
+ size -= 4;
+ if (crc32_be(0, pfw, size) != crc)
+ goto err_fw_corrupted;
+
+ /*
+ * Start to upload formware : send reset
+ */
+ value = 1;
+ ret = uea_send_modem_cmd(usb, F8051_USBCS, sizeof(value), &value);
+
+ if (ret < 0) {
+ uea_err(usb, "modem reset failed with error %d\n", ret);
+ goto err;
+ }
+
+ while (size > 3) {
+ u8 len = FW_GET_BYTE(pfw);
+ u16 add = FW_GET_WORD(pfw + 1);
+
+ size -= len + 3;
+ if (size < 0)
+ goto err_fw_corrupted;
+
+ ret = uea_send_modem_cmd(usb, add, len, pfw + 3);
+ if (ret < 0) {
+ uea_err(usb, "uploading firmware data failed "
+ "with error %d\n", ret);
+ goto err;
+ }
+ pfw += len + 3;
+ }
+
+ if (size != 0)
+ goto err_fw_corrupted;
+
+ /*
+ * Tell the modem we finish : de-assert reset
+ */
+ value = 0;
+ ret = uea_send_modem_cmd(usb, F8051_USBCS, 1, &value);
+ if (ret < 0)
+ uea_err(usb, "modem de-assert failed with error %d\n", ret);
+ else
+ uea_info(usb, "firmware uploaded\n");
+
+ uea_leaves(usb);
+ return;
+
+err_fw_corrupted:
+ uea_err(usb, "firmware is corrupted\n");
+err:
+ uea_leaves(usb);
+}
+
+/**
+ * uea_load_firmware - Load usb firmware for pre-firmware devices.
+ */
+static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
+{
+ int ret;
+ char *fw_name = FW_DIR "eagle.fw";
+
+ uea_enters(usb);
+ uea_info(usb, "pre-firmware device, uploading firmware\n");
+
+ switch (ver) {
+ case ADI930:
+ fw_name = FW_DIR "adi930.fw";
+ break;
+ case EAGLE_I:
+ fw_name = FW_DIR "eagleI.fw";
+ break;
+ case EAGLE_II:
+ fw_name = FW_DIR "eagleII.fw";
+ break;
+ case EAGLE_III:
+ fw_name = FW_DIR "eagleIII.fw";
+ break;
+ }
+
+ ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, usb, uea_upload_pre_firmware);
+ if (ret)
+ uea_err(usb, "firmware %s is not available\n", fw_name);
+ else
+ uea_info(usb, "loading firmware %s\n", fw_name);
+
+ uea_leaves(usb);
+ return ret;
+}
+
+/* modem management : dsp firmware, send/read CMV, monitoring statistic
+ */
+
+/*
+ * Make sure that the DSP code provided is safe to use.
+ */
+static int check_dsp(u8 *dsp, unsigned int len)
+{
+ u8 pagecount, blockcount;
+ u16 blocksize;
+ u32 pageoffset;
+ unsigned int i, j, p, pp;
+
+ pagecount = FW_GET_BYTE(dsp);
+ p = 1;
+
+ /* enough space for page offsets? */
+ if (p + 4 * pagecount > len)
+ return 1;
+
+ for (i = 0; i < pagecount; i++) {
+
+ pageoffset = FW_GET_LONG(dsp + p);
+ p += 4;
+
+ if (pageoffset == 0)
+ continue;
+
+ /* enough space for blockcount? */
+ if (pageoffset >= len)
+ return 1;
+
+ pp = pageoffset;
+ blockcount = FW_GET_BYTE(dsp + pp);
+ pp += 1;
+
+ for (j = 0; j < blockcount; j++) {
+
+ /* enough space for block header? */
+ if (pp + 4 > len)
+ return 1;
+
+ pp += 2; /* skip blockaddr */
+ blocksize = FW_GET_WORD(dsp + pp);
+ pp += 2;
+
+ /* enough space for block data? */
+ if (pp + blocksize > len)
+ return 1;
+
+ pp += blocksize;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * send data to the idma pipe
+ * */
+static int uea_idma_write(struct uea_softc *sc, void *data, u32 size)
+{
+ int ret = -ENOMEM;
+ u8 *xfer_buff;
+ int bytes_read;
+
+ xfer_buff = kmalloc(size, GFP_KERNEL);
+ if (!xfer_buff) {
+ uea_err(INS_TO_USBDEV(sc), "can't allocate xfer_buff\n");
+ return ret;
+ }
+
+ memcpy(xfer_buff, data, size);
+
+ ret = usb_bulk_msg(sc->usb_dev,
+ usb_sndbulkpipe(sc->usb_dev, UEA_IDMA_PIPE),
+ xfer_buff, size, &bytes_read, BULK_TIMEOUT);
+
+ kfree(xfer_buff);
+ if (ret < 0)
+ return ret;
+ if (size != bytes_read) {
+ uea_err(INS_TO_USBDEV(sc), "size != bytes_read %d %d\n", size,
+ bytes_read);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int request_dsp(struct uea_softc *sc)
+{
+ int ret;
+ char *dsp_name;
+
+ if (UEA_CHIP_VERSION(sc) == ADI930) {
+ if (IS_ISDN(sc))
+ dsp_name = FW_DIR "DSP9i.bin";
+ else
+ dsp_name = FW_DIR "DSP9p.bin";
+ } else {
+ if (IS_ISDN(sc))
+ dsp_name = FW_DIR "DSPei.bin";
+ else
+ dsp_name = FW_DIR "DSPep.bin";
+ }
+
+ ret = request_firmware(&sc->dsp_firm,
+ dsp_name, &sc->usb_dev->dev);
+ if (ret < 0) {
+ uea_err(INS_TO_USBDEV(sc),
+ "requesting firmware %s failed with error %d\n",
+ dsp_name, ret);
+ return ret;
+ }
+
+ if (check_dsp(sc->dsp_firm->data, sc->dsp_firm->size)) {
+ uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
+ dsp_name);
+ release_firmware(sc->dsp_firm);
+ sc->dsp_firm = NULL;
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+/*
+ * The uea_load_page() function must be called within a process context
+ */
+static void uea_load_page(void *xsc)
+{
+ struct uea_softc *sc = xsc;
+ u16 pageno = sc->pageno;
+ u16 ovl = sc->ovl;
+ struct block_info bi;
+
+ u8 *p;
+ u8 pagecount, blockcount;
+ u16 blockaddr, blocksize;
+ u32 pageoffset;
+ int i;
+
+ /* reload firmware when reboot start and it's loaded already */
+ if (ovl == 0 && pageno == 0 && sc->dsp_firm) {
+ release_firmware(sc->dsp_firm);
+ sc->dsp_firm = NULL;
+ }
+
+ if (sc->dsp_firm == NULL && request_dsp(sc) < 0)
+ return;
+
+ p = sc->dsp_firm->data;
+ pagecount = FW_GET_BYTE(p);
+ p += 1;
+
+ if (pageno >= pagecount)
+ goto bad1;
+
+ p += 4 * pageno;
+ pageoffset = FW_GET_LONG(p);
+
+ if (pageoffset == 0)
+ goto bad1;
+
+ p = sc->dsp_firm->data + pageoffset;
+ blockcount = FW_GET_BYTE(p);
+ p += 1;
+
+ uea_dbg(INS_TO_USBDEV(sc),
+ "sending %u blocks for DSP page %u\n", blockcount, pageno);
+
+ bi.wHdr = cpu_to_le16(UEA_BIHDR);
+ bi.wOvl = cpu_to_le16(ovl);
+ bi.wOvlOffset = cpu_to_le16(ovl | 0x8000);
+
+ for (i = 0; i < blockcount; i++) {
+ blockaddr = FW_GET_WORD(p);
+ p += 2;
+
+ blocksize = FW_GET_WORD(p);
+ p += 2;
+
+ bi.wSize = cpu_to_le16(blocksize);
+ bi.wAddress = cpu_to_le16(blockaddr);
+ bi.wLast = cpu_to_le16((i == blockcount - 1) ? 1 : 0);
+
+ /* send block info through the IDMA pipe */
+ if (uea_idma_write(sc, &bi, BLOCK_INFO_SIZE))
+ goto bad2;
+
+ /* send block data through the IDMA pipe */
+ if (uea_idma_write(sc, p, blocksize))
+ goto bad2;
+
+ p += blocksize;
+ }
+
+ return;
+
+bad2:
+ uea_err(INS_TO_USBDEV(sc), "sending DSP block %u failed\n", i);
+ return;
+bad1:
+ uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n",pageno);
+}
+
+static inline void wake_up_cmv_ack(struct uea_softc *sc)
+{
+ sc->cmv_ack = 1;
+ wake_up(&sc->cmv_ack_wait);
+}
+
+static inline int wait_cmv_ack(struct uea_softc *sc)
+{
+ int ret = wait_event_timeout(sc->cmv_ack_wait,
+ sc->cmv_ack, ACK_TIMEOUT);
+ sc->cmv_ack = 0;
+
+ if (ret < 0)
+ return ret;
+
+ return (ret == 0) ? -ETIMEDOUT : 0;
+
+}
+
+#define UCDC_SEND_ENCAPSULATED_COMMAND 0x00
+
+static int uea_request(struct uea_softc *sc,
+ u16 value, u16 index, u16 size, void *data)
+{
+ u8 *xfer_buff;
+ int ret = -ENOMEM;
+
+ xfer_buff = kmalloc(size, GFP_KERNEL);
+ if (!xfer_buff) {
+ uea_err(INS_TO_USBDEV(sc), "can't allocate xfer_buff\n");
+ return ret;
+ }
+ memcpy(xfer_buff, data, size);
+
+ ret = usb_control_msg(sc->usb_dev, usb_sndctrlpipe(sc->usb_dev, 0),
+ UCDC_SEND_ENCAPSULATED_COMMAND,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, xfer_buff, size, CTRL_TIMEOUT);
+
+ kfree(xfer_buff);
+ if (ret < 0) {
+ uea_err(INS_TO_USBDEV(sc), "usb_control_msg error %d\n", ret);
+ return ret;
+ }
+
+ if (ret != size) {
+ uea_err(INS_TO_USBDEV(sc),
+ "usb_control_msg send only %d bytes (instead of %d)\n",
+ ret, size);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int uea_cmv(struct uea_softc *sc,
+ u8 function, u32 address, u16 offset, u32 data)
+{
+ struct cmv cmv;
+ int ret;
+
+ /* we send a request, but we expect a reply */
+ sc->cmv_function = function | 0x2;
+ sc->cmv_idx++;
+ sc->cmv_address = address;
+ sc->cmv_offset = offset;
+
+ cmv.wPreamble = cpu_to_le16(PREAMBLE);
+ cmv.bDirection = HOSTTOMODEM;
+ cmv.bFunction = function;
+ cmv.wIndex = cpu_to_le16(sc->cmv_idx);
+ put_unaligned(cpu_to_le32(address), &cmv.dwSymbolicAddress);
+ cmv.wOffsetAddress = cpu_to_le16(offset);
+ put_unaligned(cpu_to_le32(data >> 16 | data << 16), &cmv.dwData);
+
+ ret = uea_request(sc, UEA_SET_BLOCK, UEA_MPTX_START, CMV_SIZE, &cmv);
+ if (ret < 0)
+ return ret;
+ return wait_cmv_ack(sc);
+}
+
+static inline int uea_read_cmv(struct uea_softc *sc,
+ u32 address, u16 offset, u32 *data)
+{
+ int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTREAD),
+ address, offset, 0);
+ if (ret < 0)
+ uea_err(INS_TO_USBDEV(sc),
+ "reading cmv failed with error %d\n", ret);
+ else
+ *data = sc->data;
+
+ return ret;
+}
+
+static inline int uea_write_cmv(struct uea_softc *sc,
+ u32 address, u16 offset, u32 data)
+{
+ int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTWRITE),
+ address, offset, data);
+ if (ret < 0)
+ uea_err(INS_TO_USBDEV(sc),
+ "writing cmv failed with error %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * Monitor the modem and update the stat
+ * return 0 if everything is ok
+ * return < 0 if an error occurs (-EAGAIN reboot needed)
+ */
+static int uea_stat(struct uea_softc *sc)
+{
+ u32 data;
+ int ret;
+
+ uea_enters(INS_TO_USBDEV(sc));
+ data = sc->stats.phy.state;
+
+ ret = uea_read_cmv(sc, SA_STAT, 0, &sc->stats.phy.state);
+ if (ret < 0)
+ return ret;
+
+ switch (GET_STATUS(sc->stats.phy.state)) {
+ case 0: /* not yet synchronized */
+ uea_dbg(INS_TO_USBDEV(sc),
+ "modem not yet synchronized\n");
+ return 0;
+
+ case 1: /* initialization */
+ uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n");
+ return 0;
+
+ case 2: /* operational */
+ uea_vdbg(INS_TO_USBDEV(sc), "modem operational\n");
+ break;
+
+ case 3: /* fail ... */
+ uea_info(INS_TO_USBDEV(sc), "modem synchronization failed\n");
+ return -EAGAIN;
+
+ case 4 ... 6: /* test state */
+ uea_warn(INS_TO_USBDEV(sc),
+ "modem in test mode - not supported\n");
+ return -EAGAIN;
+
+ case 7: /* fast-retain ... */
+ uea_info(INS_TO_USBDEV(sc), "modem in fast-retain mode\n");
+ return 0;
+ default:
+ uea_err(INS_TO_USBDEV(sc), "modem invalid SW mode %d\n",
+ GET_STATUS(sc->stats.phy.state));
+ return -EAGAIN;
+ }
+
+ if (GET_STATUS(data) != 2) {
+ uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_OFF, 0, NULL);
+ uea_info(INS_TO_USBDEV(sc), "modem operational\n");
+
+ /* release the dsp firmware as it is not needed until
+ * the next failure
+ */
+ if (sc->dsp_firm) {
+ release_firmware(sc->dsp_firm);
+ sc->dsp_firm = NULL;
+ }
+
+ ret = uea_read_cmv(sc, SA_INFO, 10, &sc->stats.phy.firmid);
+ if (ret < 0)
+ return ret;
+ uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n",
+ sc->stats.phy.firmid);
+ }
+
+ /* always update it as atm layer could not be init when we switch to
+ * operational state
+ */
+ UPDATE_ATM_STAT(signal, ATM_PHY_SIG_FOUND);
+
+ /* wake up processes waiting for synchronization */
+ wake_up(&sc->sync_q);
+
+ ret = uea_read_cmv(sc, SA_DIAG, 2, &sc->stats.phy.flags);
+ if (ret < 0)
+ return ret;
+ sc->stats.phy.mflags |= sc->stats.phy.flags;
+
+ /* in case of a flags ( for example delineation LOSS (& 0x10)),
+ * we check the status again in order to detect the failure earlier
+ */
+ if (sc->stats.phy.flags) {
+ uea_dbg(INS_TO_USBDEV(sc), "Stat flag = %d\n",
+ sc->stats.phy.flags);
+ return 0;
+ }
+
+ ret = uea_read_cmv(sc, SA_RATE, 0, &data);
+ if (ret < 0)
+ return ret;
+
+ /* in bulk mode the modem have problem with high rate
+ * changing internal timing could improve things, but the
+ * value is misterious.
+ * ADI930 don't support it (-EPIPE error).
+ */
+ if (UEA_CHIP_VERSION(sc) != ADI930
+ && sc->stats.phy.dsrate != (data >> 16) * 32) {
+ /* Original timming from ADI(used in windows driver)
+ * 0x20ffff>>16 * 32 = 32 * 32 = 1Mbits
+ */
+ u16 timeout = (data <= 0x20ffff) ? 0 : 1;
+ ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL);
+ uea_info(INS_TO_USBDEV(sc),
+ "setting new timeout %d%s\n", timeout,
+ ret < 0?" failed":"");
+ }
+ sc->stats.phy.dsrate = (data >> 16) * 32;
+ sc->stats.phy.usrate = (data & 0xffff) * 32;
+ UPDATE_ATM_STAT(link_rate, sc->stats.phy.dsrate * 1000 / 424);
+
+ ret = uea_read_cmv(sc, SA_DIAG, 23, &data);
+ if (ret < 0)
+ return ret;
+ sc->stats.phy.dsattenuation = (data & 0xff) / 2;
+
+ ret = uea_read_cmv(sc, SA_DIAG, 47, &data);
+ if (ret < 0)
+ return ret;
+ sc->stats.phy.usattenuation = (data & 0xff) / 2;
+
+ ret = uea_read_cmv(sc, SA_DIAG, 25, &sc->stats.phy.dsmargin);
+ if (ret < 0)
+ return ret;
+
+ ret = uea_read_cmv(sc, SA_DIAG, 49, &sc->stats.phy.usmargin);
+ if (ret < 0)
+ return ret;
+
+ ret = uea_read_cmv(sc, SA_DIAG, 51, &sc->stats.phy.rxflow);
+ if (ret < 0)
+ return ret;
+
+ ret = uea_read_cmv(sc, SA_DIAG, 52, &sc->stats.phy.txflow);
+ if (ret < 0)
+ return ret;
+
+ ret = uea_read_cmv(sc, SA_DIAG, 54, &sc->stats.phy.dsunc);
+ if (ret < 0)
+ return ret;
+
+ /* only for atu-c */
+ ret = uea_read_cmv(sc, SA_DIAG, 58, &sc->stats.phy.usunc);
+ if (ret < 0)
+ return ret;
+
+ ret = uea_read_cmv(sc, SA_DIAG, 53, &sc->stats.phy.dscorr);
+ if (ret < 0)
+ return ret;
+
+ /* only for atu-c */
+ ret = uea_read_cmv(sc, SA_DIAG, 57, &sc->stats.phy.uscorr);
+ if (ret < 0)
+ return ret;
+
+ ret = uea_read_cmv(sc, SA_INFO, 8, &sc->stats.phy.vidco);
+ if (ret < 0)
+ return ret;
+
+ ret = uea_read_cmv(sc, SA_INFO, 13, &sc->stats.phy.vidcpe);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int request_cmvs(struct uea_softc *sc,
+ struct uea_cmvs **cmvs, const struct firmware **fw)
+{
+ int ret, size;
+ u8 *data;
+ char *file;
+ static char cmv_name[256] = FW_DIR;
+
+ if (cmv_file[sc->modem_index] == NULL) {
+ if (UEA_CHIP_VERSION(sc) == ADI930)
+ file = (IS_ISDN(sc)) ? "CMV9i.bin" : "CMV9p.bin";
+ else
+ file = (IS_ISDN(sc)) ? "CMVei.bin" : "CMVep.bin";
+ } else
+ file = cmv_file[sc->modem_index];
+
+ strcpy(cmv_name, FW_DIR);
+ strlcat(cmv_name, file, sizeof(cmv_name));
+
+ ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev);
+ if (ret < 0) {
+ uea_err(INS_TO_USBDEV(sc),
+ "requesting firmware %s failed with error %d\n",
+ cmv_name, ret);
+ return ret;
+ }
+
+ data = (u8 *) (*fw)->data;
+ size = *data * sizeof(struct uea_cmvs) + 1;
+ if (size != (*fw)->size) {
+ uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
+ cmv_name);
+ release_firmware(*fw);
+ return -EILSEQ;
+ }
+
+ *cmvs = (struct uea_cmvs *)(data + 1);
+ return *data;
+}
+
+/* Start boot post firmware modem:
+ * - send reset commands through usb control pipe
+ * - start workqueue for DSP loading
+ * - send CMV options to modem
+ */
+
+static int uea_start_reset(struct uea_softc *sc)
+{
+ u16 zero = 0; /* ;-) */
+ int i, len, ret;
+ struct uea_cmvs *cmvs;
+ const struct firmware *cmvs_fw;
+
+ uea_enters(INS_TO_USBDEV(sc));
+ uea_info(INS_TO_USBDEV(sc), "(re)booting started\n");
+
+ sc->booting = 1;
+ UPDATE_ATM_STAT(signal, ATM_PHY_SIG_LOST);
+
+ /* reset statistics */
+ memset(&sc->stats, 0, sizeof(struct uea_stats));
+
+ /* tell the modem that we want to boot in IDMA mode */
+ uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL);
+ uea_request(sc, UEA_SET_MODE, UEA_BOOT_IDMA, 0, NULL);
+
+ /* enter reset mode */
+ uea_request(sc, UEA_SET_MODE, UEA_START_RESET, 0, NULL);
+
+ /* original driver use 200ms, but windows driver use 100ms */
+ msleep(100);
+
+ /* leave reset mode */
+ uea_request(sc, UEA_SET_MODE, UEA_END_RESET, 0, NULL);
+
+ /* clear tx and rx mailboxes */
+ uea_request(sc, UEA_SET_2183_DATA, UEA_MPTX_MAILBOX, 2, &zero);
+ uea_request(sc, UEA_SET_2183_DATA, UEA_MPRX_MAILBOX, 2, &zero);
+ uea_request(sc, UEA_SET_2183_DATA, UEA_SWAP_MAILBOX, 2, &zero);
+
+ msleep(1000);
+ sc->cmv_function = MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY);
+ sc->booting = 0;
+
+ /* start loading DSP */
+ sc->pageno = 0;
+ sc->ovl = 0;
+ schedule_work(&sc->task);
+
+ /* wait for modem ready CMV */
+ ret = wait_cmv_ack(sc);
+ if (ret < 0)
+ return ret;
+
+ /* Enter in R-IDLE (cmv) until instructed otherwise */
+ ret = uea_write_cmv(sc, SA_CNTL, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ /* get options */
+ ret = len = request_cmvs(sc, &cmvs, &cmvs_fw);
+ if (ret < 0)
+ return ret;
+
+ /* send options */
+ for (i = 0; i < len; i++) {
+ ret = uea_write_cmv(sc, FW_GET_LONG(&cmvs[i].address),
+ FW_GET_WORD(&cmvs[i].offset),
+ FW_GET_LONG(&cmvs[i].data));
+ if (ret < 0)
+ goto out;
+ }
+ /* Enter in R-ACT-REQ */
+ ret = uea_write_cmv(sc, SA_CNTL, 0, 2);
+out:
+ release_firmware(cmvs_fw);
+ sc->reset = 0;
+ uea_leaves(INS_TO_USBDEV(sc));
+ return ret;
+}
+
+/*
+ * In case of an error wait 1s before rebooting the modem
+ * if the modem don't request reboot (-EAGAIN).
+ * Monitor the modem every 1s.
+ */
+
+static int uea_kthread(void *data)
+{
+ struct uea_softc *sc = data;
+ int ret = -EAGAIN;
+
+ uea_enters(INS_TO_USBDEV(sc));
+ while (!kthread_should_stop()) {
+ if (ret < 0 || sc->reset)
+ ret = uea_start_reset(sc);
+ if (!ret)
+ ret = uea_stat(sc);
+ if (ret != -EAGAIN)
+ msleep(1000);
+ }
+ uea_leaves(INS_TO_USBDEV(sc));
+ return ret;
+}
+
+/* Load second usb firmware for ADI930 chip */
+static int load_XILINX_firmware(struct uea_softc *sc)
+{
+ const struct firmware *fw_entry;
+ int ret, size, u, ln;
+ u8 *pfw, value;
+ char *fw_name = FW_DIR "930-fpga.bin";
+
+ uea_enters(INS_TO_USBDEV(sc));
+
+ ret = request_firmware(&fw_entry, fw_name, &sc->usb_dev->dev);
+ if (ret) {
+ uea_err(INS_TO_USBDEV(sc), "firmware %s is not available\n",
+ fw_name);
+ goto err0;
+ }
+
+ pfw = fw_entry->data;
+ size = fw_entry->size;
+ if (size != 0x577B) {
+ uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n",
+ fw_name);
+ ret = -EILSEQ;
+ goto err1;
+ }
+ for (u = 0; u < size; u += ln) {
+ ln = min(size - u, 64);
+ ret = uea_request(sc, 0xe, 0, ln, pfw + u);
+ if (ret < 0) {
+ uea_err(INS_TO_USBDEV(sc),
+ "elsa download data failed (%d)\n", ret);
+ goto err1;
+ }
+ }
+
+ /* finish to send the fpga
+ */
+ ret = uea_request(sc, 0xe, 1, 0, NULL);
+ if (ret < 0) {
+ uea_err(INS_TO_USBDEV(sc),
+ "elsa download data failed (%d)\n", ret);
+ goto err1;
+ }
+
+ /*
+ * Tell the modem we finish : de-assert reset
+ */
+ value = 0;
+ ret = uea_send_modem_cmd(sc->usb_dev, 0xe, 1, &value);
+ if (ret < 0)
+ uea_err(sc->usb_dev, "elsa de-assert failed with error %d\n", ret);
+
+
+err1:
+ release_firmware(fw_entry);
+err0:
+ uea_leaves(INS_TO_USBDEV(sc));
+ return ret;
+}
+
+static void uea_dispatch_cmv(struct uea_softc *sc, struct cmv* cmv)
+{
+ uea_enters(INS_TO_USBDEV(sc));
+ if (le16_to_cpu(cmv->wPreamble) != PREAMBLE)
+ goto bad1;
+
+ if (cmv->bDirection != MODEMTOHOST)
+ goto bad1;
+
+ /* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to
+ * the first MEMACESS cmv. Ignore it...
+ */
+ if (cmv->bFunction != sc->cmv_function) {
+ if (UEA_CHIP_VERSION(sc) == ADI930
+ && cmv->bFunction == MAKEFUNCTION(2, 2)) {
+ cmv->wIndex = cpu_to_le16(sc->cmv_idx);
+ put_unaligned(cpu_to_le32(sc->cmv_address), &cmv->dwSymbolicAddress);
+ cmv->wOffsetAddress = cpu_to_le16(sc->cmv_offset);
+ }
+ else
+ goto bad2;
+ }
+
+ if (cmv->bFunction == MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY)) {
+ wake_up_cmv_ack(sc);
+ return;
+ }
+
+ /* in case of MEMACCESS */
+ if (le16_to_cpu(cmv->wIndex) != sc->cmv_idx ||
+ le32_to_cpu(get_unaligned(&cmv->dwSymbolicAddress)) !=
+ sc->cmv_address
+ || le16_to_cpu(cmv->wOffsetAddress) != sc->cmv_offset)
+ goto bad2;
+
+ sc->data = le32_to_cpu(get_unaligned(&cmv->dwData));
+ sc->data = sc->data << 16 | sc->data >> 16;
+
+ wake_up_cmv_ack(sc);
+ return;
+
+bad2:
+ uea_err(INS_TO_USBDEV(sc), "unexpected cmv received,"
+ "Function : %d, Subfunction : %d\n",
+ FUNCTION_TYPE(cmv->bFunction),
+ FUNCTION_SUBTYPE(cmv->bFunction));
+ return;
+
+bad1:
+ uea_err(INS_TO_USBDEV(sc), "invalid cmv received, "
+ "wPreamble %d, bDirection %d\n",
+ le16_to_cpu(cmv->wPreamble), cmv->bDirection);
+}
+
+/*
+ * interrupt handler
+ */
+static void uea_intr(struct urb *urb, struct pt_regs *regs)
+{
+ struct uea_softc *sc = (struct uea_softc *)urb->context;
+ struct intr_pkt *intr;
+ uea_enters(INS_TO_USBDEV(sc));
+
+ if (urb->status < 0) {
+ uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n",
+ urb->status);
+ return;
+ }
+
+ intr = (struct intr_pkt *) urb->transfer_buffer;
+
+ /* device-to-host interrupt */
+ if (intr->bType != 0x08 || sc->booting) {
+ uea_err(INS_TO_USBDEV(sc), "wrong intr\n");
+ // rebooting ?
+ // sc->reset = 1;
+ goto resubmit;
+ }
+
+ switch (le16_to_cpu(intr->wInterrupt)) {
+ case INT_LOADSWAPPAGE:
+ sc->pageno = intr->bSwapPageNo;
+ sc->ovl = intr->bOvl >> 4 | intr->bOvl << 4;
+ schedule_work(&sc->task);
+ break;
+
+ case INT_INCOMINGCMV:
+ uea_dispatch_cmv(sc, &intr->u.s2.cmv);
+ break;
+
+ default:
+ uea_err(INS_TO_USBDEV(sc), "unknown intr %u\n",
+ le16_to_cpu(intr->wInterrupt));
+ }
+
+resubmit:
+ usb_submit_urb(sc->urb_int, GFP_ATOMIC);
+}
+
+/*
+ * Start the modem : init the data and start kernel thread
+ */
+static int uea_boot(struct uea_softc *sc)
+{
+ int ret;
+ struct intr_pkt *intr;
+
+ uea_enters(INS_TO_USBDEV(sc));
+
+ INIT_WORK(&sc->task, uea_load_page, sc);
+ init_waitqueue_head(&sc->sync_q);
+ init_waitqueue_head(&sc->cmv_ack_wait);
+
+ if (UEA_CHIP_VERSION(sc) == ADI930)
+ load_XILINX_firmware(sc);
+
+ intr = kmalloc(INTR_PKT_SIZE, GFP_KERNEL);
+ if (!intr) {
+ uea_err(INS_TO_USBDEV(sc),
+ "cannot allocate interrupt package\n");
+ uea_leaves(INS_TO_USBDEV(sc));
+ return -ENOMEM;
+ }
+
+ sc->urb_int = usb_alloc_urb(0, GFP_KERNEL);
+ if (!sc->urb_int) {
+ uea_err(INS_TO_USBDEV(sc), "cannot allocate interrupt URB\n");
+ goto err;
+ }
+
+ usb_fill_int_urb(sc->urb_int, sc->usb_dev,
+ usb_rcvintpipe(sc->usb_dev, UEA_INTR_PIPE),
+ intr, INTR_PKT_SIZE, uea_intr, sc,
+ sc->usb_dev->actconfig->interface[0]->altsetting[0].
+ endpoint[0].desc.bInterval);
+
+ ret = usb_submit_urb(sc->urb_int, GFP_KERNEL);
+ if (ret < 0) {
+ uea_err(INS_TO_USBDEV(sc),
+ "urb submition failed with error %d\n", ret);
+ goto err1;
+ }
+
+ sc->kthread = kthread_run(uea_kthread, sc, "ueagle-atm");
+ if (sc->kthread == ERR_PTR(-ENOMEM)) {
+ uea_err(INS_TO_USBDEV(sc), "failed to create thread\n");
+ goto err2;
+ }
+
+ uea_leaves(INS_TO_USBDEV(sc));
+ return 0;
+
+err2:
+ usb_kill_urb(sc->urb_int);
+err1:
+ kfree(intr);
+err:
+ usb_free_urb(sc->urb_int);
+ uea_leaves(INS_TO_USBDEV(sc));
+ return -ENOMEM;
+}
+
+/*
+ * Stop the modem : kill kernel thread and free data
+ */
+static void uea_stop(struct uea_softc *sc)
+{
+ int ret;
+ uea_enters(INS_TO_USBDEV(sc));
+ ret = kthread_stop(sc->kthread);
+ uea_info(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret);
+
+ /* stop any pending boot process */
+ flush_scheduled_work();
+
+ uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL);
+
+ usb_kill_urb(sc->urb_int);
+ kfree(sc->urb_int->transfer_buffer);
+ usb_free_urb(sc->urb_int);
+
+ if (sc->dsp_firm)
+ release_firmware(sc->dsp_firm);
+ uea_leaves(INS_TO_USBDEV(sc));
+}
+
+/* syfs interface */
+static struct uea_softc *dev_to_uea(struct device *dev)
+{
+ struct usb_interface *intf;
+ struct usbatm_data *usbatm;
+
+ intf = to_usb_interface(dev);
+ if (!intf)
+ return NULL;
+
+ usbatm = usb_get_intfdata(intf);
+ if (!usbatm)
+ return NULL;
+
+ return usbatm->driver_data;
+}
+
+static ssize_t read_status(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int ret = -ENODEV;
+ struct uea_softc *sc;
+
+ down(&uea_semaphore);
+ sc = dev_to_uea(dev);
+ if (!sc)
+ goto out;
+ ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.state);
+out:
+ up(&uea_semaphore);
+ return ret;
+}
+
+static ssize_t reboot(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = -ENODEV;
+ struct uea_softc *sc;
+
+ down(&uea_semaphore);
+ sc = dev_to_uea(dev);
+ if (!sc)
+ goto out;
+ sc->reset = 1;
+ ret = count;
+out:
+ up(&uea_semaphore);
+ return ret;
+}
+
+static DEVICE_ATTR(stat_status, S_IWUGO | S_IRUGO, read_status, reboot);
+
+static ssize_t read_human_status(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int ret = -ENODEV;
+ struct uea_softc *sc;
+
+ down(&uea_semaphore);
+ sc = dev_to_uea(dev);
+ if (!sc)
+ goto out;
+
+ switch (GET_STATUS(sc->stats.phy.state)) {
+ case 0:
+ ret = sprintf(buf, "Modem is booting\n");
+ break;
+ case 1:
+ ret = sprintf(buf, "Modem is initializing\n");
+ break;
+ case 2:
+ ret = sprintf(buf, "Modem is operational\n");
+ break;
+ default:
+ ret = sprintf(buf, "Modem synchronization failed\n");
+ break;
+ }
+out:
+ up(&uea_semaphore);
+ return ret;
+}
+
+static DEVICE_ATTR(stat_human_status, S_IWUGO | S_IRUGO, read_human_status, NULL);
+
+static ssize_t read_delin(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int ret = -ENODEV;
+ struct uea_softc *sc;
+
+ down(&uea_semaphore);
+ sc = dev_to_uea(dev);
+ if (!sc)
+ goto out;
+
+ if (sc->stats.phy.flags & 0x0C00)
+ ret = sprintf(buf, "ERROR\n");
+ else if (sc->stats.phy.flags & 0x0030)
+ ret = sprintf(buf, "LOSS\n");
+ else
+ ret = sprintf(buf, "GOOD\n");
+out:
+ up(&uea_semaphore);
+ return ret;
+}
+
+static DEVICE_ATTR(stat_delin, S_IWUGO | S_IRUGO, read_delin, NULL);
+
+#define UEA_ATTR(name, reset) \
+ \
+static ssize_t read_##name(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ int ret = -ENODEV; \
+ struct uea_softc *sc; \
+ \
+ down(&uea_semaphore); \
+ sc = dev_to_uea(dev); \
+ if (!sc) \
+ goto out; \
+ ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.name); \
+ if (reset) \
+ sc->stats.phy.name = 0; \
+out: \
+ up(&uea_semaphore); \
+ return ret; \
+} \
+ \
+static DEVICE_ATTR(stat_##name, S_IRUGO, read_##name, NULL)
+
+UEA_ATTR(mflags, 1);
+UEA_ATTR(vidcpe, 0);
+UEA_ATTR(usrate, 0);
+UEA_ATTR(dsrate, 0);
+UEA_ATTR(usattenuation, 0);
+UEA_ATTR(dsattenuation, 0);
+UEA_ATTR(usmargin, 0);
+UEA_ATTR(dsmargin, 0);
+UEA_ATTR(txflow, 0);
+UEA_ATTR(rxflow, 0);
+UEA_ATTR(uscorr, 0);
+UEA_ATTR(dscorr, 0);
+UEA_ATTR(usunc, 0);
+UEA_ATTR(dsunc, 0);
+
+/* Retrieve the device End System Identifier (MAC) */
+
+#define htoi(x) (isdigit(x) ? x-'0' : toupper(x)-'A'+10)
+static int uea_getesi(struct uea_softc *sc, u_char * esi)
+{
+ unsigned char mac_str[2 * ETH_ALEN + 1];
+ int i;
+ if (usb_string
+ (sc->usb_dev, sc->usb_dev->descriptor.iSerialNumber, mac_str,
+ sizeof(mac_str)) != 2 * ETH_ALEN)
+ return 1;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ esi[i] = htoi(mac_str[2 * i]) * 16 + htoi(mac_str[2 * i + 1]);
+
+ return 0;
+}
+
+/* ATM stuff */
+static int uea_atm_open(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
+{
+ struct uea_softc *sc = usbatm->driver_data;
+
+ return uea_getesi(sc, atm_dev->esi);
+}
+
+static int uea_heavy(struct usbatm_data *usbatm, struct usb_interface *intf)
+{
+ struct uea_softc *sc = usbatm->driver_data;
+
+ wait_event(sc->sync_q, IS_OPERATIONAL(sc));
+
+ return 0;
+
+}
+
+static int claim_interface(struct usb_device *usb_dev,
+ struct usbatm_data *usbatm, int ifnum)
+{
+ int ret;
+ struct usb_interface *intf = usb_ifnum_to_if(usb_dev, ifnum);
+
+ if (!intf) {
+ uea_err(usb_dev, "interface %d not found\n", ifnum);
+ return -ENODEV;
+ }
+
+ ret = usb_driver_claim_interface(&uea_driver, intf, usbatm);
+ if (ret != 0)
+ uea_err(usb_dev, "can't claim interface %d, error %d\n", ifnum,
+ ret);
+ return ret;
+}
+
+static void create_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+{
+ /* sysfs interface */
+ device_create_file(&intf->dev, &dev_attr_stat_status);
+ device_create_file(&intf->dev, &dev_attr_stat_mflags);
+ device_create_file(&intf->dev, &dev_attr_stat_human_status);
+ device_create_file(&intf->dev, &dev_attr_stat_delin);
+ device_create_file(&intf->dev, &dev_attr_stat_vidcpe);
+ device_create_file(&intf->dev, &dev_attr_stat_usrate);
+ device_create_file(&intf->dev, &dev_attr_stat_dsrate);
+ device_create_file(&intf->dev, &dev_attr_stat_usattenuation);
+ device_create_file(&intf->dev, &dev_attr_stat_dsattenuation);
+ device_create_file(&intf->dev, &dev_attr_stat_usmargin);
+ device_create_file(&intf->dev, &dev_attr_stat_dsmargin);
+ device_create_file(&intf->dev, &dev_attr_stat_txflow);
+ device_create_file(&intf->dev, &dev_attr_stat_rxflow);
+ device_create_file(&intf->dev, &dev_attr_stat_uscorr);
+ device_create_file(&intf->dev, &dev_attr_stat_dscorr);
+ device_create_file(&intf->dev, &dev_attr_stat_usunc);
+ device_create_file(&intf->dev, &dev_attr_stat_dsunc);
+}
+
+static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
+ const struct usb_device_id *id, int *heavy)
+{
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct uea_softc *sc;
+ int ret, ifnum = intf->altsetting->desc.bInterfaceNumber;
+
+ uea_enters(usb);
+
+ /* interface 0 is for firmware/monitoring */
+ if (ifnum != UEA_INTR_IFACE_NO)
+ return -ENODEV;
+
+ *heavy = sync_wait[modem_index];
+
+ /* interface 1 is for outbound traffic */
+ ret = claim_interface(usb, usbatm, UEA_US_IFACE_NO);
+ if (ret < 0)
+ return ret;
+
+ /* ADI930 has only 2 interfaces and inbound traffic
+ * is on interface 1
+ */
+ if (UEA_CHIP_VERSION(id) != ADI930) {
+ /* interface 2 is for inbound traffic */
+ ret = claim_interface(usb, usbatm, UEA_DS_IFACE_NO);
+ if (ret < 0)
+ return ret;
+ }
+
+ sc = kzalloc(sizeof(struct uea_softc), GFP_KERNEL);
+ if (!sc) {
+ uea_err(INS_TO_USBDEV(sc), "uea_init: not enough memory !\n");
+ return -ENOMEM;
+ }
+
+ sc->usb_dev = usb;
+ usbatm->driver_data = sc;
+ sc->usbatm = usbatm;
+ sc->modem_index = (modem_index < NB_MODEM) ? modem_index++ : 0;
+ sc->driver_info = id->driver_info;
+
+ ret = uea_boot(sc);
+ if (ret < 0) {
+ kfree(sc);
+ return ret;
+ }
+
+ create_fs_entries(sc, intf);
+ return 0;
+}
+
+static void destroy_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+{
+ /* sysfs interface */
+ device_remove_file(&intf->dev, &dev_attr_stat_status);
+ device_remove_file(&intf->dev, &dev_attr_stat_mflags);
+ device_remove_file(&intf->dev, &dev_attr_stat_human_status);
+ device_remove_file(&intf->dev, &dev_attr_stat_delin);
+ device_remove_file(&intf->dev, &dev_attr_stat_vidcpe);
+ device_remove_file(&intf->dev, &dev_attr_stat_usrate);
+ device_remove_file(&intf->dev, &dev_attr_stat_dsrate);
+ device_remove_file(&intf->dev, &dev_attr_stat_usattenuation);
+ device_remove_file(&intf->dev, &dev_attr_stat_dsattenuation);
+ device_remove_file(&intf->dev, &dev_attr_stat_usmargin);
+ device_remove_file(&intf->dev, &dev_attr_stat_dsmargin);
+ device_remove_file(&intf->dev, &dev_attr_stat_txflow);
+ device_remove_file(&intf->dev, &dev_attr_stat_rxflow);
+ device_remove_file(&intf->dev, &dev_attr_stat_uscorr);
+ device_remove_file(&intf->dev, &dev_attr_stat_dscorr);
+ device_remove_file(&intf->dev, &dev_attr_stat_usunc);
+ device_remove_file(&intf->dev, &dev_attr_stat_dsunc);
+}
+
+static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
+{
+ struct uea_softc *sc = usbatm->driver_data;
+
+ destroy_fs_entries(sc, intf);
+ uea_stop(sc);
+ kfree(sc);
+}
+
+static struct usbatm_driver uea_usbatm_driver = {
+ .driver_name = "ueagle-atm",
+ .owner = THIS_MODULE,
+ .bind = uea_bind,
+ .atm_start = uea_atm_open,
+ .unbind = uea_unbind,
+ .heavy_init = uea_heavy,
+ .in = UEA_BULK_DATA_PIPE,
+ .out = UEA_BULK_DATA_PIPE,
+};
+
+static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *usb = interface_to_usbdev(intf);
+
+ uea_enters(usb);
+ uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) : %s\n",
+ le16_to_cpu(usb->descriptor.idVendor),
+ le16_to_cpu(usb->descriptor.idProduct),
+ chip_name[UEA_CHIP_VERSION(id)]);
+
+ usb_reset_device(usb);
+
+ if (UEA_IS_PREFIRM(id))
+ return uea_load_firmware(usb, UEA_CHIP_VERSION(id));
+
+ return usbatm_usb_probe(intf, id, &uea_usbatm_driver);
+}
+
+static void uea_disconnect(struct usb_interface *intf)
+{
+ struct usb_device *usb = interface_to_usbdev(intf);
+ int ifnum = intf->altsetting->desc.bInterfaceNumber;
+ uea_enters(usb);
+
+ /* ADI930 has 2 interfaces and eagle 3 interfaces.
+ * Pre-firmware device has one interface
+ */
+ if (usb->config->desc.bNumInterfaces != 1 && ifnum == 0) {
+ down(&uea_semaphore);
+ usbatm_usb_disconnect(intf);
+ up(&uea_semaphore);
+ uea_info(usb, "ADSL device removed\n");
+ }
+
+ uea_leaves(usb);
+}
+
+/*
+ * List of supported VID/PID
+ */
+static const struct usb_device_id uea_ids[] = {
+ {USB_DEVICE(ELSA_VID, ELSA_PID_PREFIRM), .driver_info = ADI930 | PREFIRM},
+ {USB_DEVICE(ELSA_VID, ELSA_PID_PSTFIRM), .driver_info = ADI930 | PSTFIRM},
+ {USB_DEVICE(EAGLE_VID, EAGLE_I_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(EAGLE_VID, EAGLE_I_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM},
+ {USB_DEVICE(EAGLE_VID, EAGLE_II_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
+ {USB_DEVICE(EAGLE_VID, EAGLE_II_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM},
+ {USB_DEVICE(EAGLE_VID, EAGLE_IIC_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM},
+ {USB_DEVICE(EAGLE_VID, EAGLE_IIC_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM},
+ {USB_DEVICE(EAGLE_VID, EAGLE_III_PID_PREFIRM), .driver_info = EAGLE_III | PREFIRM},
+ {USB_DEVICE(EAGLE_VID, EAGLE_III_PID_PSTFIRM), .driver_info = EAGLE_III | PSTFIRM},
+ {USB_DEVICE(USR_VID, MILLER_A_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(USR_VID, MILLER_A_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM},
+ {USB_DEVICE(USR_VID, MILLER_B_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(USR_VID, MILLER_B_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM},
+ {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM},
+ {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM},
+ {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM},
+ {}
+};
+
+/*
+ * USB driver descriptor
+ */
+static struct usb_driver uea_driver = {
+ .name = "ueagle-atm",
+ .id_table = uea_ids,
+ .probe = uea_probe,
+ .disconnect = uea_disconnect,
+};
+
+MODULE_DEVICE_TABLE(usb, uea_ids);
+
+/**
+ * uea_init - Initialize the module.
+ * Register to USB subsystem
+ */
+static int __init uea_init(void)
+{
+ printk(KERN_INFO "[ueagle-atm] driver " EAGLEUSBVERSION " loaded\n");
+
+ usb_register(&uea_driver);
+
+ return 0;
+}
+
+module_init(uea_init);
+
+/**
+ * uea_exit - Destroy module
+ * Deregister with USB subsystem
+ */
+static void __exit uea_exit(void)
+{
+ /*
+ * This calls automatically the uea_disconnect method if necessary:
+ */
+ usb_deregister(&uea_driver);
+
+ printk(KERN_INFO "[ueagle-atm] driver unloaded\n");
+}
+
+module_exit(uea_exit);
+
+MODULE_AUTHOR("Damien Bergamini/Matthieu Castet/Stanislaw W. Gruszka");
+MODULE_DESCRIPTION("ADI 930/Eagle USB ADSL Modem driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index 2e6593e6c1bd..9baa6296fc95 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -646,14 +646,14 @@ static void usbatm_destroy_instance(struct kref *kref)
kfree(instance);
}
-void usbatm_get_instance(struct usbatm_data *instance)
+static void usbatm_get_instance(struct usbatm_data *instance)
{
dbg("%s", __func__);
kref_get(&instance->refcount);
}
-void usbatm_put_instance(struct usbatm_data *instance)
+static void usbatm_put_instance(struct usbatm_data *instance)
{
dbg("%s", __func__);
diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c
index 7fe7fb484d10..5c76e3aaaa5e 100644
--- a/drivers/usb/atm/xusbatm.c
+++ b/drivers/usb/atm/xusbatm.c
@@ -140,7 +140,6 @@ static int xusbatm_usb_probe(struct usb_interface *intf,
}
static struct usb_driver xusbatm_usb_driver = {
- .owner = THIS_MODULE,
.name = xusbatm_driver_name,
.probe = xusbatm_usb_probe,
.disconnect = usbatm_usb_disconnect,
diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c
index 50858273f8d3..3ad9ee8b84a9 100644
--- a/drivers/usb/class/audio.c
+++ b/drivers/usb/class/audio.c
@@ -2732,7 +2732,6 @@ static struct usb_device_id usb_audio_ids [] = {
MODULE_DEVICE_TABLE (usb, usb_audio_ids);
static struct usb_driver usb_audio_driver = {
- .owner = THIS_MODULE,
.name = "audio",
.probe = usb_audio_probe,
.disconnect = usb_audio_disconnect,
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 1b4751412970..248279e44c99 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -6,6 +6,7 @@
* Copyright (c) 1999 Johannes Erdfelt <johannes@erdfelt.com>
* Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2004 Oliver Neukum <oliver@neukum.name>
+ * Copyright (c) 2005 David Kubicek <dave@awk.cz>
*
* USB Abstract Control Model driver for USB modems and ISDN adapters
*
@@ -29,6 +30,7 @@
* config we want, sysadmin changes bConfigurationValue in sysfs.
* v0.23 - use softirq for rx processing, as needed by tty layer
* v0.24 - change probe method to evaluate CDC union descriptor
+ * v0.25 - downstream tasks paralelized to maximize throughput
*/
/*
@@ -63,14 +65,15 @@
#include <linux/usb_cdc.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
+#include <linux/list.h>
#include "cdc-acm.h"
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.23"
-#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik"
+#define DRIVER_VERSION "v0.25"
+#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
static struct usb_driver acm_driver;
@@ -284,7 +287,9 @@ exit:
/* data interface returns incoming bytes, or we got unthrottled */
static void acm_read_bulk(struct urb *urb, struct pt_regs *regs)
{
- struct acm *acm = urb->context;
+ struct acm_rb *buf;
+ struct acm_ru *rcv = urb->context;
+ struct acm *acm = rcv->instance;
dbg("Entering acm_read_bulk with status %d\n", urb->status);
if (!ACM_READY(acm))
@@ -293,49 +298,109 @@ static void acm_read_bulk(struct urb *urb, struct pt_regs *regs)
if (urb->status)
dev_dbg(&acm->data->dev, "bulk rx status %d\n", urb->status);
- /* calling tty_flip_buffer_push() in_irq() isn't allowed */
- tasklet_schedule(&acm->bh);
+ buf = rcv->buffer;
+ buf->size = urb->actual_length;
+
+ spin_lock(&acm->read_lock);
+ list_add_tail(&rcv->list, &acm->spare_read_urbs);
+ list_add_tail(&buf->list, &acm->filled_read_bufs);
+ spin_unlock(&acm->read_lock);
+
+ tasklet_schedule(&acm->urb_task);
}
static void acm_rx_tasklet(unsigned long _acm)
{
struct acm *acm = (void *)_acm;
- struct urb *urb = acm->readurb;
+ struct acm_rb *buf;
struct tty_struct *tty = acm->tty;
- unsigned char *data = urb->transfer_buffer;
+ struct acm_ru *rcv;
+ //unsigned long flags;
int i = 0;
dbg("Entering acm_rx_tasklet");
- if (urb->actual_length > 0 && !acm->throttle) {
- for (i = 0; i < urb->actual_length && !acm->throttle; i++) {
- /* if we insert more than TTY_FLIPBUF_SIZE characters,
- * we drop them. */
- if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
- tty_flip_buffer_push(tty);
- }
- tty_insert_flip_char(tty, data[i], 0);
- }
- dbg("Handed %d bytes to tty layer", i+1);
- tty_flip_buffer_push(tty);
+ if (!ACM_READY(acm) || acm->throttle)
+ return;
+
+next_buffer:
+ spin_lock(&acm->read_lock);
+ if (list_empty(&acm->filled_read_bufs)) {
+ spin_unlock(&acm->read_lock);
+ goto urbs;
}
+ buf = list_entry(acm->filled_read_bufs.next,
+ struct acm_rb, list);
+ list_del(&buf->list);
+ spin_unlock(&acm->read_lock);
+
+ dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d\n", buf, buf->size);
+
+ for (i = 0; i < buf->size && !acm->throttle; i++) {
+ /* if we insert more than TTY_FLIPBUF_SIZE characters,
+ we drop them. */
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty_flip_buffer_push(tty);
+ }
+ tty_insert_flip_char(tty, buf->base[i], 0);
+ }
+ tty_flip_buffer_push(tty);
spin_lock(&acm->throttle_lock);
if (acm->throttle) {
dbg("Throtteling noticed");
- memmove(data, data + i, urb->actual_length - i);
- urb->actual_length -= i;
- acm->resubmit_to_unthrottle = 1;
+ memmove(buf->base, buf->base + i, buf->size - i);
+ buf->size -= i;
spin_unlock(&acm->throttle_lock);
+ spin_lock(&acm->read_lock);
+ list_add(&buf->list, &acm->filled_read_bufs);
+ spin_unlock(&acm->read_lock);
return;
}
spin_unlock(&acm->throttle_lock);
- urb->actual_length = 0;
- urb->dev = acm->dev;
-
- i = usb_submit_urb(urb, GFP_ATOMIC);
- if (i)
- dev_dbg(&acm->data->dev, "bulk rx resubmit %d\n", i);
+ spin_lock(&acm->read_lock);
+ list_add(&buf->list, &acm->spare_read_bufs);
+ spin_unlock(&acm->read_lock);
+ goto next_buffer;
+
+urbs:
+ while (!list_empty(&acm->spare_read_bufs)) {
+ spin_lock(&acm->read_lock);
+ if (list_empty(&acm->spare_read_urbs)) {
+ spin_unlock(&acm->read_lock);
+ return;
+ }
+ rcv = list_entry(acm->spare_read_urbs.next,
+ struct acm_ru, list);
+ list_del(&rcv->list);
+ spin_unlock(&acm->read_lock);
+
+ buf = list_entry(acm->spare_read_bufs.next,
+ struct acm_rb, list);
+ list_del(&buf->list);
+
+ rcv->buffer = buf;
+
+ usb_fill_bulk_urb(rcv->urb, acm->dev,
+ acm->rx_endpoint,
+ buf->base,
+ acm->readsize,
+ acm_read_bulk, rcv);
+ rcv->urb->transfer_dma = buf->dma;
+ rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p\n", rcv->urb, rcv, buf);
+
+ /* This shouldn't kill the driver as unsuccessful URBs are returned to the
+ free-urbs-pool and resubmited ASAP */
+ if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
+ list_add(&buf->list, &acm->spare_read_bufs);
+ spin_lock(&acm->read_lock);
+ list_add(&rcv->list, &acm->spare_read_urbs);
+ spin_unlock(&acm->read_lock);
+ return;
+ }
+ }
}
/* data interface wrote those outgoing bytes */
@@ -369,6 +434,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
{
struct acm *acm;
int rv = -EINVAL;
+ int i;
dbg("Entering acm_tty_open.\n");
down(&open_sem);
@@ -382,7 +448,9 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
tty->driver_data = acm;
acm->tty = tty;
-
+ /* force low_latency on so that our tty_push actually forces the data through,
+ otherwise it is scheduled, and with high data rates data can get lost. */
+ tty->low_latency = 1;
if (acm->used++) {
goto done;
@@ -394,18 +462,20 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
goto bail_out;
}
- acm->readurb->dev = acm->dev;
- if (usb_submit_urb(acm->readurb, GFP_KERNEL)) {
- dbg("usb_submit_urb(read bulk) failed");
- goto bail_out_and_unlink;
- }
-
if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS))
goto full_bailout;
- /* force low_latency on so that our tty_push actually forces the data through,
- otherwise it is scheduled, and with high data rates data can get lost. */
- tty->low_latency = 1;
+ INIT_LIST_HEAD(&acm->spare_read_urbs);
+ INIT_LIST_HEAD(&acm->spare_read_bufs);
+ INIT_LIST_HEAD(&acm->filled_read_bufs);
+ for (i = 0; i < ACM_NRU; i++) {
+ list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
+ }
+ for (i = 0; i < ACM_NRB; i++) {
+ list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
+ }
+
+ tasklet_schedule(&acm->urb_task);
done:
err_out:
@@ -413,8 +483,6 @@ err_out:
return rv;
full_bailout:
- usb_kill_urb(acm->readurb);
-bail_out_and_unlink:
usb_kill_urb(acm->ctrlurb);
bail_out:
acm->used--;
@@ -424,18 +492,22 @@ bail_out:
static void acm_tty_unregister(struct acm *acm)
{
+ int i;
+
tty_unregister_device(acm_tty_driver, acm->minor);
usb_put_intf(acm->control);
acm_table[acm->minor] = NULL;
usb_free_urb(acm->ctrlurb);
- usb_free_urb(acm->readurb);
usb_free_urb(acm->writeurb);
+ for (i = 0; i < ACM_NRU; i++)
+ usb_free_urb(acm->ru[i].urb);
kfree(acm);
}
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
{
struct acm *acm = tty->driver_data;
+ int i;
if (!acm || !acm->used)
return;
@@ -446,7 +518,8 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
acm_set_control(acm, acm->ctrlout = 0);
usb_kill_urb(acm->ctrlurb);
usb_kill_urb(acm->writeurb);
- usb_kill_urb(acm->readurb);
+ for (i = 0; i < ACM_NRU; i++)
+ usb_kill_urb(acm->ru[i].urb);
} else
acm_tty_unregister(acm);
}
@@ -528,10 +601,7 @@ static void acm_tty_unthrottle(struct tty_struct *tty)
spin_lock_bh(&acm->throttle_lock);
acm->throttle = 0;
spin_unlock_bh(&acm->throttle_lock);
- if (acm->resubmit_to_unthrottle) {
- acm->resubmit_to_unthrottle = 0;
- acm_read_bulk(acm->readurb, NULL);
- }
+ tasklet_schedule(&acm->urb_task);
}
static void acm_tty_break_ctl(struct tty_struct *tty, int state)
@@ -588,7 +658,7 @@ static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int
return -ENOIOCTLCMD;
}
-static __u32 acm_tty_speed[] = {
+static const __u32 acm_tty_speed[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600,
1200, 1800, 2400, 4800, 9600, 19200, 38400,
57600, 115200, 230400, 460800, 500000, 576000,
@@ -596,7 +666,7 @@ static __u32 acm_tty_speed[] = {
2500000, 3000000, 3500000, 4000000
};
-static __u8 acm_tty_size[] = {
+static const __u8 acm_tty_size[] = {
5, 6, 7, 8
};
@@ -694,6 +764,7 @@ static int acm_probe (struct usb_interface *intf,
int call_interface_num = -1;
int data_interface_num;
unsigned long quirks;
+ int i;
/* handle quirks deadly to normal probing*/
quirks = (unsigned long)id->driver_info;
@@ -833,7 +904,7 @@ skip_normal_probe:
}
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
- readsize = le16_to_cpu(epread->wMaxPacketSize);
+ readsize = le16_to_cpu(epread->wMaxPacketSize)*2;
acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize);
acm->control = control_interface;
acm->data = data_interface;
@@ -842,12 +913,14 @@ skip_normal_probe:
acm->ctrl_caps = ac_management_function;
acm->ctrlsize = ctrlsize;
acm->readsize = readsize;
- acm->bh.func = acm_rx_tasklet;
- acm->bh.data = (unsigned long) acm;
+ acm->urb_task.func = acm_rx_tasklet;
+ acm->urb_task.data = (unsigned long) acm;
INIT_WORK(&acm->work, acm_softint, acm);
spin_lock_init(&acm->throttle_lock);
spin_lock_init(&acm->write_lock);
+ spin_lock_init(&acm->read_lock);
acm->write_ready = 1;
+ acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
if (!buf) {
@@ -856,13 +929,6 @@ skip_normal_probe:
}
acm->ctrl_buffer = buf;
- buf = usb_buffer_alloc(usb_dev, readsize, GFP_KERNEL, &acm->read_dma);
- if (!buf) {
- dev_dbg(&intf->dev, "out of memory (read buffer alloc)\n");
- goto alloc_fail3;
- }
- acm->read_buffer = buf;
-
if (acm_write_buffers_alloc(acm) < 0) {
dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
goto alloc_fail4;
@@ -873,10 +939,25 @@ skip_normal_probe:
dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
goto alloc_fail5;
}
- acm->readurb = usb_alloc_urb(0, GFP_KERNEL);
- if (!acm->readurb) {
- dev_dbg(&intf->dev, "out of memory (readurb kmalloc)\n");
- goto alloc_fail6;
+ for (i = 0; i < ACM_NRU; i++) {
+ struct acm_ru *rcv = &(acm->ru[i]);
+
+ if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
+ dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n");
+ goto alloc_fail7;
+ }
+
+ rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ rcv->instance = acm;
+ }
+ for (i = 0; i < ACM_NRB; i++) {
+ struct acm_rb *buf = &(acm->rb[i]);
+
+ // Using usb_buffer_alloc instead of kmalloc as Oliver suggested
+ if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
+ dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
+ goto alloc_fail7;
+ }
}
acm->writeurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->writeurb) {
@@ -889,15 +970,9 @@ skip_normal_probe:
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
acm->ctrlurb->transfer_dma = acm->ctrl_dma;
- usb_fill_bulk_urb(acm->readurb, usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress),
- acm->read_buffer, readsize, acm_read_bulk, acm);
- acm->readurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
- acm->readurb->transfer_dma = acm->read_dma;
-
usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
NULL, acm->writesize, acm_write_bulk, acm);
acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
- /* acm->writeurb->transfer_dma = 0; */
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
@@ -917,14 +992,14 @@ skip_normal_probe:
return 0;
alloc_fail7:
- usb_free_urb(acm->readurb);
-alloc_fail6:
+ for (i = 0; i < ACM_NRB; i++)
+ usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
+ for (i = 0; i < ACM_NRU; i++)
+ usb_free_urb(acm->ru[i].urb);
usb_free_urb(acm->ctrlurb);
alloc_fail5:
acm_write_buffers_free(acm);
alloc_fail4:
- usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma);
-alloc_fail3:
usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
alloc_fail2:
kfree(acm);
@@ -936,6 +1011,7 @@ static void acm_disconnect(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata (intf);
struct usb_device *usb_dev = interface_to_usbdev(intf);
+ int i;
if (!acm || !acm->dev) {
dbg("disconnect on nonexisting interface");
@@ -946,15 +1022,24 @@ static void acm_disconnect(struct usb_interface *intf)
acm->dev = NULL;
usb_set_intfdata (intf, NULL);
+ tasklet_disable(&acm->urb_task);
+
usb_kill_urb(acm->ctrlurb);
- usb_kill_urb(acm->readurb);
usb_kill_urb(acm->writeurb);
+ for (i = 0; i < ACM_NRU; i++)
+ usb_kill_urb(acm->ru[i].urb);
+
+ INIT_LIST_HEAD(&acm->filled_read_bufs);
+ INIT_LIST_HEAD(&acm->spare_read_bufs);
+
+ tasklet_enable(&acm->urb_task);
flush_scheduled_work(); /* wait for acm_softint */
acm_write_buffers_free(acm);
- usb_buffer_free(usb_dev, acm->readsize, acm->read_buffer, acm->read_dma);
usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
+ for (i = 0; i < ACM_NRB; i++)
+ usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
usb_driver_release_interface(&acm_driver, acm->data);
@@ -1003,7 +1088,6 @@ static struct usb_device_id acm_ids[] = {
MODULE_DEVICE_TABLE (usb, acm_ids);
static struct usb_driver acm_driver = {
- .owner = THIS_MODULE,
.name = "cdc_acm",
.probe = acm_probe,
.disconnect = acm_disconnect,
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 963a5dfd2096..fd2aaccdcbac 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -59,6 +59,9 @@
* when processing onlcr, so we only need 2 buffers.
*/
#define ACM_NWB 2
+#define ACM_NRU 16
+#define ACM_NRB 16
+
struct acm_wb {
unsigned char *buf;
dma_addr_t dmah;
@@ -66,22 +69,43 @@ struct acm_wb {
int use;
};
+struct acm_rb {
+ struct list_head list;
+ int size;
+ unsigned char *base;
+ dma_addr_t dma;
+};
+
+struct acm_ru {
+ struct list_head list;
+ struct acm_rb *buffer;
+ struct urb *urb;
+ struct acm *instance;
+};
+
struct acm {
struct usb_device *dev; /* the corresponding usb device */
struct usb_interface *control; /* control interface */
struct usb_interface *data; /* data interface */
struct tty_struct *tty; /* the corresponding tty */
- struct urb *ctrlurb, *readurb, *writeurb; /* urbs */
- u8 *ctrl_buffer, *read_buffer; /* buffers of urbs */
- dma_addr_t ctrl_dma, read_dma; /* dma handles of buffers */
+ struct urb *ctrlurb, *writeurb; /* urbs */
+ u8 *ctrl_buffer; /* buffers of urbs */
+ dma_addr_t ctrl_dma; /* dma handles of buffers */
struct acm_wb wb[ACM_NWB];
+ struct acm_ru ru[ACM_NRU];
+ struct acm_rb rb[ACM_NRB];
+ int rx_endpoint;
+ spinlock_t read_lock;
+ struct list_head spare_read_urbs;
+ struct list_head spare_read_bufs;
+ struct list_head filled_read_bufs;
int write_current; /* current write buffer */
int write_used; /* number of non-empty write buffers */
int write_ready; /* write urb is not running */
spinlock_t write_lock;
struct usb_cdc_line_coding line; /* bits, stop, parity */
struct work_struct work; /* work queue entry for line discipline waking up */
- struct tasklet_struct bh; /* rx processing */
+ struct tasklet_struct urb_task; /* rx processing */
spinlock_t throttle_lock; /* synchronize throtteling and read callback */
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
unsigned int ctrlout; /* output control lines (DTR, RTS) */
@@ -91,7 +115,6 @@ struct acm {
unsigned int minor; /* acm minor number */
unsigned char throttle; /* throttled by tty layer */
unsigned char clocal; /* termios CLOCAL */
- unsigned char resubmit_to_unthrottle; /* throtteling has disabled the read urb */
unsigned int ctrl_caps; /* control capabilities from the class specific header */
};
diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c
index 5f8af35e7633..f13f004d311f 100644
--- a/drivers/usb/class/usb-midi.c
+++ b/drivers/usb/class/usb-midi.c
@@ -2027,7 +2027,6 @@ static struct usb_device_id id_table[] = {
};
static struct usb_driver usb_midi_driver = {
- .owner = THIS_MODULE,
.name = "midi",
.probe = usb_midi_probe,
.disconnect = usb_midi_disconnect,
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index 357e75335f17..27e9404547f3 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -199,7 +199,7 @@ struct quirk_printer_struct {
#define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires unidirectional mode (no INs/reads) */
#define USBLP_QUIRK_USB_INIT 0x2 /* needs vendor USB init string */
-static struct quirk_printer_struct quirk_printers[] = {
+static const struct quirk_printer_struct quirk_printers[] = {
{ 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */
{ 0x03f0, 0x0104, USBLP_QUIRK_BIDIR }, /* HP DeskJet 880C */
{ 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */
@@ -301,7 +301,7 @@ static void usblp_bulk_write(struct urb *urb, struct pt_regs *regs)
* Get and print printer errors.
*/
-static char *usblp_messages[] = { "ok", "out of paper", "off-line", "on fire" };
+static const char *usblp_messages[] = { "ok", "out of paper", "off-line", "on fire" };
static int usblp_check_status(struct usblp *usblp, int err)
{
@@ -438,7 +438,7 @@ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait
| (!usblp->wcomplete ? 0 : POLLOUT | POLLWRNORM);
}
-static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct usblp *usblp = file->private_data;
int length, err, i;
@@ -838,7 +838,8 @@ static struct file_operations usblp_fops = {
.read = usblp_read,
.write = usblp_write,
.poll = usblp_poll,
- .ioctl = usblp_ioctl,
+ .unlocked_ioctl = usblp_ioctl,
+ .compat_ioctl = usblp_ioctl,
.open = usblp_open,
.release = usblp_release,
};
@@ -849,6 +850,20 @@ static struct usb_class_driver usblp_class = {
.minor_base = USBLP_MINOR_BASE,
};
+static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usblp *usblp = usb_get_intfdata (intf);
+
+ if (usblp->device_id_string[0] == 0 &&
+ usblp->device_id_string[1] == 0)
+ return 0;
+
+ return sprintf(buf, "%s", usblp->device_id_string+2);
+}
+
+static DEVICE_ATTR(ieee1284_id, S_IRUGO, usblp_show_ieee1284_id, NULL);
+
static int usblp_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -933,20 +948,12 @@ static int usblp_probe(struct usb_interface *intf,
/* Retrieve and store the device ID string. */
usblp_cache_device_id_string(usblp);
+ device_create_file(&intf->dev, &dev_attr_ieee1284_id);
#ifdef DEBUG
usblp_check_status(usblp, 0);
#endif
- info("usblp%d: USB %sdirectional printer dev %d "
- "if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X",
- usblp->minor, usblp->bidir ? "Bi" : "Uni", dev->devnum,
- usblp->ifnum,
- usblp->protocol[usblp->current_protocol].alt_setting,
- usblp->current_protocol,
- le16_to_cpu(usblp->dev->descriptor.idVendor),
- le16_to_cpu(usblp->dev->descriptor.idProduct));
-
usb_set_intfdata (intf, usblp);
usblp->present = 1;
@@ -957,11 +964,20 @@ static int usblp_probe(struct usb_interface *intf,
goto abort_intfdata;
}
usblp->minor = intf->minor;
+ info("usblp%d: USB %sdirectional printer dev %d "
+ "if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X",
+ usblp->minor, usblp->bidir ? "Bi" : "Uni", dev->devnum,
+ usblp->ifnum,
+ usblp->protocol[usblp->current_protocol].alt_setting,
+ usblp->current_protocol,
+ le16_to_cpu(usblp->dev->descriptor.idVendor),
+ le16_to_cpu(usblp->dev->descriptor.idProduct));
return 0;
abort_intfdata:
usb_set_intfdata (intf, NULL);
+ device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
abort:
if (usblp) {
if (usblp->writebuf)
@@ -1156,6 +1172,8 @@ static void usblp_disconnect(struct usb_interface *intf)
BUG ();
}
+ device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
+
down (&usblp_sem);
down (&usblp->sem);
usblp->present = 0;
@@ -1186,7 +1204,6 @@ static struct usb_device_id usblp_ids [] = {
MODULE_DEVICE_TABLE (usb, usblp_ids);
static struct usb_driver usblp_driver = {
- .owner = THIS_MODULE,
.name = "usblp",
.probe = usblp_probe,
.disconnect = usblp_disconnect,
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index 86d5c380892d..28329ddf187c 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -2,7 +2,7 @@
# Makefile for USB Core files and filesystem
#
-usbcore-objs := usb.o hub.o hcd.o urb.o message.o \
+usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
config.o file.o buffer.o sysfs.o devio.o notify.o
ifeq ($(CONFIG_PCI),y)
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
index 419c9943a7cb..ad742cec94fa 100644
--- a/drivers/usb/core/buffer.c
+++ b/drivers/usb/core/buffer.c
@@ -55,6 +55,9 @@ int hcd_buffer_create (struct usb_hcd *hcd)
char name [16];
int i, size;
+ if (!hcd->self.controller->dma_mask)
+ return 0;
+
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
if (!(size = pool_max [i]))
continue;
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 83e815d3cd52..2684e15b813b 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -67,45 +67,45 @@
/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
#define ALLOW_SERIAL_NUMBER
-static char *format_topo =
+static const char *format_topo =
/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */
"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n";
-static char *format_string_manufacturer =
+static const char *format_string_manufacturer =
/* S: Manufacturer=xxxx */
"S: Manufacturer=%.100s\n";
-static char *format_string_product =
+static const char *format_string_product =
/* S: Product=xxxx */
"S: Product=%.100s\n";
#ifdef ALLOW_SERIAL_NUMBER
-static char *format_string_serialnumber =
+static const char *format_string_serialnumber =
/* S: SerialNumber=xxxx */
"S: SerialNumber=%.100s\n";
#endif
-static char *format_bandwidth =
+static const char *format_bandwidth =
/* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */
"B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n";
-static char *format_device1 =
+static const char *format_device1 =
/* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */
"D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n";
-static char *format_device2 =
+static const char *format_device2 =
/* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */
"P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n";
-static char *format_config =
+static const char *format_config =
/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
"C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
-static char *format_iface =
+static const char *format_iface =
/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
"I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
-static char *format_endpt =
+static const char *format_endpt =
/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */
"E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n";
@@ -545,10 +545,10 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *ski
struct usb_device *childdev = usbdev->children[chix];
if (childdev) {
- down(&childdev->serialize);
+ usb_lock_device(childdev);
ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, childdev,
bus, level + 1, chix, ++cnt);
- up(&childdev->serialize);
+ usb_unlock_device(childdev);
if (ret == -EFAULT)
return total_written;
total_written += ret;
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index b1d6e9af732d..2b68998fe4b3 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -402,7 +402,6 @@ static void driver_disconnect(struct usb_interface *intf)
}
struct usb_driver usbfs_driver = {
- .owner = THIS_MODULE,
.name = "usbfs",
.probe = driver_probe,
.disconnect = driver_disconnect,
@@ -1350,9 +1349,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
/* let kernel drivers try to (re)bind to the interface */
case USBDEVFS_CONNECT:
usb_unlock_device(ps->dev);
- usb_lock_all_devices();
bus_rescan_devices(intf->dev.bus);
- usb_unlock_all_devices();
usb_lock_device(ps->dev);
break;
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
new file mode 100644
index 000000000000..076462c8ba2a
--- /dev/null
+++ b/drivers/usb/core/driver.c
@@ -0,0 +1,472 @@
+/*
+ * drivers/usb/driver.c - most of the driver model stuff for usb
+ *
+ * (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * based on drivers/usb/usb.c which had the following copyrights:
+ * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999-2001
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright David Brownell 2000-2004
+ * (C) Copyright Yggdrasil Computing, Inc. 2000
+ * (usb_device_id matching changes by Adam J. Richter)
+ * (C) Copyright Greg Kroah-Hartman 2002-2003
+ *
+ * NOTE! This is not actually a driver at all, rather this is
+ * just a collection of helper routines that implement the
+ * generic USB things that the real drivers can use..
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/device.h>
+#include <linux/usb.h>
+#include "hcd.h"
+#include "usb.h"
+
+static int usb_match_one_id(struct usb_interface *interface,
+ const struct usb_device_id *id);
+
+struct usb_dynid {
+ struct list_head node;
+ struct usb_device_id id;
+};
+
+
+static int generic_probe(struct device *dev)
+{
+ return 0;
+}
+static int generic_remove(struct device *dev)
+{
+ struct usb_device *udev = to_usb_device(dev);
+
+ /* if this is only an unbind, not a physical disconnect, then
+ * unconfigure the device */
+ if (udev->state == USB_STATE_CONFIGURED)
+ usb_set_configuration(udev, 0);
+
+ /* in case the call failed or the device was suspended */
+ if (udev->state >= USB_STATE_CONFIGURED)
+ usb_disable_device(udev, 0);
+ return 0;
+}
+
+struct device_driver usb_generic_driver = {
+ .owner = THIS_MODULE,
+ .name = "usb",
+ .bus = &usb_bus_type,
+ .probe = generic_probe,
+ .remove = generic_remove,
+};
+
+/* Fun hack to determine if the struct device is a
+ * usb device or a usb interface. */
+int usb_generic_driver_data;
+
+#ifdef CONFIG_HOTPLUG
+
+/*
+ * Adds a new dynamic USBdevice ID to this driver,
+ * and cause the driver to probe for all devices again.
+ */
+static ssize_t store_new_id(struct device_driver *driver,
+ const char *buf, size_t count)
+{
+ struct usb_driver *usb_drv = to_usb_driver(driver);
+ struct usb_dynid *dynid;
+ u32 idVendor = 0;
+ u32 idProduct = 0;
+ int fields = 0;
+
+ fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
+ if (fields < 2)
+ return -EINVAL;
+
+ dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
+ if (!dynid)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&dynid->node);
+ dynid->id.idVendor = idVendor;
+ dynid->id.idProduct = idProduct;
+ dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE;
+
+ spin_lock(&usb_drv->dynids.lock);
+ list_add_tail(&usb_drv->dynids.list, &dynid->node);
+ spin_unlock(&usb_drv->dynids.lock);
+
+ if (get_driver(driver)) {
+ driver_attach(driver);
+ put_driver(driver);
+ }
+
+ return count;
+}
+static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
+
+static int usb_create_newid_file(struct usb_driver *usb_drv)
+{
+ int error = 0;
+
+ if (usb_drv->no_dynamic_id)
+ goto exit;
+
+ if (usb_drv->probe != NULL)
+ error = sysfs_create_file(&usb_drv->driver.kobj,
+ &driver_attr_new_id.attr);
+exit:
+ return error;
+}
+
+static void usb_remove_newid_file(struct usb_driver *usb_drv)
+{
+ if (usb_drv->no_dynamic_id)
+ return;
+
+ if (usb_drv->probe != NULL)
+ sysfs_remove_file(&usb_drv->driver.kobj,
+ &driver_attr_new_id.attr);
+}
+
+static void usb_free_dynids(struct usb_driver *usb_drv)
+{
+ struct usb_dynid *dynid, *n;
+
+ spin_lock(&usb_drv->dynids.lock);
+ list_for_each_entry_safe(dynid, n, &usb_drv->dynids.list, node) {
+ list_del(&dynid->node);
+ kfree(dynid);
+ }
+ spin_unlock(&usb_drv->dynids.lock);
+}
+#else
+static inline int usb_create_newid_file(struct usb_driver *usb_drv)
+{
+ return 0;
+}
+
+static void usb_remove_newid_file(struct usb_driver *usb_drv)
+{
+}
+
+static inline void usb_free_dynids(struct usb_driver *usb_drv)
+{
+}
+#endif
+
+static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *intf,
+ struct usb_driver *drv)
+{
+ struct usb_dynid *dynid;
+
+ spin_lock(&drv->dynids.lock);
+ list_for_each_entry(dynid, &drv->dynids.list, node) {
+ if (usb_match_one_id(intf, &dynid->id)) {
+ spin_unlock(&drv->dynids.lock);
+ return &dynid->id;
+ }
+ }
+ spin_unlock(&drv->dynids.lock);
+ return NULL;
+}
+
+
+/* called from driver core with usb_bus_type.subsys writelock */
+static int usb_probe_interface(struct device *dev)
+{
+ struct usb_interface * intf = to_usb_interface(dev);
+ struct usb_driver * driver = to_usb_driver(dev->driver);
+ const struct usb_device_id *id;
+ int error = -ENODEV;
+
+ dev_dbg(dev, "%s\n", __FUNCTION__);
+
+ if (!driver->probe)
+ return error;
+ /* FIXME we'd much prefer to just resume it ... */
+ if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED)
+ return -EHOSTUNREACH;
+
+ id = usb_match_id(intf, driver->id_table);
+ if (!id)
+ id = usb_match_dynamic_id(intf, driver);
+ if (id) {
+ dev_dbg(dev, "%s - got id\n", __FUNCTION__);
+
+ /* Interface "power state" doesn't correspond to any hardware
+ * state whatsoever. We use it to record when it's bound to
+ * a driver that may start I/0: it's not frozen/quiesced.
+ */
+ mark_active(intf);
+ intf->condition = USB_INTERFACE_BINDING;
+ error = driver->probe(intf, id);
+ if (error) {
+ mark_quiesced(intf);
+ intf->condition = USB_INTERFACE_UNBOUND;
+ } else
+ intf->condition = USB_INTERFACE_BOUND;
+ }
+
+ return error;
+}
+
+/* called from driver core with usb_bus_type.subsys writelock */
+static int usb_unbind_interface(struct device *dev)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+
+ intf->condition = USB_INTERFACE_UNBINDING;
+
+ /* release all urbs for this interface */
+ usb_disable_interface(interface_to_usbdev(intf), intf);
+
+ if (driver && driver->disconnect)
+ driver->disconnect(intf);
+
+ /* reset other interface state */
+ usb_set_interface(interface_to_usbdev(intf),
+ intf->altsetting[0].desc.bInterfaceNumber,
+ 0);
+ usb_set_intfdata(intf, NULL);
+ intf->condition = USB_INTERFACE_UNBOUND;
+ mark_quiesced(intf);
+
+ return 0;
+}
+
+/* returns 0 if no match, 1 if match */
+static int usb_match_one_id(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *intf;
+ struct usb_device *dev;
+
+ /* proc_connectinfo in devio.c may call us with id == NULL. */
+ if (id == NULL)
+ return 0;
+
+ intf = interface->cur_altsetting;
+ dev = interface_to_usbdev(interface);
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
+ id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
+ id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
+ return 0;
+
+ /* No need to test id->bcdDevice_lo != 0, since 0 is never
+ greater than any unsigned number. */
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
+ (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
+ (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
+ (id->bDeviceClass != dev->descriptor.bDeviceClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
+ (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
+ (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
+ (id->bInterfaceClass != intf->desc.bInterfaceClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
+ (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
+ return 0;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
+ (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
+ return 0;
+
+ return 1;
+}
+/**
+ * usb_match_id - find first usb_device_id matching device or interface
+ * @interface: the interface of interest
+ * @id: array of usb_device_id structures, terminated by zero entry
+ *
+ * usb_match_id searches an array of usb_device_id's and returns
+ * the first one matching the device or interface, or null.
+ * This is used when binding (or rebinding) a driver to an interface.
+ * Most USB device drivers will use this indirectly, through the usb core,
+ * but some layered driver frameworks use it directly.
+ * These device tables are exported with MODULE_DEVICE_TABLE, through
+ * modutils, to support the driver loading functionality of USB hotplugging.
+ *
+ * What Matches:
+ *
+ * The "match_flags" element in a usb_device_id controls which
+ * members are used. If the corresponding bit is set, the
+ * value in the device_id must match its corresponding member
+ * in the device or interface descriptor, or else the device_id
+ * does not match.
+ *
+ * "driver_info" is normally used only by device drivers,
+ * but you can create a wildcard "matches anything" usb_device_id
+ * as a driver's "modules.usbmap" entry if you provide an id with
+ * only a nonzero "driver_info" field. If you do this, the USB device
+ * driver's probe() routine should use additional intelligence to
+ * decide whether to bind to the specified interface.
+ *
+ * What Makes Good usb_device_id Tables:
+ *
+ * The match algorithm is very simple, so that intelligence in
+ * driver selection must come from smart driver id records.
+ * Unless you have good reasons to use another selection policy,
+ * provide match elements only in related groups, and order match
+ * specifiers from specific to general. Use the macros provided
+ * for that purpose if you can.
+ *
+ * The most specific match specifiers use device descriptor
+ * data. These are commonly used with product-specific matches;
+ * the USB_DEVICE macro lets you provide vendor and product IDs,
+ * and you can also match against ranges of product revisions.
+ * These are widely used for devices with application or vendor
+ * specific bDeviceClass values.
+ *
+ * Matches based on device class/subclass/protocol specifications
+ * are slightly more general; use the USB_DEVICE_INFO macro, or
+ * its siblings. These are used with single-function devices
+ * where bDeviceClass doesn't specify that each interface has
+ * its own class.
+ *
+ * Matches based on interface class/subclass/protocol are the
+ * most general; they let drivers bind to any interface on a
+ * multiple-function device. Use the USB_INTERFACE_INFO
+ * macro, or its siblings, to match class-per-interface style
+ * devices (as recorded in bDeviceClass).
+ *
+ * Within those groups, remember that not all combinations are
+ * meaningful. For example, don't give a product version range
+ * without vendor and product IDs; or specify a protocol without
+ * its associated class and subclass.
+ */
+const struct usb_device_id *usb_match_id(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ /* proc_connectinfo in devio.c may call us with id == NULL. */
+ if (id == NULL)
+ return NULL;
+
+ /* It is important to check that id->driver_info is nonzero,
+ since an entry that is all zeroes except for a nonzero
+ id->driver_info is the way to create an entry that
+ indicates that the driver want to examine every
+ device and interface. */
+ for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
+ id->driver_info; id++) {
+ if (usb_match_one_id(interface, id))
+ return id;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(usb_match_id);
+
+int usb_device_match(struct device *dev, struct device_driver *drv)
+{
+ struct usb_interface *intf;
+ struct usb_driver *usb_drv;
+ const struct usb_device_id *id;
+
+ /* check for generic driver, which we don't match any device with */
+ if (drv == &usb_generic_driver)
+ return 0;
+
+ intf = to_usb_interface(dev);
+ usb_drv = to_usb_driver(drv);
+
+ id = usb_match_id(intf, usb_drv->id_table);
+ if (id)
+ return 1;
+
+ id = usb_match_dynamic_id(intf, usb_drv);
+ if (id)
+ return 1;
+ return 0;
+}
+
+/**
+ * usb_register_driver - register a USB driver
+ * @new_driver: USB operations for the driver
+ * @owner: module owner of this driver.
+ *
+ * Registers a USB driver with the USB core. The list of unattached
+ * interfaces will be rescanned whenever a new driver is added, allowing
+ * the new driver to attach to any recognized devices.
+ * Returns a negative error code on failure and 0 on success.
+ *
+ * NOTE: if you want your driver to use the USB major number, you must call
+ * usb_register_dev() to enable that functionality. This function no longer
+ * takes care of that.
+ */
+int usb_register_driver(struct usb_driver *new_driver, struct module *owner)
+{
+ int retval = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ new_driver->driver.name = (char *)new_driver->name;
+ new_driver->driver.bus = &usb_bus_type;
+ new_driver->driver.probe = usb_probe_interface;
+ new_driver->driver.remove = usb_unbind_interface;
+ new_driver->driver.owner = owner;
+ spin_lock_init(&new_driver->dynids.lock);
+ INIT_LIST_HEAD(&new_driver->dynids.list);
+
+ retval = driver_register(&new_driver->driver);
+
+ if (!retval) {
+ pr_info("%s: registered new driver %s\n",
+ usbcore_name, new_driver->name);
+ usbfs_update_special();
+ usb_create_newid_file(new_driver);
+ } else {
+ printk(KERN_ERR "%s: error %d registering driver %s\n",
+ usbcore_name, retval, new_driver->name);
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(usb_register_driver);
+
+/**
+ * usb_deregister - unregister a USB driver
+ * @driver: USB operations of the driver to unregister
+ * Context: must be able to sleep
+ *
+ * Unlinks the specified driver from the internal USB driver list.
+ *
+ * NOTE: If you called usb_register_dev(), you still need to call
+ * usb_deregister_dev() to clean up your driver's allocated minor numbers,
+ * this * call will no longer do it for you.
+ */
+void usb_deregister(struct usb_driver *driver)
+{
+ pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name);
+
+ usb_remove_newid_file(driver);
+ usb_free_dynids(driver);
+ driver_unregister(&driver->driver);
+
+ usbfs_update_special();
+}
+EXPORT_SYMBOL_GPL(usb_deregister);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index da24c31ee00d..0018bbc4de34 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -857,9 +857,7 @@ static int register_root_hub (struct usb_device *usb_dev,
return (retval < 0) ? retval : -EMSGSIZE;
}
- usb_lock_device (usb_dev);
retval = usb_new_device (usb_dev);
- usb_unlock_device (usb_dev);
if (retval) {
usb_dev->bus->root_hub = NULL;
dev_err (parent_dev, "can't register root hub for %s, %d\n",
@@ -1827,8 +1825,6 @@ int usb_add_hcd(struct usb_hcd *hcd,
retval = -ENOMEM;
goto err_allocate_root_hub;
}
- rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
- USB_SPEED_FULL;
/* Although in principle hcd->driver->start() might need to use rhdev,
* none of the current drivers do.
@@ -1846,6 +1842,9 @@ int usb_add_hcd(struct usb_hcd *hcd,
dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
hcd->remote_wakeup = hcd->can_wakeup;
+ rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
+ USB_SPEED_FULL;
+ rhdev->bus_mA = min(500u, hcd->power_budget);
if ((retval = register_root_hub(rhdev, hcd)) != 0)
goto err_register_root_hub;
@@ -1891,7 +1890,10 @@ void usb_remove_hcd(struct usb_hcd *hcd)
spin_lock_irq (&hcd_root_hub_lock);
hcd->rh_registered = 0;
spin_unlock_irq (&hcd_root_hub_lock);
+
+ down(&usb_bus_list_lock);
usb_disconnect(&hcd->self.root_hub);
+ up(&usb_bus_list_lock);
hcd->poll_rh = 0;
del_timer_sync(&hcd->rh_timer);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index c8a1b350e2cf..591b5aad1a18 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -380,6 +380,7 @@ extern int usb_find_interface_driver (struct usb_device *dev,
#ifdef CONFIG_PM
extern void usb_hcd_suspend_root_hub (struct usb_hcd *hcd);
extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
+extern void usb_root_hub_lost_power (struct usb_device *rhdev);
extern int hcd_bus_suspend (struct usb_bus *bus);
extern int hcd_bus_resume (struct usb_bus *bus);
#else
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index f78bd124d290..650d5ee5871b 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -32,7 +32,7 @@
#include "hub.h"
/* Protect struct usb_device->state and ->children members
- * Note: Both are also protected by ->serialize, except that ->state can
+ * Note: Both are also protected by ->dev.sem, except that ->state can
* change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
static DEFINE_SPINLOCK(device_state_lock);
@@ -515,6 +515,31 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
return ret;
}
+
+/* caller has locked the hub device */
+static void hub_pre_reset(struct usb_hub *hub, int disable_ports)
+{
+ struct usb_device *hdev = hub->hdev;
+ int port1;
+
+ for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
+ if (hdev->children[port1 - 1]) {
+ usb_disconnect(&hdev->children[port1 - 1]);
+ if (disable_ports)
+ hub_port_disable(hub, port1, 0);
+ }
+ }
+ hub_quiesce(hub);
+}
+
+/* caller has locked the hub device */
+static void hub_post_reset(struct usb_hub *hub)
+{
+ hub_activate(hub);
+ hub_power_on(hub);
+}
+
+
static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
@@ -677,26 +702,40 @@ static int hub_configure(struct usb_hub *hub,
* and battery-powered root hubs (may provide just 8 mA).
*/
ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
- if (ret < 0) {
+ if (ret < 2) {
message = "can't get hub status";
goto fail;
}
le16_to_cpus(&hubstatus);
if (hdev == hdev->bus->root_hub) {
- struct usb_hcd *hcd =
- container_of(hdev->bus, struct usb_hcd, self);
-
- hub->power_budget = min(500u, hcd->power_budget) / 2;
+ if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)
+ hub->mA_per_port = 500;
+ else {
+ hub->mA_per_port = hdev->bus_mA;
+ hub->limited_power = 1;
+ }
} else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
hub->descriptor->bHubContrCurrent);
- hub->power_budget = (501 - hub->descriptor->bHubContrCurrent)
- / 2;
+ hub->limited_power = 1;
+ if (hdev->maxchild > 0) {
+ int remaining = hdev->bus_mA -
+ hub->descriptor->bHubContrCurrent;
+
+ if (remaining < hdev->maxchild * 100)
+ dev_warn(hub_dev,
+ "insufficient power available "
+ "to use all downstream ports\n");
+ hub->mA_per_port = 100; /* 7.2.1.1 */
+ }
+ } else { /* Self-powered external hub */
+ /* FIXME: What about battery-powered external hubs that
+ * provide less current per port? */
+ hub->mA_per_port = 500;
}
- if (hub->power_budget)
- dev_dbg(hub_dev, "%dmA bus power budget for children\n",
- hub->power_budget * 2);
-
+ if (hub->mA_per_port < 500)
+ dev_dbg(hub_dev, "%umA bus power budget for each child\n",
+ hub->mA_per_port);
ret = hub_hub_status(hub, &hubstatus, &hubchange);
if (ret < 0) {
@@ -750,29 +789,10 @@ fail:
static unsigned highspeed_hubs;
-/* Called after the hub driver is unbound from a hub with children */
-static void hub_remove_children_work(void *__hub)
-{
- struct usb_hub *hub = __hub;
- struct usb_device *hdev = hub->hdev;
- int i;
-
- kfree(hub);
-
- usb_lock_device(hdev);
- for (i = 0; i < hdev->maxchild; ++i) {
- if (hdev->children[i])
- usb_disconnect(&hdev->children[i]);
- }
- usb_unlock_device(hdev);
- usb_put_dev(hdev);
-}
-
static void hub_disconnect(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata (intf);
struct usb_device *hdev;
- int n, port1;
usb_set_intfdata (intf, NULL);
hdev = hub->hdev;
@@ -780,7 +800,9 @@ static void hub_disconnect(struct usb_interface *intf)
if (hdev->speed == USB_SPEED_HIGH)
highspeed_hubs--;
- hub_quiesce(hub);
+ /* Disconnect all children and quiesce the hub */
+ hub_pre_reset(hub, 1);
+
usb_free_urb(hub->urb);
hub->urb = NULL;
@@ -800,27 +822,7 @@ static void hub_disconnect(struct usb_interface *intf)
hub->buffer = NULL;
}
- /* If there are any children then this is an unbind only, not a
- * physical disconnection. The active ports must be disabled
- * and later on we must call usb_disconnect(). We can't call
- * it now because we may not hold the hub's device lock.
- */
- n = 0;
- for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
- if (hdev->children[port1 - 1]) {
- ++n;
- hub_port_disable(hub, port1, 1);
- }
- }
-
- if (n == 0)
- kfree(hub);
- else {
- /* Reuse the hub->leds work_struct for our own purposes */
- INIT_WORK(&hub->leds, hub_remove_children_work, hub);
- schedule_work(&hub->leds);
- usb_get_dev(hdev);
- }
+ kfree(hub);
}
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -917,26 +919,6 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
}
}
-/* caller has locked the hub device */
-static void hub_pre_reset(struct usb_hub *hub)
-{
- struct usb_device *hdev = hub->hdev;
- int i;
-
- for (i = 0; i < hdev->maxchild; ++i) {
- if (hdev->children[i])
- usb_disconnect(&hdev->children[i]);
- }
- hub_quiesce(hub);
-}
-
-/* caller has locked the hub device */
-static void hub_post_reset(struct usb_hub *hub)
-{
- hub_activate(hub);
- hub_power_on(hub);
-}
-
/* grab device/port lock, returning index of that port (zero based).
* protects the upstream link used by this device from concurrent
@@ -964,24 +946,21 @@ static int locktree(struct usb_device *udev)
t = locktree(hdev);
if (t < 0)
return t;
- for (t = 0; t < hdev->maxchild; t++) {
- if (hdev->children[t] == udev) {
- /* everything is fail-fast once disconnect
- * processing starts
- */
- if (udev->state == USB_STATE_NOTATTACHED)
- break;
- /* when everyone grabs locks top->bottom,
- * non-overlapping work may be concurrent
- */
- down(&udev->serialize);
- up(&hdev->serialize);
- return t + 1;
- }
+ /* everything is fail-fast once disconnect
+ * processing starts
+ */
+ if (udev->state == USB_STATE_NOTATTACHED) {
+ usb_unlock_device(hdev);
+ return -ENODEV;
}
+
+ /* when everyone grabs locks top->bottom,
+ * non-overlapping work may be concurrent
+ */
+ usb_lock_device(udev);
usb_unlock_device(hdev);
- return -ENODEV;
+ return udev->portnum;
}
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
@@ -1039,6 +1018,39 @@ void usb_set_device_state(struct usb_device *udev,
EXPORT_SYMBOL(usb_set_device_state);
+#ifdef CONFIG_PM
+
+/**
+ * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
+ * @rhdev: struct usb_device for the root hub
+ *
+ * The USB host controller driver calls this function when its root hub
+ * is resumed and Vbus power has been interrupted or the controller
+ * has been reset. The routine marks all the children of the root hub
+ * as NOTATTACHED and marks logical connect-change events on their ports.
+ */
+void usb_root_hub_lost_power(struct usb_device *rhdev)
+{
+ struct usb_hub *hub;
+ int port1;
+ unsigned long flags;
+
+ dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
+ spin_lock_irqsave(&device_state_lock, flags);
+ hub = hdev_to_hub(rhdev);
+ for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
+ if (rhdev->children[port1 - 1]) {
+ recursively_mark_NOTATTACHED(
+ rhdev->children[port1 - 1]);
+ set_bit(port1, hub->change_bits);
+ }
+ }
+ spin_unlock_irqrestore(&device_state_lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
+
+#endif
+
static void choose_address(struct usb_device *udev)
{
int devnum;
@@ -1099,16 +1111,10 @@ void usb_disconnect(struct usb_device **pdev)
* this quiesces everyting except pending urbs.
*/
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
-
- /* lock the bus list on behalf of HCDs unregistering their root hubs */
- if (!udev->parent) {
- down(&usb_bus_list_lock);
- usb_lock_device(udev);
- } else
- down(&udev->serialize);
-
dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
+ usb_lock_device(udev);
+
/* Free up all the children before we remove this device */
for (i = 0; i < USB_MAXCHILDREN; i++) {
if (udev->children[i])
@@ -1136,54 +1142,112 @@ void usb_disconnect(struct usb_device **pdev)
*pdev = NULL;
spin_unlock_irq(&device_state_lock);
- if (!udev->parent) {
- usb_unlock_device(udev);
- up(&usb_bus_list_lock);
- } else
- up(&udev->serialize);
+ usb_unlock_device(udev);
device_unregister(&udev->dev);
}
+static inline const char *plural(int n)
+{
+ return (n == 1 ? "" : "s");
+}
+
static int choose_configuration(struct usb_device *udev)
{
- int c, i;
+ int i;
+ u16 devstatus;
+ int bus_powered;
+ int num_configs;
+ struct usb_host_config *c, *best;
+
+ /* If this fails, assume the device is bus-powered */
+ devstatus = 0;
+ usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
+ le16_to_cpus(&devstatus);
+ bus_powered = ((devstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0);
+ dev_dbg(&udev->dev, "device is %s-powered\n",
+ bus_powered ? "bus" : "self");
+
+ best = NULL;
+ c = udev->config;
+ num_configs = udev->descriptor.bNumConfigurations;
+ for (i = 0; i < num_configs; (i++, c++)) {
+ struct usb_interface_descriptor *desc =
+ &c->intf_cache[0]->altsetting->desc;
+
+ /*
+ * HP's USB bus-powered keyboard has only one configuration
+ * and it claims to be self-powered; other devices may have
+ * similar errors in their descriptors. If the next test
+ * were allowed to execute, such configurations would always
+ * be rejected and the devices would not work as expected.
+ */
+#if 0
+ /* Rule out self-powered configs for a bus-powered device */
+ if (bus_powered && (c->desc.bmAttributes &
+ USB_CONFIG_ATT_SELFPOWER))
+ continue;
+#endif
- /* NOTE: this should interact with hub power budgeting */
+ /*
+ * The next test may not be as effective as it should be.
+ * Some hubs have errors in their descriptor, claiming
+ * to be self-powered when they are really bus-powered.
+ * We will overestimate the amount of current such hubs
+ * make available for each port.
+ *
+ * This is a fairly benign sort of failure. It won't
+ * cause us to reject configurations that we should have
+ * accepted.
+ */
- c = udev->config[0].desc.bConfigurationValue;
- if (udev->descriptor.bNumConfigurations != 1) {
- for (i = 0; i < udev->descriptor.bNumConfigurations; i++) {
- struct usb_interface_descriptor *desc;
+ /* Rule out configs that draw too much bus current */
+ if (c->desc.bMaxPower * 2 > udev->bus_mA)
+ continue;
- /* heuristic: Linux is more likely to have class
- * drivers, so avoid vendor-specific interfaces.
- */
- desc = &udev->config[i].intf_cache[0]
- ->altsetting->desc;
- if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC)
- continue;
- /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS.
- * MSFT needs this to be the first config; never use
- * it as the default unless Linux has host-side RNDIS.
- * A second config would ideally be CDC-Ethernet, but
- * may instead be the "vendor specific" CDC subset
- * long used by ARM Linux for sa1100 or pxa255.
- */
- if (desc->bInterfaceClass == USB_CLASS_COMM
- && desc->bInterfaceSubClass == 2
- && desc->bInterfaceProtocol == 0xff) {
- c = udev->config[1].desc.bConfigurationValue;
- continue;
- }
- c = udev->config[i].desc.bConfigurationValue;
+ /* If the first config's first interface is COMM/2/0xff
+ * (MSFT RNDIS), rule it out unless Linux has host-side
+ * RNDIS support. */
+ if (i == 0 && desc->bInterfaceClass == USB_CLASS_COMM
+ && desc->bInterfaceSubClass == 2
+ && desc->bInterfaceProtocol == 0xff) {
+#ifndef CONFIG_USB_NET_RNDIS
+ continue;
+#else
+ best = c;
+#endif
+ }
+
+ /* From the remaining configs, choose the first one whose
+ * first interface is for a non-vendor-specific class.
+ * Reason: Linux is more likely to have a class driver
+ * than a vendor-specific driver. */
+ else if (udev->descriptor.bDeviceClass !=
+ USB_CLASS_VENDOR_SPEC &&
+ desc->bInterfaceClass !=
+ USB_CLASS_VENDOR_SPEC) {
+ best = c;
break;
}
+
+ /* If all the remaining configs are vendor-specific,
+ * choose the first one. */
+ else if (!best)
+ best = c;
+ }
+
+ if (best) {
+ i = best->desc.bConfigurationValue;
dev_info(&udev->dev,
- "configuration #%d chosen from %d choices\n",
- c, udev->descriptor.bNumConfigurations);
+ "configuration #%d chosen from %d choice%s\n",
+ i, num_configs, plural(num_configs));
+ } else {
+ i = -1;
+ dev_warn(&udev->dev,
+ "no configuration chosen from %d choice%s\n",
+ num_configs, plural(num_configs));
}
- return c;
+ return i;
}
#ifdef DEBUG
@@ -1210,8 +1274,8 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
*
* This is called with devices which have been enumerated, but not yet
* configured. The device descriptor is available, but not descriptors
- * for any device configuration. The caller must have locked udev and
- * either the parent hub (if udev is a normal device) or else the
+ * for any device configuration. The caller must have locked either
+ * the parent hub (if udev is a normal device) or else the
* usb_bus_list_lock (if udev is a root hub). The parent's pointer to
* udev has already been installed, but udev is not yet visible through
* sysfs or other filesystem code.
@@ -1221,8 +1285,7 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
*
* This call is synchronous, and may not be used in an interrupt context.
*
- * Only the hub driver should ever call this; root hub registration
- * uses it indirectly.
+ * Only the hub driver or root-hub registrar should ever call this.
*/
int usb_new_device(struct usb_device *udev)
{
@@ -1269,15 +1332,9 @@ int usb_new_device(struct usb_device *udev)
le16_to_cpu(udev->config[0].desc.wTotalLength),
USB_DT_OTG, (void **) &desc) == 0) {
if (desc->bmAttributes & USB_OTG_HNP) {
- unsigned port1;
+ unsigned port1 = udev->portnum;
struct usb_device *root = udev->parent;
- for (port1 = 1; port1 <= root->maxchild;
- port1++) {
- if (root->children[port1-1] == udev)
- break;
- }
-
dev_info(&udev->dev,
"Dual-Role OTG device on %sHNP port\n",
(port1 == bus->otg_port)
@@ -1331,27 +1388,27 @@ int usb_new_device(struct usb_device *udev)
}
usb_create_sysfs_dev_files (udev);
+ usb_lock_device(udev);
+
/* choose and set the configuration. that registers the interfaces
* with the driver core, and lets usb device drivers bind to them.
*/
c = choose_configuration(udev);
- if (c < 0)
- dev_warn(&udev->dev,
- "can't choose an initial configuration\n");
- else {
+ if (c >= 0) {
err = usb_set_configuration(udev, c);
if (err) {
dev_err(&udev->dev, "can't set config #%d, error %d\n",
c, err);
- usb_remove_sysfs_dev_files(udev);
- device_del(&udev->dev);
- goto fail;
+ /* This need not be fatal. The user can try to
+ * set other configurations. */
}
}
/* USB device state == configured ... usable */
usb_notify_add_device(udev);
+ usb_unlock_device(udev);
+
return 0;
fail:
@@ -1654,15 +1711,9 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
int usb_suspend_device(struct usb_device *udev)
{
#ifdef CONFIG_USB_SUSPEND
- int port1, status;
-
- port1 = locktree(udev);
- if (port1 < 0)
- return port1;
-
- status = __usb_suspend_device(udev, port1);
- usb_unlock_device(udev);
- return status;
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+ return __usb_suspend_device(udev, udev->portnum);
#else
/* NOTE: udev->state unchanged, it's not lying ... */
udev->dev.power.power_state = PMSG_SUSPEND;
@@ -1694,13 +1745,14 @@ static int finish_device_resume(struct usb_device *udev)
usb_set_device_state(udev, udev->actconfig
? USB_STATE_CONFIGURED
: USB_STATE_ADDRESS);
+ udev->dev.power.power_state = PMSG_ON;
/* 10.5.4.5 says be sure devices in the tree are still there.
* For now let's assume the device didn't go crazy on resume,
* and device drivers will know about any resume quirks.
*/
status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
- if (status < 0)
+ if (status < 2)
dev_dbg(&udev->dev,
"gone after usb resume? status %d\n",
status);
@@ -1709,7 +1761,7 @@ static int finish_device_resume(struct usb_device *udev)
int (*resume)(struct device *);
le16_to_cpus(&devstatus);
- if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)
+ if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
&& udev->parent) {
status = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
@@ -1729,8 +1781,14 @@ static int finish_device_resume(struct usb_device *udev)
* may have a child resume event to deal with soon
*/
resume = udev->dev.bus->resume;
- for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++)
- (void) resume(&udev->actconfig->interface[i]->dev);
+ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ struct device *dev =
+ &udev->actconfig->interface[i]->dev;
+
+ down(&dev->sem);
+ (void) resume(dev);
+ up(&dev->sem);
+ }
status = 0;
} else if (udev->devnum <= 0) {
@@ -1813,11 +1871,10 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
*/
int usb_resume_device(struct usb_device *udev)
{
- int port1, status;
+ int status;
- port1 = locktree(udev);
- if (port1 < 0)
- return port1;
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
#ifdef CONFIG_USB_SUSPEND
/* selective resume of one downstream hub-to-device port */
@@ -1826,7 +1883,7 @@ int usb_resume_device(struct usb_device *udev)
// NOTE swsusp may bork us, device state being wrong...
// NOTE this fails if parent is also suspended...
status = hub_port_resume(hdev_to_hub(udev->parent),
- port1, udev);
+ udev->portnum, udev);
} else
status = 0;
} else
@@ -1836,13 +1893,11 @@ int usb_resume_device(struct usb_device *udev)
dev_dbg(&udev->dev, "can't resume, status %d\n",
status);
- usb_unlock_device(udev);
-
/* rebind drivers that had no suspend() */
if (status == 0) {
- usb_lock_all_devices();
+ usb_unlock_device(udev);
bus_rescan_devices(&usb_bus_type);
- usb_unlock_all_devices();
+ usb_lock_device(udev);
}
return status;
}
@@ -1856,14 +1911,14 @@ static int remote_wakeup(struct usb_device *udev)
/* don't repeat RESUME sequence if this device
* was already woken up by some other task
*/
- down(&udev->serialize);
+ usb_lock_device(udev);
if (udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "RESUME (wakeup)\n");
/* TRSMRCY = 10 msec */
msleep(10);
status = finish_device_resume(udev);
}
- up(&udev->serialize);
+ usb_unlock_device(udev);
#endif
return status;
}
@@ -1964,7 +2019,7 @@ static int hub_resume(struct usb_interface *intf)
if (!udev || status < 0)
continue;
- down (&udev->serialize);
+ usb_lock_device(udev);
if (portstat & USB_PORT_STAT_SUSPEND)
status = hub_port_resume(hub, port1, udev);
else {
@@ -1975,7 +2030,7 @@ static int hub_resume(struct usb_interface *intf)
hub_port_logical_disconnect(hub, port1);
}
}
- up(&udev->serialize);
+ usb_unlock_device(udev);
}
}
#endif
@@ -2359,39 +2414,36 @@ hub_power_remaining (struct usb_hub *hub)
{
struct usb_device *hdev = hub->hdev;
int remaining;
- unsigned i;
+ int port1;
- remaining = hub->power_budget;
- if (!remaining) /* self-powered */
+ if (!hub->limited_power)
return 0;
- for (i = 0; i < hdev->maxchild; i++) {
- struct usb_device *udev = hdev->children[i];
- int delta, ceiling;
+ remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent;
+ for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
+ struct usb_device *udev = hdev->children[port1 - 1];
+ int delta;
if (!udev)
continue;
- /* 100mA per-port ceiling, or 8mA for OTG ports */
- if (i != (udev->bus->otg_port - 1) || hdev->parent)
- ceiling = 50;
- else
- ceiling = 4;
-
+ /* Unconfigured devices may not use more than 100mA,
+ * or 8mA for OTG ports */
if (udev->actconfig)
- delta = udev->actconfig->desc.bMaxPower;
+ delta = udev->actconfig->desc.bMaxPower * 2;
+ else if (port1 != udev->bus->otg_port || hdev->parent)
+ delta = 100;
else
- delta = ceiling;
- // dev_dbg(&udev->dev, "budgeted %dmA\n", 2 * delta);
- if (delta > ceiling)
- dev_warn(&udev->dev, "%dmA over %dmA budget!\n",
- 2 * (delta - ceiling), 2 * ceiling);
+ delta = 8;
+ if (delta > hub->mA_per_port)
+ dev_warn(&udev->dev, "%dmA is over %umA budget "
+ "for port %d!\n",
+ delta, hub->mA_per_port, port1);
remaining -= delta;
}
if (remaining < 0) {
- dev_warn(hub->intfdev,
- "%dmA over power budget!\n",
- -2 * remaining);
+ dev_warn(hub->intfdev, "%dmA over power budget!\n",
+ - remaining);
remaining = 0;
}
return remaining;
@@ -2486,7 +2538,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
usb_set_device_state(udev, USB_STATE_POWERED);
udev->speed = USB_SPEED_UNKNOWN;
-
+ udev->bus_mA = hub->mA_per_port;
+
/* set the address */
choose_address(udev);
if (udev->devnum <= 0) {
@@ -2506,16 +2559,16 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
* on the parent.
*/
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
- && hub->power_budget) {
+ && udev->bus_mA <= 100) {
u16 devstat;
status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
&devstat);
- if (status < 0) {
+ if (status < 2) {
dev_dbg(&udev->dev, "get status %d ?\n", status);
goto loop_disable;
}
- cpu_to_le16s(&devstat);
+ le16_to_cpus(&devstat);
if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
dev_err(&udev->dev,
"can't connect bus-powered hub "
@@ -2540,7 +2593,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
* udev becomes globally accessible, although presumably
* no one will look at it until hdev is unlocked.
*/
- down (&udev->serialize);
status = 0;
/* We mustn't add new devices if the parent hub has
@@ -2564,15 +2616,12 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
}
- up (&udev->serialize);
if (status)
goto loop_disable;
status = hub_power_remaining(hub);
if (status)
- dev_dbg(hub_dev,
- "%dmA power budget left\n",
- 2 * status);
+ dev_dbg(hub_dev, "%dmA power budget left\n", status);
return;
@@ -2648,6 +2697,8 @@ static void hub_events(void)
if (i) {
dpm_runtime_resume(&hdev->dev);
dpm_runtime_resume(&intf->dev);
+ usb_put_intf(intf);
+ continue;
}
/* Lock the device, then check to see if we were
@@ -2661,7 +2712,7 @@ static void hub_events(void)
/* If the hub has died, clean up after it */
if (hdev->state == USB_STATE_NOTATTACHED) {
- hub_pre_reset(hub);
+ hub_pre_reset(hub, 0);
goto loop;
}
@@ -2784,6 +2835,11 @@ static void hub_events(void)
if (hubchange & HUB_CHANGE_LOCAL_POWER) {
dev_dbg (hub_dev, "power change\n");
clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
+ if (hubstatus & HUB_STATUS_LOCAL_POWER)
+ /* FIXME: Is this always true? */
+ hub->limited_power = 0;
+ else
+ hub->limited_power = 1;
}
if (hubchange & HUB_CHANGE_OVERCURRENT) {
dev_dbg (hub_dev, "overcurrent change\n");
@@ -2832,7 +2888,6 @@ static struct usb_device_id hub_id_table [] = {
MODULE_DEVICE_TABLE (usb, hub_id_table);
static struct usb_driver hub_driver = {
- .owner = THIS_MODULE,
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
@@ -2944,7 +2999,8 @@ int usb_reset_device(struct usb_device *udev)
struct usb_hub *parent_hub;
struct usb_device_descriptor descriptor = udev->descriptor;
struct usb_hub *hub = NULL;
- int i, ret = 0, port1 = -1;
+ int i, ret = 0;
+ int port1 = udev->portnum;
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED) {
@@ -2958,18 +3014,6 @@ int usb_reset_device(struct usb_device *udev)
dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__);
return -EISDIR;
}
-
- for (i = 0; i < parent_hdev->maxchild; i++)
- if (parent_hdev->children[i] == udev) {
- port1 = i + 1;
- break;
- }
-
- if (port1 < 0) {
- /* If this ever happens, it's very bad */
- dev_err(&udev->dev, "Can't locate device's port!\n");
- return -ENOENT;
- }
parent_hub = hdev_to_hub(parent_hdev);
/* If we're resetting an active hub, take some special actions */
@@ -2977,7 +3021,7 @@ int usb_reset_device(struct usb_device *udev)
udev->actconfig->interface[0]->dev.driver ==
&hub_driver.driver &&
(hub = hdev_to_hub(udev)) != NULL) {
- hub_pre_reset(hub);
+ hub_pre_reset(hub, 0);
}
set_bit(port1, parent_hub->busy_bits);
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index bf23f8978024..29d5f45a8456 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -220,8 +220,9 @@ struct usb_hub {
struct usb_hub_descriptor *descriptor; /* class descriptor */
struct usb_tt tt; /* Transaction Translator */
- u8 power_budget; /* in 2mA units; or zero */
+ unsigned mA_per_port; /* current for each child */
+ unsigned limited_power:1;
unsigned quiescing:1;
unsigned activating:1;
unsigned resume_root_hub:1;
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index fe74f99ca5f4..319de03944e7 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1387,6 +1387,12 @@ free_interfaces:
if (dev->state != USB_STATE_ADDRESS)
usb_disable_device (dev, 1); // Skip ep0
+ i = dev->bus_mA - cp->desc.bMaxPower * 2;
+ if (i < 0)
+ dev_warn(&dev->dev, "new config #%d exceeds power "
+ "limit by %dmA\n",
+ configuration, -i);
+
if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0)
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index e197ce9353de..56a3520863a9 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -32,7 +32,6 @@
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/smp_lock.h>
-#include <linux/rwsem.h>
#include <linux/usb.h>
#include <asm/io.h>
@@ -47,165 +46,7 @@
const char *usbcore_name = "usbcore";
static int nousb; /* Disable USB when built into kernel image */
- /* Not honored on modular build */
-static DECLARE_RWSEM(usb_all_devices_rwsem);
-
-
-static int generic_probe (struct device *dev)
-{
- return 0;
-}
-static int generic_remove (struct device *dev)
-{
- struct usb_device *udev = to_usb_device(dev);
-
- /* if this is only an unbind, not a physical disconnect, then
- * unconfigure the device */
- if (udev->state == USB_STATE_CONFIGURED)
- usb_set_configuration(udev, 0);
-
- /* in case the call failed or the device was suspended */
- if (udev->state >= USB_STATE_CONFIGURED)
- usb_disable_device(udev, 0);
- return 0;
-}
-
-static struct device_driver usb_generic_driver = {
- .owner = THIS_MODULE,
- .name = "usb",
- .bus = &usb_bus_type,
- .probe = generic_probe,
- .remove = generic_remove,
-};
-
-static int usb_generic_driver_data;
-
-/* called from driver core with usb_bus_type.subsys writelock */
-static int usb_probe_interface(struct device *dev)
-{
- struct usb_interface * intf = to_usb_interface(dev);
- struct usb_driver * driver = to_usb_driver(dev->driver);
- const struct usb_device_id *id;
- int error = -ENODEV;
-
- dev_dbg(dev, "%s\n", __FUNCTION__);
-
- if (!driver->probe)
- return error;
- /* FIXME we'd much prefer to just resume it ... */
- if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED)
- return -EHOSTUNREACH;
-
- id = usb_match_id (intf, driver->id_table);
- if (id) {
- dev_dbg (dev, "%s - got id\n", __FUNCTION__);
-
- /* Interface "power state" doesn't correspond to any hardware
- * state whatsoever. We use it to record when it's bound to
- * a driver that may start I/0: it's not frozen/quiesced.
- */
- mark_active(intf);
- intf->condition = USB_INTERFACE_BINDING;
- error = driver->probe (intf, id);
- if (error) {
- mark_quiesced(intf);
- intf->condition = USB_INTERFACE_UNBOUND;
- } else
- intf->condition = USB_INTERFACE_BOUND;
- }
-
- return error;
-}
-
-/* called from driver core with usb_bus_type.subsys writelock */
-static int usb_unbind_interface(struct device *dev)
-{
- struct usb_interface *intf = to_usb_interface(dev);
- struct usb_driver *driver = to_usb_driver(intf->dev.driver);
-
- intf->condition = USB_INTERFACE_UNBINDING;
-
- /* release all urbs for this interface */
- usb_disable_interface(interface_to_usbdev(intf), intf);
-
- if (driver && driver->disconnect)
- driver->disconnect(intf);
-
- /* reset other interface state */
- usb_set_interface(interface_to_usbdev(intf),
- intf->altsetting[0].desc.bInterfaceNumber,
- 0);
- usb_set_intfdata(intf, NULL);
- intf->condition = USB_INTERFACE_UNBOUND;
- mark_quiesced(intf);
-
- return 0;
-}
-
-/**
- * usb_register - register a USB driver
- * @new_driver: USB operations for the driver
- *
- * Registers a USB driver with the USB core. The list of unattached
- * interfaces will be rescanned whenever a new driver is added, allowing
- * the new driver to attach to any recognized devices.
- * Returns a negative error code on failure and 0 on success.
- *
- * NOTE: if you want your driver to use the USB major number, you must call
- * usb_register_dev() to enable that functionality. This function no longer
- * takes care of that.
- */
-int usb_register(struct usb_driver *new_driver)
-{
- int retval = 0;
-
- if (nousb)
- return -ENODEV;
-
- new_driver->driver.name = (char *)new_driver->name;
- new_driver->driver.bus = &usb_bus_type;
- new_driver->driver.probe = usb_probe_interface;
- new_driver->driver.remove = usb_unbind_interface;
- new_driver->driver.owner = new_driver->owner;
-
- usb_lock_all_devices();
- retval = driver_register(&new_driver->driver);
- usb_unlock_all_devices();
-
- if (!retval) {
- pr_info("%s: registered new driver %s\n",
- usbcore_name, new_driver->name);
- usbfs_update_special();
- } else {
- printk(KERN_ERR "%s: error %d registering driver %s\n",
- usbcore_name, retval, new_driver->name);
- }
-
- return retval;
-}
-
-/**
- * usb_deregister - unregister a USB driver
- * @driver: USB operations of the driver to unregister
- * Context: must be able to sleep
- *
- * Unlinks the specified driver from the internal USB driver list.
- *
- * NOTE: If you called usb_register_dev(), you still need to call
- * usb_deregister_dev() to clean up your driver's allocated minor numbers,
- * this * call will no longer do it for you.
- */
-void usb_deregister(struct usb_driver *driver)
-{
- pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name);
-
- usb_lock_all_devices();
- driver_unregister (&driver->driver);
- usb_unlock_all_devices();
-
- usbfs_update_special();
-}
/**
* usb_ifnum_to_if - get the interface object with a given interface number
@@ -351,152 +192,23 @@ void usb_driver_release_interface(struct usb_driver *driver,
iface->condition = USB_INTERFACE_UNBOUND;
mark_quiesced(iface);
}
-
-/**
- * usb_match_id - find first usb_device_id matching device or interface
- * @interface: the interface of interest
- * @id: array of usb_device_id structures, terminated by zero entry
- *
- * usb_match_id searches an array of usb_device_id's and returns
- * the first one matching the device or interface, or null.
- * This is used when binding (or rebinding) a driver to an interface.
- * Most USB device drivers will use this indirectly, through the usb core,
- * but some layered driver frameworks use it directly.
- * These device tables are exported with MODULE_DEVICE_TABLE, through
- * modutils and "modules.usbmap", to support the driver loading
- * functionality of USB hotplugging.
- *
- * What Matches:
- *
- * The "match_flags" element in a usb_device_id controls which
- * members are used. If the corresponding bit is set, the
- * value in the device_id must match its corresponding member
- * in the device or interface descriptor, or else the device_id
- * does not match.
- *
- * "driver_info" is normally used only by device drivers,
- * but you can create a wildcard "matches anything" usb_device_id
- * as a driver's "modules.usbmap" entry if you provide an id with
- * only a nonzero "driver_info" field. If you do this, the USB device
- * driver's probe() routine should use additional intelligence to
- * decide whether to bind to the specified interface.
- *
- * What Makes Good usb_device_id Tables:
- *
- * The match algorithm is very simple, so that intelligence in
- * driver selection must come from smart driver id records.
- * Unless you have good reasons to use another selection policy,
- * provide match elements only in related groups, and order match
- * specifiers from specific to general. Use the macros provided
- * for that purpose if you can.
- *
- * The most specific match specifiers use device descriptor
- * data. These are commonly used with product-specific matches;
- * the USB_DEVICE macro lets you provide vendor and product IDs,
- * and you can also match against ranges of product revisions.
- * These are widely used for devices with application or vendor
- * specific bDeviceClass values.
- *
- * Matches based on device class/subclass/protocol specifications
- * are slightly more general; use the USB_DEVICE_INFO macro, or
- * its siblings. These are used with single-function devices
- * where bDeviceClass doesn't specify that each interface has
- * its own class.
- *
- * Matches based on interface class/subclass/protocol are the
- * most general; they let drivers bind to any interface on a
- * multiple-function device. Use the USB_INTERFACE_INFO
- * macro, or its siblings, to match class-per-interface style
- * devices (as recorded in bDeviceClass).
- *
- * Within those groups, remember that not all combinations are
- * meaningful. For example, don't give a product version range
- * without vendor and product IDs; or specify a protocol without
- * its associated class and subclass.
- */
-const struct usb_device_id *
-usb_match_id(struct usb_interface *interface, const struct usb_device_id *id)
-{
- struct usb_host_interface *intf;
- struct usb_device *dev;
-
- /* proc_connectinfo in devio.c may call us with id == NULL. */
- if (id == NULL)
- return NULL;
-
- intf = interface->cur_altsetting;
- dev = interface_to_usbdev(interface);
-
- /* It is important to check that id->driver_info is nonzero,
- since an entry that is all zeroes except for a nonzero
- id->driver_info is the way to create an entry that
- indicates that the driver want to examine every
- device and interface. */
- for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
- id->driver_info; id++) {
-
- if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
- id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
- continue;
-
- if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
- id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
- continue;
-
- /* No need to test id->bcdDevice_lo != 0, since 0 is never
- greater than any unsigned number. */
- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
- (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
- continue;
-
- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
- (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
- continue;
-
- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
- (id->bDeviceClass != dev->descriptor.bDeviceClass))
- continue;
-
- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
- (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass))
- continue;
-
- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
- (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
- continue;
-
- if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
- (id->bInterfaceClass != intf->desc.bInterfaceClass))
- continue;
-
- if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
- (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
- continue;
-
- if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
- (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
- continue;
-
- return id;
- }
-
- return NULL;
-}
-
+struct find_interface_arg {
+ int minor;
+ struct usb_interface *interface;
+};
static int __find_interface(struct device * dev, void * data)
{
- struct usb_interface ** ret = (struct usb_interface **)data;
- struct usb_interface * intf = *ret;
- int *minor = (int *)data;
+ struct find_interface_arg *arg = data;
+ struct usb_interface *intf;
/* can't look at usb devices, only interfaces */
if (dev->driver == &usb_generic_driver)
return 0;
intf = to_usb_interface(dev);
- if (intf->minor != -1 && intf->minor == *minor) {
- *ret = intf;
+ if (intf->minor != -1 && intf->minor == arg->minor) {
+ arg->interface = intf;
return 1;
}
return 0;
@@ -513,35 +225,14 @@ static int __find_interface(struct device * dev, void * data)
*/
struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
{
- struct usb_interface *intf = (struct usb_interface *)(long)minor;
- int ret;
-
- ret = driver_for_each_device(&drv->driver, NULL, &intf, __find_interface);
+ struct find_interface_arg argb;
- return ret ? intf : NULL;
+ argb.minor = minor;
+ argb.interface = NULL;
+ driver_for_each_device(&drv->driver, NULL, &argb, __find_interface);
+ return argb.interface;
}
-static int usb_device_match (struct device *dev, struct device_driver *drv)
-{
- struct usb_interface *intf;
- struct usb_driver *usb_drv;
- const struct usb_device_id *id;
-
- /* check for generic driver, which we don't match any device with */
- if (drv == &usb_generic_driver)
- return 0;
-
- intf = to_usb_interface(dev);
- usb_drv = to_usb_driver(drv);
-
- id = usb_match_id (intf, usb_drv->id_table);
- if (id)
- return 1;
-
- return 0;
-}
-
-
#ifdef CONFIG_HOTPLUG
/*
@@ -750,12 +441,11 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
/* hub driver sets up TT records */
}
+ dev->portnum = port1;
dev->bus = bus;
dev->parent = parent;
INIT_LIST_HEAD(&dev->filelist);
- init_MUTEX(&dev->serialize);
-
return dev;
}
@@ -828,76 +518,21 @@ void usb_put_intf(struct usb_interface *intf)
/* USB device locking
*
- * Although locking USB devices should be straightforward, it is
- * complicated by the way the driver-model core works. When a new USB
- * driver is registered or unregistered, the core will automatically
- * probe or disconnect all matching interfaces on all USB devices while
- * holding the USB subsystem writelock. There's no good way for us to
- * tell which devices will be used or to lock them beforehand; our only
- * option is to effectively lock all the USB devices.
- *
- * We do that by using a private rw-semaphore, usb_all_devices_rwsem.
- * When locking an individual device you must first acquire the rwsem's
- * readlock. When a driver is registered or unregistered the writelock
- * must be held. These actions are encapsulated in the subroutines
- * below, so all a driver needs to do is call usb_lock_device() and
- * usb_unlock_device().
+ * USB devices and interfaces are locked using the semaphore in their
+ * embedded struct device. The hub driver guarantees that whenever a
+ * device is connected or disconnected, drivers are called with the
+ * USB device locked as well as their particular interface.
*
* Complications arise when several devices are to be locked at the same
* time. Only hub-aware drivers that are part of usbcore ever have to
- * do this; nobody else needs to worry about it. The problem is that
- * usb_lock_device() must not be called to lock a second device since it
- * would acquire the rwsem's readlock reentrantly, leading to deadlock if
- * another thread was waiting for the writelock. The solution is simple:
- *
- * When locking more than one device, call usb_lock_device()
- * to lock the first one. Lock the others by calling
- * down(&udev->serialize) directly.
- *
- * When unlocking multiple devices, use up(&udev->serialize)
- * to unlock all but the last one. Unlock the last one by
- * calling usb_unlock_device().
+ * do this; nobody else needs to worry about it. The rule for locking
+ * is simple:
*
* When locking both a device and its parent, always lock the
* the parent first.
*/
/**
- * usb_lock_device - acquire the lock for a usb device structure
- * @udev: device that's being locked
- *
- * Use this routine when you don't hold any other device locks;
- * to acquire nested inner locks call down(&udev->serialize) directly.
- * This is necessary for proper interaction with usb_lock_all_devices().
- */
-void usb_lock_device(struct usb_device *udev)
-{
- down_read(&usb_all_devices_rwsem);
- down(&udev->serialize);
-}
-
-/**
- * usb_trylock_device - attempt to acquire the lock for a usb device structure
- * @udev: device that's being locked
- *
- * Don't use this routine if you already hold a device lock;
- * use down_trylock(&udev->serialize) instead.
- * This is necessary for proper interaction with usb_lock_all_devices().
- *
- * Returns 1 if successful, 0 if contention.
- */
-int usb_trylock_device(struct usb_device *udev)
-{
- if (!down_read_trylock(&usb_all_devices_rwsem))
- return 0;
- if (down_trylock(&udev->serialize)) {
- up_read(&usb_all_devices_rwsem);
- return 0;
- }
- return 1;
-}
-
-/**
* usb_lock_device_for_reset - cautiously acquire the lock for a
* usb device structure
* @udev: device that's being locked
@@ -935,7 +570,7 @@ int usb_lock_device_for_reset(struct usb_device *udev,
}
}
- while (!usb_trylock_device(udev)) {
+ while (usb_trylock_device(udev) != 0) {
/* If we can't acquire the lock after waiting one second,
* we're probably deadlocked */
@@ -953,39 +588,6 @@ int usb_lock_device_for_reset(struct usb_device *udev,
return 1;
}
-/**
- * usb_unlock_device - release the lock for a usb device structure
- * @udev: device that's being unlocked
- *
- * Use this routine when releasing the only device lock you hold;
- * to release inner nested locks call up(&udev->serialize) directly.
- * This is necessary for proper interaction with usb_lock_all_devices().
- */
-void usb_unlock_device(struct usb_device *udev)
-{
- up(&udev->serialize);
- up_read(&usb_all_devices_rwsem);
-}
-
-/**
- * usb_lock_all_devices - acquire the lock for all usb device structures
- *
- * This is necessary when registering a new driver or probing a bus,
- * since the driver-model core may try to use any usb_device.
- */
-void usb_lock_all_devices(void)
-{
- down_write(&usb_all_devices_rwsem);
-}
-
-/**
- * usb_unlock_all_devices - release the lock for all usb device structures
- */
-void usb_unlock_all_devices(void)
-{
- up_write(&usb_all_devices_rwsem);
-}
-
static struct usb_device *match_device(struct usb_device *dev,
u16 vendor_id, u16 product_id)
@@ -1008,10 +610,10 @@ static struct usb_device *match_device(struct usb_device *dev,
/* look through all of the children of this device */
for (child = 0; child < dev->maxchild; ++child) {
if (dev->children[child]) {
- down(&dev->children[child]->serialize);
+ usb_lock_device(dev->children[child]);
ret_dev = match_device(dev->children[child],
vendor_id, product_id);
- up(&dev->children[child]->serialize);
+ usb_unlock_device(dev->children[child]);
if (ret_dev)
goto exit;
}
@@ -1432,7 +1034,8 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message)
mark_quiesced(intf);
} else {
// FIXME else if there's no suspend method, disconnect...
- dev_warn(dev, "no %s?\n", "suspend");
+ dev_warn(dev, "no suspend for driver %s?\n", driver->name);
+ mark_quiesced(intf);
status = 0;
}
return status;
@@ -1460,8 +1063,10 @@ static int usb_generic_resume(struct device *dev)
}
if ((dev->driver == NULL) ||
- (dev->driver_data == &usb_generic_driver_data))
+ (dev->driver_data == &usb_generic_driver_data)) {
+ dev->power.power_state.event = PM_EVENT_FREEZE;
return 0;
+ }
intf = to_usb_interface(dev);
driver = to_usb_driver(dev->driver);
@@ -1481,7 +1086,7 @@ static int usb_generic_resume(struct device *dev)
mark_quiesced(intf);
}
} else
- dev_warn(dev, "no %s?\n", "resume");
+ dev_warn(dev, "no resume for driver %s?\n", driver->name);
return 0;
}
@@ -1493,18 +1098,8 @@ struct bus_type usb_bus_type = {
.resume = usb_generic_resume,
};
-#ifndef MODULE
-
-static int __init usb_setup_disable(char *str)
-{
- nousb = 1;
- return 1;
-}
-
/* format to disable USB on kernel command line is: nousb */
-__setup("nousb", usb_setup_disable);
-
-#endif
+__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
/*
* for external read access to <nousb>
@@ -1595,8 +1190,6 @@ module_exit(usb_exit);
* driver modules to use.
*/
-EXPORT_SYMBOL(usb_register);
-EXPORT_SYMBOL(usb_deregister);
EXPORT_SYMBOL(usb_disabled);
EXPORT_SYMBOL_GPL(usb_get_intf);
@@ -1607,14 +1200,10 @@ EXPORT_SYMBOL(usb_put_dev);
EXPORT_SYMBOL(usb_get_dev);
EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
-EXPORT_SYMBOL(usb_lock_device);
-EXPORT_SYMBOL(usb_trylock_device);
EXPORT_SYMBOL(usb_lock_device_for_reset);
-EXPORT_SYMBOL(usb_unlock_device);
EXPORT_SYMBOL(usb_driver_claim_interface);
EXPORT_SYMBOL(usb_driver_release_interface);
-EXPORT_SYMBOL(usb_match_id);
EXPORT_SYMBOL(usb_find_interface);
EXPORT_SYMBOL(usb_ifnum_to_if);
EXPORT_SYMBOL(usb_altnum_to_altsetting);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 1c4a68499dce..4647e1ebc68d 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -16,9 +16,6 @@ extern int usb_get_device_descriptor(struct usb_device *dev,
extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
-extern void usb_lock_all_devices(void);
-extern void usb_unlock_all_devices(void);
-
extern void usb_kick_khubd(struct usb_device *dev);
extern void usb_suspend_root_hub(struct usb_device *hdev);
extern void usb_resume_root_hub(struct usb_device *dev);
@@ -33,6 +30,9 @@ extern void usb_host_cleanup(void);
extern int usb_suspend_device(struct usb_device *dev);
extern int usb_resume_device(struct usb_device *dev);
+extern struct device_driver usb_generic_driver;
+extern int usb_generic_driver_data;
+extern int usb_device_match(struct device *dev, struct device_driver *drv);
/* Interfaces and their "power state" are owned by usbcore */
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index c655d46c8aed..9734cb76dd6c 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -138,7 +138,7 @@ static const char *const ep_name [] = {
/* or like sa1100: two fixed function endpoints */
"ep1out-bulk", "ep2in-bulk",
};
-#define DUMMY_ENDPOINTS (sizeof(ep_name)/sizeof(char *))
+#define DUMMY_ENDPOINTS ARRAY_SIZE(ep_name)
/*-------------------------------------------------------------------------*/
@@ -896,7 +896,7 @@ dummy_gadget_release (struct device *dev)
#endif
}
-static int dummy_udc_probe (struct platform_device *dev)
+static int dummy_udc_probe (struct platform_device *pdev)
{
struct dummy *dum = the_controller;
int rc;
@@ -909,7 +909,7 @@ static int dummy_udc_probe (struct platform_device *dev)
dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
strcpy (dum->gadget.dev.bus_id, "gadget");
- dum->gadget.dev.parent = &dev->dev;
+ dum->gadget.dev.parent = &pdev->dev;
dum->gadget.dev.release = dummy_gadget_release;
rc = device_register (&dum->gadget.dev);
if (rc < 0)
@@ -919,47 +919,47 @@ static int dummy_udc_probe (struct platform_device *dev)
usb_bus_get (&dummy_to_hcd (dum)->self);
#endif
- platform_set_drvdata (dev, dum);
+ platform_set_drvdata (pdev, dum);
device_create_file (&dum->gadget.dev, &dev_attr_function);
return rc;
}
-static int dummy_udc_remove (struct platform_device *dev)
+static int dummy_udc_remove (struct platform_device *pdev)
{
- struct dummy *dum = platform_get_drvdata (dev);
+ struct dummy *dum = platform_get_drvdata (pdev);
- platform_set_drvdata (dev, NULL);
+ platform_set_drvdata (pdev, NULL);
device_remove_file (&dum->gadget.dev, &dev_attr_function);
device_unregister (&dum->gadget.dev);
return 0;
}
-static int dummy_udc_suspend (struct platform_device *dev, pm_message_t state)
+static int dummy_udc_suspend (struct platform_device *pdev, pm_message_t state)
{
- struct dummy *dum = platform_get_drvdata(dev);
+ struct dummy *dum = platform_get_drvdata(pdev);
- dev_dbg (&dev->dev, "%s\n", __FUNCTION__);
+ dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
spin_lock_irq (&dum->lock);
dum->udc_suspended = 1;
set_link_state (dum);
spin_unlock_irq (&dum->lock);
- dev->dev.power.power_state = state;
+ pdev->dev.power.power_state = state;
usb_hcd_poll_rh_status (dummy_to_hcd (dum));
return 0;
}
-static int dummy_udc_resume (struct platform_device *dev)
+static int dummy_udc_resume (struct platform_device *pdev)
{
- struct dummy *dum = platform_get_drvdata(dev);
+ struct dummy *dum = platform_get_drvdata(pdev);
- dev_dbg (&dev->dev, "%s\n", __FUNCTION__);
+ dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
spin_lock_irq (&dum->lock);
dum->udc_suspended = 0;
set_link_state (dum);
spin_unlock_irq (&dum->lock);
- dev->dev.power.power_state = PMSG_ON;
+ pdev->dev.power.power_state = PMSG_ON;
usb_hcd_poll_rh_status (dummy_to_hcd (dum));
return 0;
}
@@ -1576,7 +1576,7 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf)
dum = hcd_to_dummy (hcd);
spin_lock_irqsave (&dum->lock, flags);
- if (hcd->state != HC_STATE_RUNNING)
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
goto done;
if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) {
@@ -1623,7 +1623,7 @@ static int dummy_hub_control (
int retval = 0;
unsigned long flags;
- if (hcd->state != HC_STATE_RUNNING)
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
return -ETIMEDOUT;
dum = hcd_to_dummy (hcd);
@@ -1756,9 +1756,12 @@ static int dummy_bus_suspend (struct usb_hcd *hcd)
{
struct dummy *dum = hcd_to_dummy (hcd);
+ dev_dbg (&hcd->self.root_hub->dev, "%s\n", __FUNCTION__);
+
spin_lock_irq (&dum->lock);
dum->rh_state = DUMMY_RH_SUSPENDED;
set_link_state (dum);
+ hcd->state = HC_STATE_SUSPENDED;
spin_unlock_irq (&dum->lock);
return 0;
}
@@ -1766,14 +1769,23 @@ static int dummy_bus_suspend (struct usb_hcd *hcd)
static int dummy_bus_resume (struct usb_hcd *hcd)
{
struct dummy *dum = hcd_to_dummy (hcd);
+ int rc = 0;
+
+ dev_dbg (&hcd->self.root_hub->dev, "%s\n", __FUNCTION__);
spin_lock_irq (&dum->lock);
- dum->rh_state = DUMMY_RH_RUNNING;
- set_link_state (dum);
- if (!list_empty(&dum->urbp_list))
- mod_timer (&dum->timer, jiffies);
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ dev_warn (&hcd->self.root_hub->dev, "HC isn't running!\n");
+ rc = -ENODEV;
+ } else {
+ dum->rh_state = DUMMY_RH_RUNNING;
+ set_link_state (dum);
+ if (!list_empty(&dum->urbp_list))
+ mod_timer (&dum->timer, jiffies);
+ hcd->state = HC_STATE_RUNNING;
+ }
spin_unlock_irq (&dum->lock);
- return 0;
+ return rc;
}
/*-------------------------------------------------------------------------*/
@@ -1899,14 +1911,14 @@ static const struct hc_driver dummy_hcd = {
.bus_resume = dummy_bus_resume,
};
-static int dummy_hcd_probe (struct platform_device *dev)
+static int dummy_hcd_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd;
int retval;
- dev_info(&dev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
+ dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
- hcd = usb_create_hcd (&dummy_hcd, &dev->dev, dev->dev.bus_id);
+ hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, pdev->dev.bus_id);
if (!hcd)
return -ENOMEM;
the_controller = hcd_to_dummy (hcd);
@@ -1919,36 +1931,43 @@ static int dummy_hcd_probe (struct platform_device *dev)
return retval;
}
-static int dummy_hcd_remove (struct platform_device *dev)
+static int dummy_hcd_remove (struct platform_device *pdev)
{
struct usb_hcd *hcd;
- hcd = platform_get_drvdata (dev);
+ hcd = platform_get_drvdata (pdev);
usb_remove_hcd (hcd);
usb_put_hcd (hcd);
the_controller = NULL;
return 0;
}
-static int dummy_hcd_suspend (struct platform_device *dev, pm_message_t state)
+static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t state)
{
struct usb_hcd *hcd;
+ struct dummy *dum;
+ int rc = 0;
- dev_dbg (&dev->dev, "%s\n", __FUNCTION__);
- hcd = platform_get_drvdata (dev);
+ dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
- hcd->state = HC_STATE_SUSPENDED;
- return 0;
+ hcd = platform_get_drvdata (pdev);
+ dum = hcd_to_dummy (hcd);
+ if (dum->rh_state == DUMMY_RH_RUNNING) {
+ dev_warn(&pdev->dev, "Root hub isn't suspended!\n");
+ rc = -EBUSY;
+ } else
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ return rc;
}
-static int dummy_hcd_resume (struct platform_device *dev)
+static int dummy_hcd_resume (struct platform_device *pdev)
{
struct usb_hcd *hcd;
- dev_dbg (&dev->dev, "%s\n", __FUNCTION__);
- hcd = platform_get_drvdata (dev);
- hcd->state = HC_STATE_RUNNING;
+ dev_dbg (&pdev->dev, "%s\n", __FUNCTION__);
+ hcd = platform_get_drvdata (pdev);
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
usb_hcd_poll_rh_status (hcd);
return 0;
}
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index ea09aaa3cab6..0cea9782d7d4 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -224,6 +224,7 @@
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/kref.h>
#include <linux/kthread.h>
#include <linux/limits.h>
#include <linux/list.h>
@@ -238,7 +239,6 @@
#include <linux/string.h>
#include <linux/suspend.h>
#include <linux/utsname.h>
-#include <linux/wait.h>
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
@@ -250,7 +250,7 @@
#define DRIVER_DESC "File-backed Storage Gadget"
#define DRIVER_NAME "g_file_storage"
-#define DRIVER_VERSION "20 October 2004"
+#define DRIVER_VERSION "28 November 2005"
static const char longname[] = DRIVER_DESC;
static const char shortname[] = DRIVER_NAME;
@@ -335,8 +335,8 @@ MODULE_LICENSE("Dual BSD/GPL");
#define MAX_LUNS 8
/* Arggh! There should be a module_param_array_named macro! */
-static char *file[MAX_LUNS] = {NULL, };
-static int ro[MAX_LUNS] = {0, };
+static char *file[MAX_LUNS];
+static int ro[MAX_LUNS];
static struct {
int num_filenames;
@@ -587,7 +587,7 @@ enum fsg_buffer_state {
struct fsg_buffhd {
void *buf;
dma_addr_t dma;
- volatile enum fsg_buffer_state state;
+ enum fsg_buffer_state state;
struct fsg_buffhd *next;
/* The NetChip 2280 is faster, and handles some protocol faults
@@ -596,9 +596,9 @@ struct fsg_buffhd {
unsigned int bulk_out_intended_length;
struct usb_request *inreq;
- volatile int inreq_busy;
+ int inreq_busy;
struct usb_request *outreq;
- volatile int outreq_busy;
+ int outreq_busy;
};
enum fsg_state {
@@ -631,13 +631,16 @@ struct fsg_dev {
/* filesem protects: backing files in use */
struct rw_semaphore filesem;
+ /* reference counting: wait until all LUNs are released */
+ struct kref ref;
+
struct usb_ep *ep0; // Handy copy of gadget->ep0
struct usb_request *ep0req; // For control responses
- volatile unsigned int ep0_req_tag;
+ unsigned int ep0_req_tag;
const char *ep0req_name;
struct usb_request *intreq; // For interrupt responses
- volatile int intreq_busy;
+ int intreq_busy;
struct fsg_buffhd *intr_buffhd;
unsigned int bulk_out_maxpacket;
@@ -667,7 +670,6 @@ struct fsg_dev {
struct fsg_buffhd *next_buffhd_to_drain;
struct fsg_buffhd buffhds[NUM_BUFFERS];
- wait_queue_head_t thread_wqh;
int thread_wakeup_needed;
struct completion thread_notifier;
struct task_struct *thread_task;
@@ -694,7 +696,6 @@ struct fsg_dev {
unsigned int nluns;
struct lun *luns;
struct lun *curlun;
- struct completion lun_released;
};
typedef void (*fsg_routine_t)(struct fsg_dev *);
@@ -1073,11 +1074,13 @@ static int populate_config_buf(struct usb_gadget *gadget,
/* These routines may be called in process context or in_irq */
+/* Caller must hold fsg->lock */
static void wakeup_thread(struct fsg_dev *fsg)
{
/* Tell the main thread that something has happened */
fsg->thread_wakeup_needed = 1;
- wake_up_all(&fsg->thread_wqh);
+ if (fsg->thread_task)
+ wake_up_process(fsg->thread_task);
}
@@ -1164,11 +1167,12 @@ static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
usb_ep_fifo_flush(ep);
/* Hold the lock while we update the request and buffer states */
+ smp_wmb();
spin_lock(&fsg->lock);
bh->inreq_busy = 0;
bh->state = BUF_STATE_EMPTY;
- spin_unlock(&fsg->lock);
wakeup_thread(fsg);
+ spin_unlock(&fsg->lock);
}
static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
@@ -1185,11 +1189,12 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
usb_ep_fifo_flush(ep);
/* Hold the lock while we update the request and buffer states */
+ smp_wmb();
spin_lock(&fsg->lock);
bh->outreq_busy = 0;
bh->state = BUF_STATE_FULL;
- spin_unlock(&fsg->lock);
wakeup_thread(fsg);
+ spin_unlock(&fsg->lock);
}
@@ -1206,11 +1211,12 @@ static void intr_in_complete(struct usb_ep *ep, struct usb_request *req)
usb_ep_fifo_flush(ep);
/* Hold the lock while we update the request and buffer states */
+ smp_wmb();
spin_lock(&fsg->lock);
fsg->intreq_busy = 0;
bh->state = BUF_STATE_EMPTY;
- spin_unlock(&fsg->lock);
wakeup_thread(fsg);
+ spin_unlock(&fsg->lock);
}
#else
@@ -1261,8 +1267,8 @@ static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
fsg->cbbuf_cmnd_size = req->actual;
memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size);
- spin_unlock(&fsg->lock);
wakeup_thread(fsg);
+ spin_unlock(&fsg->lock);
}
#else
@@ -1514,8 +1520,8 @@ static int fsg_setup(struct usb_gadget *gadget,
/* Use this for bulk or interrupt transfers, not ep0 */
static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
- struct usb_request *req, volatile int *pbusy,
- volatile enum fsg_buffer_state *state)
+ struct usb_request *req, int *pbusy,
+ enum fsg_buffer_state *state)
{
int rc;
@@ -1523,8 +1529,11 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
dump_msg(fsg, "bulk-in", req->buf, req->length);
else if (ep == fsg->intr_in)
dump_msg(fsg, "intr-in", req->buf, req->length);
+
+ spin_lock_irq(&fsg->lock);
*pbusy = 1;
*state = BUF_STATE_BUSY;
+ spin_unlock_irq(&fsg->lock);
rc = usb_ep_queue(ep, req, GFP_KERNEL);
if (rc != 0) {
*pbusy = 0;
@@ -1544,14 +1553,23 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
static int sleep_thread(struct fsg_dev *fsg)
{
- int rc;
+ int rc = 0;
/* Wait until a signal arrives or we are woken up */
- rc = wait_event_interruptible(fsg->thread_wqh,
- fsg->thread_wakeup_needed);
+ for (;;) {
+ try_to_freeze();
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current)) {
+ rc = -EINTR;
+ break;
+ }
+ if (fsg->thread_wakeup_needed)
+ break;
+ schedule();
+ }
+ __set_current_state(TASK_RUNNING);
fsg->thread_wakeup_needed = 0;
- try_to_freeze();
- return (rc ? -EINTR : 0);
+ return rc;
}
@@ -1788,6 +1806,7 @@ static int do_write(struct fsg_dev *fsg)
if (bh->state == BUF_STATE_EMPTY && !get_some_more)
break; // We stopped early
if (bh->state == BUF_STATE_FULL) {
+ smp_rmb();
fsg->next_buffhd_to_drain = bh->next;
bh->state = BUF_STATE_EMPTY;
@@ -2356,6 +2375,7 @@ static int throw_away_data(struct fsg_dev *fsg)
/* Throw away the data in a filled buffer */
if (bh->state == BUF_STATE_FULL) {
+ smp_rmb();
bh->state = BUF_STATE_EMPTY;
fsg->next_buffhd_to_drain = bh->next;
@@ -3021,6 +3041,7 @@ static int get_next_command(struct fsg_dev *fsg)
if ((rc = sleep_thread(fsg)) != 0)
return rc;
}
+ smp_rmb();
rc = received_cbw(fsg, bh);
bh->state = BUF_STATE_EMPTY;
@@ -3642,11 +3663,19 @@ static DEVICE_ATTR(file, 0444, show_file, NULL);
/*-------------------------------------------------------------------------*/
+static void fsg_release(struct kref *ref)
+{
+ struct fsg_dev *fsg = container_of(ref, struct fsg_dev, ref);
+
+ kfree(fsg->luns);
+ kfree(fsg);
+}
+
static void lun_release(struct device *dev)
{
struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev);
- complete(&fsg->lun_released);
+ kref_put(&fsg->ref, fsg_release);
}
static void fsg_unbind(struct usb_gadget *gadget)
@@ -3660,14 +3689,12 @@ static void fsg_unbind(struct usb_gadget *gadget)
clear_bit(REGISTERED, &fsg->atomic_bitflags);
/* Unregister the sysfs attribute files and the LUNs */
- init_completion(&fsg->lun_released);
for (i = 0; i < fsg->nluns; ++i) {
curlun = &fsg->luns[i];
if (curlun->registered) {
device_remove_file(&curlun->dev, &dev_attr_ro);
device_remove_file(&curlun->dev, &dev_attr_file);
device_unregister(&curlun->dev);
- wait_for_completion(&fsg->lun_released);
curlun->registered = 0;
}
}
@@ -3846,6 +3873,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
curlun->dev.release = lun_release;
device_create_file(&curlun->dev, &dev_attr_ro);
device_create_file(&curlun->dev, &dev_attr_file);
+ kref_get(&fsg->ref);
}
if (file[i] && *file[i]) {
@@ -4061,7 +4089,7 @@ static int __init fsg_alloc(void)
return -ENOMEM;
spin_lock_init(&fsg->lock);
init_rwsem(&fsg->filesem);
- init_waitqueue_head(&fsg->thread_wqh);
+ kref_init(&fsg->ref);
init_completion(&fsg->thread_notifier);
the_fsg = fsg;
@@ -4069,13 +4097,6 @@ static int __init fsg_alloc(void)
}
-static void fsg_free(struct fsg_dev *fsg)
-{
- kfree(fsg->luns);
- kfree(fsg);
-}
-
-
static int __init fsg_init(void)
{
int rc;
@@ -4085,7 +4106,7 @@ static int __init fsg_init(void)
return rc;
fsg = the_fsg;
if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0)
- fsg_free(fsg);
+ kref_put(&fsg->ref, fsg_release);
return rc;
}
module_init(fsg_init);
@@ -4103,6 +4124,6 @@ static void __exit fsg_cleanup(void)
wait_for_completion(&fsg->thread_notifier);
close_all_backing_files(fsg);
- fsg_free(fsg);
+ kref_put(&fsg->ref, fsg_release);
}
module_exit(fsg_cleanup);
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index b35ac6d334f8..65e084a2c87e 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -890,10 +890,12 @@ static void gs_close(struct tty_struct *tty, struct file *file)
/* wait for write buffer to drain, or */
/* at most GS_CLOSE_TIMEOUT seconds */
if (gs_buf_data_avail(port->port_write_buf) > 0) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
wait_cond_interruptible_timeout(port->port_write_wait,
port->port_dev == NULL
|| gs_buf_data_avail(port->port_write_buf) == 0,
&port->port_lock, flags, GS_CLOSE_TIMEOUT * HZ);
+ spin_lock_irqsave(&port->port_lock, flags);
}
/* free disconnected port on final close */
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 58321d3f314c..e3020f4b17be 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -2,6 +2,10 @@
# Makefile for USB Host Controller Drivers
#
+ifeq ($(CONFIG_USB_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
obj-$(CONFIG_PCI) += pci-quirks.o
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 29f52a44b928..9dd3d14c64f3 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -17,13 +17,6 @@
*/
#include <linux/config.h>
-
-#ifdef CONFIG_USB_DEBUG
- #define DEBUG
-#else
- #undef DEBUG
-#endif
-
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/dmapool.h>
@@ -624,7 +617,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
}
/* remote wakeup [4.3.1] */
- if ((status & STS_PCD) && hcd->remote_wakeup) {
+ if (status & STS_PCD) {
unsigned i = HCS_N_PORTS (ehci->hcs_params);
/* resume root hub? */
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 82caf336e9b6..69b0b9be7a64 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -59,7 +59,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
t2 |= PORT_SUSPEND;
- if (hcd->remote_wakeup)
+ if (device_may_wakeup(&hcd->self.root_hub->dev))
t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E;
else
t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
@@ -517,7 +517,7 @@ static int ehci_hub_control (
if ((temp & PORT_PE) == 0
|| (temp & PORT_RESET) != 0)
goto error;
- if (hcd->remote_wakeup)
+ if (device_may_wakeup(&hcd->self.root_hub->dev))
temp |= PORT_WAKE_BITS;
writel (temp | PORT_SUSPEND,
&ehci->regs->port_status [wIndex]);
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 13f73a836e45..08ca0f849dab 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -210,7 +210,16 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
/* Serial Bus Release Number is at PCI 0x60 offset */
pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
- /* REVISIT: per-port wake capability (PCI 0x62) currently unused */
+ /* Workaround current PCI init glitch: wakeup bits aren't
+ * being set from PCI PM capability.
+ */
+ if (!device_can_wakeup(&pdev->dev)) {
+ u16 port_wake;
+
+ pci_read_config_word(pdev, 0x62, &port_wake);
+ if (port_wake & 0x0001)
+ device_init_wakeup(&pdev->dev, 1);
+ }
retval = ehci_pci_reinit(ehci, pdev);
done:
@@ -269,7 +278,6 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
unsigned port;
- struct usb_device *root = hcd->self.root_hub;
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
int retval = -EINVAL;
@@ -303,13 +311,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd)
restart:
ehci_dbg(ehci, "lost power, restarting\n");
- for (port = HCS_N_PORTS(ehci->hcs_params); port > 0; ) {
- port--;
- if (!root->children [port])
- continue;
- usb_set_device_state(root->children[port],
- USB_STATE_NOTATTACHED);
- }
+ usb_root_hub_lost_power(hcd->self.root_hub);
/* Else reset, to cope with power loss or flush-to-storage
* style "resume" having let BIOS kick in during reboot.
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index bf03ec0d8ee2..9b13bf2fa98d 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -514,18 +514,18 @@ qh_urb_transaction (
qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head);
+
+ /* for zero length DATA stages, STATUS is always IN */
+ if (len == 0)
+ token |= (1 /* "in" */ << 8);
}
/*
* data transfer stage: buffer setup
*/
- if (likely (len > 0))
- buf = urb->transfer_dma;
- else
- buf = 0;
+ buf = urb->transfer_dma;
- /* for zero length DATA stages, STATUS is always IN */
- if (!buf || is_input)
+ if (is_input)
token |= (1 /* "in" */ << 8);
/* else it's already initted to "out" pid (0 << 8) */
@@ -572,7 +572,7 @@ qh_urb_transaction (
* control requests may need a terminating data "status" ack;
* bulk ones may need a terminating short packet (zero length).
*/
- if (likely (buf != 0)) {
+ if (likely (urb->transfer_buffer_length != 0)) {
int one_more = 0;
if (usb_pipecontrol (urb->pipe)) {
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 82f64986bc22..584b8dc65119 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -55,19 +55,13 @@
/* enqueuing/finishing log of urbs */
//#define URB_TRACE
-#include <linux/config.h>
#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/list.h>
-#include <linux/interrupt.h>
#include <linux/usb.h>
#include <linux/usb_isp116x.h>
#include <linux/platform_device.h>
@@ -77,14 +71,10 @@
#include <asm/system.h>
#include <asm/byteorder.h>
-#ifndef DEBUG
-# define STUB_DEBUG_FILE
-#endif
-
#include "../core/hcd.h"
#include "isp116x.h"
-#define DRIVER_VERSION "05 Aug 2005"
+#define DRIVER_VERSION "03 Nov 2005"
#define DRIVER_DESC "ISP116x USB Host Controller Driver"
MODULE_DESCRIPTION(DRIVER_DESC);
@@ -164,13 +154,11 @@ static void pack_fifo(struct isp116x *isp116x)
struct ptd *ptd;
int buflen = isp116x->atl_last_dir == PTD_DIR_IN
? isp116x->atl_bufshrt : isp116x->atl_buflen;
- int ptd_count = 0;
isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET);
for (ep = isp116x->atl_active; ep; ep = ep->active) {
- ++ptd_count;
ptd = &ep->ptd;
dump_ptd(ptd);
dump_ptd_out_data(ptd, ep->data);
@@ -305,9 +293,8 @@ static void postproc_atl_queue(struct isp116x *isp116x)
udev = urb->dev;
ptd = &ep->ptd;
cc = PTD_GET_CC(ptd);
-
- spin_lock(&urb->lock);
short_not_ok = 1;
+ spin_lock(&urb->lock);
/* Data underrun is special. For allowed underrun
we clear the error and continue as normal. For
@@ -420,7 +407,7 @@ static void postproc_atl_queue(struct isp116x *isp116x)
ep->nextpid = 0;
break;
default:
- BUG_ON(1);
+ BUG();
}
spin_unlock(&urb->lock);
}
@@ -628,8 +615,12 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
u32 intstat = isp116x_read_reg32(isp116x, HCINTSTAT);
isp116x_write_reg32(isp116x, HCINTSTAT, intstat);
if (intstat & HCINT_UE) {
- ERR("Unrecoverable error\n");
- /* What should we do here? Reset? */
+ ERR("Unrecoverable error, HC is dead!\n");
+ /* IRQ's are off, we do no DMA,
+ perfectly ready to die ... */
+ hcd->state = HC_STATE_HALT;
+ ret = IRQ_HANDLED;
+ goto done;
}
if (intstat & HCINT_RHSC)
/* When root hub or any of its ports is going
@@ -640,7 +631,6 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
if (intstat & HCINT_RD) {
DBG("---- remote wakeup\n");
usb_hcd_resume_root_hub(hcd);
- ret = IRQ_HANDLED;
}
irqstat &= ~HCuPINT_OPR;
ret = IRQ_HANDLED;
@@ -651,6 +641,7 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
}
isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb);
+ done:
spin_unlock(&isp116x->lock);
return ret;
}
@@ -724,6 +715,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
spin_lock_irqsave(&isp116x->lock, flags);
if (!HC_IS_RUNNING(hcd->state)) {
+ kfree(ep);
ret = -ENODEV;
goto fail;
}
@@ -888,7 +880,7 @@ static void isp116x_endpoint_disable(struct usb_hcd *hcd,
struct usb_host_endpoint *hep)
{
int i;
- struct isp116x_ep *ep = hep->hcpriv;;
+ struct isp116x_ep *ep = hep->hcpriv;
if (!ep)
return;
@@ -916,8 +908,6 @@ static int isp116x_get_frame(struct usb_hcd *hcd)
return (int)fmnum;
}
-/*----------------------------------------------------------------*/
-
/*
Adapted from ohci-hub.c. Currently we don't support autosuspend.
*/
@@ -968,11 +958,10 @@ static void isp116x_hub_descriptor(struct isp116x *isp116x,
desc->bHubContrCurrent = 0;
desc->bNbrPorts = (u8) (reg & 0x3);
/* Power switching, device type, overcurrent. */
- desc->wHubCharacteristics =
- (__force __u16) cpu_to_le16((u16) ((reg >> 8) & 0x1f));
+ desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) & 0x1f));
desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff);
/* two bitmaps: ports removable, and legacy PortPwrCtrlMask */
- desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
+ desc->bitmap[0] = 0;
desc->bitmap[1] = ~0;
}
@@ -1159,135 +1148,9 @@ static int isp116x_hub_control(struct usb_hcd *hcd,
return ret;
}
-#ifdef CONFIG_PM
-
-static int isp116x_bus_suspend(struct usb_hcd *hcd)
-{
- struct isp116x *isp116x = hcd_to_isp116x(hcd);
- unsigned long flags;
- u32 val;
- int ret = 0;
-
- spin_lock_irqsave(&isp116x->lock, flags);
-
- val = isp116x_read_reg32(isp116x, HCCONTROL);
- switch (val & HCCONTROL_HCFS) {
- case HCCONTROL_USB_OPER:
- hcd->state = HC_STATE_QUIESCING;
- val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE);
- val |= HCCONTROL_USB_SUSPEND;
- if (hcd->remote_wakeup)
- val |= HCCONTROL_RWE;
- /* Wait for usb transfers to finish */
- mdelay(2);
- isp116x_write_reg32(isp116x, HCCONTROL, val);
- hcd->state = HC_STATE_SUSPENDED;
- /* Wait for devices to suspend */
- mdelay(5);
- case HCCONTROL_USB_SUSPEND:
- break;
- case HCCONTROL_USB_RESUME:
- isp116x_write_reg32(isp116x, HCCONTROL,
- (val & ~HCCONTROL_HCFS) |
- HCCONTROL_USB_RESET);
- case HCCONTROL_USB_RESET:
- ret = -EBUSY;
- break;
- default:
- ret = -EINVAL;
- }
-
- spin_unlock_irqrestore(&isp116x->lock, flags);
- return ret;
-}
-
-static int isp116x_bus_resume(struct usb_hcd *hcd)
-{
- struct isp116x *isp116x = hcd_to_isp116x(hcd);
- u32 val;
- int ret = -EINPROGRESS;
-
- msleep(5);
- spin_lock_irq(&isp116x->lock);
-
- val = isp116x_read_reg32(isp116x, HCCONTROL);
- switch (val & HCCONTROL_HCFS) {
- case HCCONTROL_USB_SUSPEND:
- val &= ~HCCONTROL_HCFS;
- val |= HCCONTROL_USB_RESUME;
- isp116x_write_reg32(isp116x, HCCONTROL, val);
- case HCCONTROL_USB_RESUME:
- break;
- case HCCONTROL_USB_OPER:
- /* Without setting power_state here the
- SUSPENDED state won't be removed from
- sysfs/usbN/power.state as a response to remote
- wakeup. Maybe in the future. */
- hcd->self.root_hub->dev.power.power_state = PMSG_ON;
- ret = 0;
- break;
- default:
- ret = -EBUSY;
- }
-
- if (ret != -EINPROGRESS) {
- spin_unlock_irq(&isp116x->lock);
- return ret;
- }
-
- val = isp116x->rhdesca & RH_A_NDP;
- while (val--) {
- u32 stat =
- isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1);
- /* force global, not selective, resume */
- if (!(stat & RH_PS_PSS))
- continue;
- DBG("%s: Resuming port %d\n", __func__, val);
- isp116x_write_reg32(isp116x, RH_PS_POCI, val
- ? HCRHPORT2 : HCRHPORT1);
- }
- spin_unlock_irq(&isp116x->lock);
-
- hcd->state = HC_STATE_RESUMING;
- mdelay(20);
-
- /* Go operational */
- spin_lock_irq(&isp116x->lock);
- val = isp116x_read_reg32(isp116x, HCCONTROL);
- isp116x_write_reg32(isp116x, HCCONTROL,
- (val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER);
- spin_unlock_irq(&isp116x->lock);
- /* see analogous comment above */
- hcd->self.root_hub->dev.power.power_state = PMSG_ON;
- hcd->state = HC_STATE_RUNNING;
-
- return 0;
-}
-
-
-#else
-
-#define isp116x_bus_suspend NULL
-#define isp116x_bus_resume NULL
-
-#endif
-
/*-----------------------------------------------------------------*/
-#ifdef STUB_DEBUG_FILE
-
-static inline void create_debug_file(struct isp116x *isp116x)
-{
-}
-
-static inline void remove_debug_file(struct isp116x *isp116x)
-{
-}
-
-#else
-
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
+#ifdef CONFIG_DEBUG_FS
static void dump_irq(struct seq_file *s, char *label, u16 mask)
{
@@ -1311,13 +1174,9 @@ static void dump_int(struct seq_file *s, char *label, u32 mask)
mask & HCINT_SF ? " sof" : "", mask & HCINT_SO ? " so" : "");
}
-static int proc_isp116x_show(struct seq_file *s, void *unused)
+static int isp116x_show_dbg(struct seq_file *s, void *unused)
{
struct isp116x *isp116x = s->private;
- struct isp116x_ep *ep;
- struct urb *urb;
- unsigned i;
- char *str;
seq_printf(s, "%s\n%s version %s\n",
isp116x_to_hcd(isp116x)->product_desc, hcd_name,
@@ -1333,105 +1192,50 @@ static int proc_isp116x_show(struct seq_file *s, void *unused)
}
spin_lock_irq(&isp116x->lock);
-
dump_irq(s, "hc_irq_enable", isp116x_read_reg16(isp116x, HCuPINTENB));
dump_irq(s, "hc_irq_status", isp116x_read_reg16(isp116x, HCuPINT));
dump_int(s, "hc_int_enable", isp116x_read_reg32(isp116x, HCINTENB));
dump_int(s, "hc_int_status", isp116x_read_reg32(isp116x, HCINTSTAT));
-
- list_for_each_entry(ep, &isp116x->async, schedule) {
-
- switch (ep->nextpid) {
- case USB_PID_IN:
- str = "in";
- break;
- case USB_PID_OUT:
- str = "out";
- break;
- case USB_PID_SETUP:
- str = "setup";
- break;
- case USB_PID_ACK:
- str = "status";
- break;
- default:
- str = "?";
- break;
- };
- seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep,
- ep->epnum, str, ep->maxpacket);
- list_for_each_entry(urb, &ep->hep->urb_list, urb_list) {
- seq_printf(s, " urb%p, %d/%d\n", urb,
- urb->actual_length,
- urb->transfer_buffer_length);
- }
- }
- if (!list_empty(&isp116x->async))
- seq_printf(s, "\n");
-
- seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
-
- for (i = 0; i < PERIODIC_SIZE; i++) {
- ep = isp116x->periodic[i];
- if (!ep)
- continue;
- seq_printf(s, "%2d [%3d]:\n", i, isp116x->load[i]);
-
- /* DUMB: prints shared entries multiple times */
- do {
- seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n",
- ep->period, ep,
- (ep->udev->speed ==
- USB_SPEED_FULL) ? "" : "ls ",
- ep->udev->devnum, ep->epnum,
- (ep->epnum ==
- 0) ? "" : ((ep->nextpid ==
- USB_PID_IN) ? "in" : "out"),
- ep->maxpacket);
- ep = ep->next;
- } while (ep);
- }
+ isp116x_show_regs_seq(isp116x, s);
spin_unlock_irq(&isp116x->lock);
seq_printf(s, "\n");
return 0;
}
-static int proc_isp116x_open(struct inode *inode, struct file *file)
+static int isp116x_open_seq(struct inode *inode, struct file *file)
{
- return single_open(file, proc_isp116x_show, PDE(inode)->data);
+ return single_open(file, isp116x_show_dbg, inode->u.generic_ip);
}
-static struct file_operations proc_ops = {
- .open = proc_isp116x_open,
+static struct file_operations isp116x_debug_fops = {
+ .open = isp116x_open_seq,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
-/* expect just one isp116x per system */
-static const char proc_filename[] = "driver/isp116x";
-
-static void create_debug_file(struct isp116x *isp116x)
+static int create_debug_file(struct isp116x *isp116x)
{
- struct proc_dir_entry *pde;
-
- pde = create_proc_entry(proc_filename, 0, NULL);
- if (pde == NULL)
- return;
-
- pde->proc_fops = &proc_ops;
- pde->data = isp116x;
- isp116x->pde = pde;
+ isp116x->dentry = debugfs_create_file(hcd_name,
+ S_IRUGO, NULL, isp116x,
+ &isp116x_debug_fops);
+ if (!isp116x->dentry)
+ return -ENOMEM;
+ return 0;
}
static void remove_debug_file(struct isp116x *isp116x)
{
- if (isp116x->pde)
- remove_proc_entry(proc_filename, NULL);
+ debugfs_remove(isp116x->dentry);
}
-#endif
+#else
+
+#define create_debug_file(d) 0
+#define remove_debug_file(d) do{}while(0)
+
+#endif /* CONFIG_DEBUG_FS */
/*-----------------------------------------------------------------*/
@@ -1466,7 +1270,7 @@ static int isp116x_reset(struct usb_hcd *hcd)
struct isp116x *isp116x = hcd_to_isp116x(hcd);
unsigned long t;
u16 clkrdy = 0;
- int ret = 0, timeout = 15 /* ms */ ;
+ int ret, timeout = 15 /* ms */ ;
ret = isp116x_sw_reset(isp116x);
if (ret)
@@ -1482,7 +1286,7 @@ static int isp116x_reset(struct usb_hcd *hcd)
break;
}
if (!clkrdy) {
- ERR("Clock not ready after 20ms\n");
+ ERR("Clock not ready after %dms\n", timeout);
/* After sw_reset the clock won't report to be ready, if
H_WAKEUP pin is high. */
ERR("Please make sure that the H_WAKEUP pin is pulled low!\n");
@@ -1572,7 +1376,8 @@ static int isp116x_start(struct usb_hcd *hcd)
val = 0;
if (board->remote_wakeup_enable) {
- hcd->can_wakeup = 1;
+ if (!device_can_wakeup(hcd->self.controller))
+ device_init_wakeup(hcd->self.controller, 1);
val |= RH_HS_DRWE;
}
isp116x_write_reg32(isp116x, HCRHSTATUS, val);
@@ -1600,12 +1405,126 @@ static int isp116x_start(struct usb_hcd *hcd)
isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS);
isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS);
- isp116x_show_regs(isp116x);
+ isp116x_show_regs_log(isp116x);
spin_unlock_irqrestore(&isp116x->lock, flags);
return 0;
}
-/*-----------------------------------------------------------------*/
+#ifdef CONFIG_PM
+
+static int isp116x_bus_suspend(struct usb_hcd *hcd)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ unsigned long flags;
+ u32 val;
+ int ret = 0;
+
+ spin_lock_irqsave(&isp116x->lock, flags);
+
+ val = isp116x_read_reg32(isp116x, HCCONTROL);
+ switch (val & HCCONTROL_HCFS) {
+ case HCCONTROL_USB_OPER:
+ val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE);
+ val |= HCCONTROL_USB_SUSPEND;
+ if (device_may_wakeup(&hcd->self.root_hub->dev))
+ val |= HCCONTROL_RWE;
+ /* Wait for usb transfers to finish */
+ mdelay(2);
+ isp116x_write_reg32(isp116x, HCCONTROL, val);
+ /* Wait for devices to suspend */
+ mdelay(5);
+ case HCCONTROL_USB_SUSPEND:
+ break;
+ case HCCONTROL_USB_RESUME:
+ isp116x_write_reg32(isp116x, HCCONTROL,
+ (val & ~HCCONTROL_HCFS) |
+ HCCONTROL_USB_RESET);
+ case HCCONTROL_USB_RESET:
+ ret = -EBUSY;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&isp116x->lock, flags);
+ return ret;
+}
+
+static int isp116x_bus_resume(struct usb_hcd *hcd)
+{
+ struct isp116x *isp116x = hcd_to_isp116x(hcd);
+ u32 val;
+
+ msleep(5);
+ spin_lock_irq(&isp116x->lock);
+
+ val = isp116x_read_reg32(isp116x, HCCONTROL);
+ switch (val & HCCONTROL_HCFS) {
+ case HCCONTROL_USB_SUSPEND:
+ val &= ~HCCONTROL_HCFS;
+ val |= HCCONTROL_USB_RESUME;
+ isp116x_write_reg32(isp116x, HCCONTROL, val);
+ case HCCONTROL_USB_RESUME:
+ break;
+ case HCCONTROL_USB_OPER:
+ spin_unlock_irq(&isp116x->lock);
+ /* Without setting power_state here the
+ SUSPENDED state won't be removed from
+ sysfs/usbN/power.state as a response to remote
+ wakeup. Maybe in the future. */
+ hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+ return 0;
+ default:
+ /* HCCONTROL_USB_RESET: this may happen, when during
+ suspension the HC lost power. Reinitialize completely */
+ spin_unlock_irq(&isp116x->lock);
+ DBG("Chip has been reset while suspended. Reinit from scratch.\n");
+ isp116x_reset(hcd);
+ isp116x_start(hcd);
+ isp116x_hub_control(hcd, SetPortFeature,
+ USB_PORT_FEAT_POWER, 1, NULL, 0);
+ if ((isp116x->rhdesca & RH_A_NDP) == 2)
+ isp116x_hub_control(hcd, SetPortFeature,
+ USB_PORT_FEAT_POWER, 2, NULL, 0);
+ hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+ return 0;
+ }
+
+ val = isp116x->rhdesca & RH_A_NDP;
+ while (val--) {
+ u32 stat =
+ isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1);
+ /* force global, not selective, resume */
+ if (!(stat & RH_PS_PSS))
+ continue;
+ DBG("%s: Resuming port %d\n", __func__, val);
+ isp116x_write_reg32(isp116x, RH_PS_POCI, val
+ ? HCRHPORT2 : HCRHPORT1);
+ }
+ spin_unlock_irq(&isp116x->lock);
+
+ hcd->state = HC_STATE_RESUMING;
+ msleep(20);
+
+ /* Go operational */
+ spin_lock_irq(&isp116x->lock);
+ val = isp116x_read_reg32(isp116x, HCCONTROL);
+ isp116x_write_reg32(isp116x, HCCONTROL,
+ (val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER);
+ spin_unlock_irq(&isp116x->lock);
+ /* see analogous comment above */
+ hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+ hcd->state = HC_STATE_RUNNING;
+
+ return 0;
+}
+
+#else
+
+#define isp116x_bus_suspend NULL
+#define isp116x_bus_resume NULL
+
+#endif
static struct hc_driver isp116x_hc_driver = {
.description = hcd_name,
@@ -1735,12 +1654,19 @@ static int __init isp116x_probe(struct platform_device *pdev)
}
ret = usb_add_hcd(hcd, irq, SA_INTERRUPT);
- if (ret != 0)
+ if (ret)
goto err6;
- create_debug_file(isp116x);
+ ret = create_debug_file(isp116x);
+ if (ret) {
+ ERR("Couldn't create debugfs entry\n");
+ goto err7;
+ }
+
return 0;
+ err7:
+ usb_remove_hcd(hcd);
err6:
usb_put_hcd(hcd);
err5:
@@ -1762,13 +1688,9 @@ static int __init isp116x_probe(struct platform_device *pdev)
*/
static int isp116x_suspend(struct platform_device *dev, pm_message_t state)
{
- int ret = 0;
-
- VDBG("%s: state %x\n", __func__, state);
-
+ VDBG("%s: state %x\n", __func__, state.event);
dev->dev.power.power_state = state;
-
- return ret;
+ return 0;
}
/*
@@ -1776,13 +1698,9 @@ static int isp116x_suspend(struct platform_device *dev, pm_message_t state)
*/
static int isp116x_resume(struct platform_device *dev)
{
- int ret = 0;
-
- VDBG("%s: state %x\n", __func__, dev->dev.power.power_state);
-
+ VDBG("%s: state %x\n", __func__, dev->power.power_state.event);
dev->dev.power.power_state = PMSG_ON;
-
- return ret;
+ return 0;
}
#else
diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h
index c6fec96785fe..a1b7c3813d3a 100644
--- a/drivers/usb/host/isp116x.h
+++ b/drivers/usb/host/isp116x.h
@@ -259,7 +259,7 @@ struct isp116x {
struct isp116x_platform_data *board;
- struct proc_dir_entry *pde;
+ struct dentry *dentry;
unsigned long stat1, stat2, stat4, stat8, stat16;
/* HC registers */
@@ -450,7 +450,7 @@ static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg,
isp116x_write_data32(isp116x, (u32) val);
}
-#define isp116x_show_reg(d,r) { \
+#define isp116x_show_reg_log(d,r,s) { \
if ((r) < 0x20) { \
DBG("%-12s[%02x]: %08x\n", #r, \
r, isp116x_read_reg32(d, r)); \
@@ -459,35 +459,60 @@ static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg,
r, isp116x_read_reg16(d, r)); \
} \
}
+#define isp116x_show_reg_seq(d,r,s) { \
+ if ((r) < 0x20) { \
+ seq_printf(s, "%-12s[%02x]: %08x\n", #r, \
+ r, isp116x_read_reg32(d, r)); \
+ } else { \
+ seq_printf(s, "%-12s[%02x]: %04x\n", #r, \
+ r, isp116x_read_reg16(d, r)); \
+ } \
+}
-static inline void isp116x_show_regs(struct isp116x *isp116x)
+#define isp116x_show_regs(d,type,s) { \
+ isp116x_show_reg_##type(d, HCREVISION, s); \
+ isp116x_show_reg_##type(d, HCCONTROL, s); \
+ isp116x_show_reg_##type(d, HCCMDSTAT, s); \
+ isp116x_show_reg_##type(d, HCINTSTAT, s); \
+ isp116x_show_reg_##type(d, HCINTENB, s); \
+ isp116x_show_reg_##type(d, HCFMINTVL, s); \
+ isp116x_show_reg_##type(d, HCFMREM, s); \
+ isp116x_show_reg_##type(d, HCFMNUM, s); \
+ isp116x_show_reg_##type(d, HCLSTHRESH, s); \
+ isp116x_show_reg_##type(d, HCRHDESCA, s); \
+ isp116x_show_reg_##type(d, HCRHDESCB, s); \
+ isp116x_show_reg_##type(d, HCRHSTATUS, s); \
+ isp116x_show_reg_##type(d, HCRHPORT1, s); \
+ isp116x_show_reg_##type(d, HCRHPORT2, s); \
+ isp116x_show_reg_##type(d, HCHWCFG, s); \
+ isp116x_show_reg_##type(d, HCDMACFG, s); \
+ isp116x_show_reg_##type(d, HCXFERCTR, s); \
+ isp116x_show_reg_##type(d, HCuPINT, s); \
+ isp116x_show_reg_##type(d, HCuPINTENB, s); \
+ isp116x_show_reg_##type(d, HCCHIPID, s); \
+ isp116x_show_reg_##type(d, HCSCRATCH, s); \
+ isp116x_show_reg_##type(d, HCITLBUFLEN, s); \
+ isp116x_show_reg_##type(d, HCATLBUFLEN, s); \
+ isp116x_show_reg_##type(d, HCBUFSTAT, s); \
+ isp116x_show_reg_##type(d, HCRDITL0LEN, s); \
+ isp116x_show_reg_##type(d, HCRDITL1LEN, s); \
+}
+
+/*
+ Dump registers for debugfs.
+*/
+static inline void isp116x_show_regs_seq(struct isp116x *isp116x,
+ struct seq_file *s)
+{
+ isp116x_show_regs(isp116x, seq, s);
+}
+
+/*
+ Dump registers to syslog.
+*/
+static inline void isp116x_show_regs_log(struct isp116x *isp116x)
{
- isp116x_show_reg(isp116x, HCREVISION);
- isp116x_show_reg(isp116x, HCCONTROL);
- isp116x_show_reg(isp116x, HCCMDSTAT);
- isp116x_show_reg(isp116x, HCINTSTAT);
- isp116x_show_reg(isp116x, HCINTENB);
- isp116x_show_reg(isp116x, HCFMINTVL);
- isp116x_show_reg(isp116x, HCFMREM);
- isp116x_show_reg(isp116x, HCFMNUM);
- isp116x_show_reg(isp116x, HCLSTHRESH);
- isp116x_show_reg(isp116x, HCRHDESCA);
- isp116x_show_reg(isp116x, HCRHDESCB);
- isp116x_show_reg(isp116x, HCRHSTATUS);
- isp116x_show_reg(isp116x, HCRHPORT1);
- isp116x_show_reg(isp116x, HCRHPORT2);
- isp116x_show_reg(isp116x, HCHWCFG);
- isp116x_show_reg(isp116x, HCDMACFG);
- isp116x_show_reg(isp116x, HCXFERCTR);
- isp116x_show_reg(isp116x, HCuPINT);
- isp116x_show_reg(isp116x, HCuPINTENB);
- isp116x_show_reg(isp116x, HCCHIPID);
- isp116x_show_reg(isp116x, HCSCRATCH);
- isp116x_show_reg(isp116x, HCITLBUFLEN);
- isp116x_show_reg(isp116x, HCATLBUFLEN);
- isp116x_show_reg(isp116x, HCBUFSTAT);
- isp116x_show_reg(isp116x, HCRDITL0LEN);
- isp116x_show_reg(isp116x, HCRDITL1LEN);
+ isp116x_show_regs(isp116x, log, NULL);
}
#if defined(URB_TRACE)
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index bf1d9abc07ac..a4b12404ae08 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -75,13 +75,6 @@
*/
#include <linux/config.h>
-
-#ifdef CONFIG_USB_DEBUG
-# define DEBUG
-#else
-# undef DEBUG
-#endif
-
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
@@ -802,7 +795,6 @@ static int ohci_restart (struct ohci_hcd *ohci)
int temp;
int i;
struct urb_priv *priv;
- struct usb_device *root = ohci_to_hcd(ohci)->self.root_hub;
/* mark any devices gone, so they do nothing till khubd disconnects.
* recycle any "live" eds/tds (and urbs) right away.
@@ -811,11 +803,7 @@ static int ohci_restart (struct ohci_hcd *ohci)
*/
spin_lock_irq(&ohci->lock);
disable (ohci);
- for (i = 0; i < root->maxchild; i++) {
- if (root->children [i])
- usb_set_device_state (root->children[i],
- USB_STATE_NOTATTACHED);
- }
+ usb_root_hub_lost_power(ohci_to_hcd(ohci)->self.root_hub);
if (!list_empty (&ohci->pending))
ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) {
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 72e3b12a1926..4b2226d77b34 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -372,7 +372,7 @@ done:
& ohci->hc_control)
== OHCI_USB_OPER
&& time_after (jiffies, ohci->next_statechange)
- && usb_trylock_device (hcd->self.root_hub)
+ && usb_trylock_device (hcd->self.root_hub) == 0
) {
ohci_vdbg (ohci, "autosuspend\n");
(void) ohci_bus_suspend (hcd);
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index 9d65ec307990..acde8868da21 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -26,18 +26,12 @@
#include <asm/mach-types.h>
#include <asm/hardware.h>
#include <asm/arch/pxa-regs.h>
-
-
-#define PMM_NPS_MODE 1
-#define PMM_GLOBAL_MODE 2
-#define PMM_PERPORT_MODE 3
+#include <asm/arch/ohci.h>
#define PXA_UHC_MAX_PORTNUM 3
#define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 )
-static int pxa27x_ohci_pmm_state;
-
/*
PMM_NPS_MODE -- PMM Non-power switching mode
Ports are powered continuously.
@@ -50,8 +44,6 @@ static int pxa27x_ohci_pmm_state;
*/
static int pxa27x_ohci_select_pmm( int mode )
{
- pxa27x_ohci_pmm_state = mode;
-
switch ( mode ) {
case PMM_NPS_MODE:
UHCRHDA |= RH_A_NPS;
@@ -71,7 +63,6 @@ static int pxa27x_ohci_select_pmm( int mode )
"Invalid mode %d, set to non-power switch mode.\n",
mode );
- pxa27x_ohci_pmm_state = PMM_NPS_MODE;
UHCRHDA |= RH_A_NPS;
}
@@ -82,8 +73,13 @@ extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
-static void pxa27x_start_hc(struct platform_device *dev)
+static int pxa27x_start_hc(struct device *dev)
{
+ int retval = 0;
+ struct pxaohci_platform_data *inf;
+
+ inf = dev->platform_data;
+
pxa_set_cken(CKEN10_USBHOST, 1);
UHCHR |= UHCHR_FHR;
@@ -94,21 +90,11 @@ static void pxa27x_start_hc(struct platform_device *dev)
while (UHCHR & UHCHR_FSBIR)
cpu_relax();
- /* This could be properly abstracted away through the
- device data the day more machines are supported and
- their differences can be figured out correctly. */
- if (machine_is_mainstone()) {
- /* setup Port1 GPIO pin. */
- pxa_gpio_mode( 88 | GPIO_ALT_FN_1_IN); /* USBHPWR1 */
- pxa_gpio_mode( 89 | GPIO_ALT_FN_2_OUT); /* USBHPEN1 */
-
- /* Set the Power Control Polarity Low and Power Sense
- Polarity Low to active low. Supply power to USB ports. */
- UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) &
- ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE);
+ if (inf->init)
+ retval = inf->init(dev);
- pxa27x_ohci_pmm_state = PMM_PERPORT_MODE;
- }
+ if (retval < 0)
+ return retval;
UHCHR &= ~UHCHR_SSE;
@@ -117,10 +103,19 @@ static void pxa27x_start_hc(struct platform_device *dev)
/* Clear any OTG Pin Hold */
if (PSSR & PSSR_OTGPH)
PSSR |= PSSR_OTGPH;
+
+ return 0;
}
-static void pxa27x_stop_hc(struct platform_device *dev)
+static void pxa27x_stop_hc(struct device *dev)
{
+ struct pxaohci_platform_data *inf;
+
+ inf = dev->platform_data;
+
+ if (inf->exit)
+ inf->exit(dev);
+
UHCHR |= UHCHR_FHR;
udelay(11);
UHCHR &= ~UHCHR_FHR;
@@ -147,22 +142,27 @@ static void pxa27x_stop_hc(struct platform_device *dev)
* through the hotplug entry's driver_data.
*
*/
-int usb_hcd_pxa27x_probe (const struct hc_driver *driver,
- struct platform_device *dev)
+int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device *pdev)
{
int retval;
struct usb_hcd *hcd;
+ struct pxaohci_platform_data *inf;
- if (dev->resource[1].flags != IORESOURCE_IRQ) {
+ inf = pdev->dev.platform_data;
+
+ if (!inf)
+ return -ENODEV;
+
+ if (pdev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug ("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
}
- hcd = usb_create_hcd (driver, &dev->dev, "pxa27x");
+ hcd = usb_create_hcd (driver, &pdev->dev, "pxa27x");
if (!hcd)
return -ENOMEM;
- hcd->rsrc_start = dev->resource[0].start;
- hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
+ hcd->rsrc_start = pdev->resource[0].start;
+ hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed");
@@ -177,18 +177,22 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver,
goto err2;
}
- pxa27x_start_hc(dev);
+ if ((retval = pxa27x_start_hc(&pdev->dev)) < 0) {
+ pr_debug("pxa27x_start_hc failed");
+ goto err3;
+ }
/* Select Power Management Mode */
- pxa27x_ohci_select_pmm(pxa27x_ohci_pmm_state);
+ pxa27x_ohci_select_pmm(inf->port_mode);
ohci_hcd_init(hcd_to_ohci(hcd));
- retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT);
+ retval = usb_add_hcd(hcd, pdev->resource[1].start, SA_INTERRUPT);
if (retval == 0)
return retval;
- pxa27x_stop_hc(dev);
+ pxa27x_stop_hc(&pdev->dev);
+ err3:
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
@@ -211,10 +215,10 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver,
* context, normally "rmmod", "apmd", or something similar.
*
*/
-void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *dev)
+void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev)
{
usb_remove_hcd(hcd);
- pxa27x_stop_hc(dev);
+ pxa27x_stop_hc(&pdev->dev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
@@ -292,15 +296,12 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
static int ohci_hcd_pxa27x_drv_probe(struct platform_device *pdev)
{
- int ret;
-
pr_debug ("In ohci_hcd_pxa27x_drv_probe");
if (usb_disabled())
return -ENODEV;
- ret = usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, pdev);
- return ret;
+ return usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, pdev);
}
static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev)
@@ -308,31 +309,55 @@ static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_hcd_pxa27x_remove(hcd, pdev);
+ platform_set_drvdata(pdev, NULL);
return 0;
}
-static int ohci_hcd_pxa27x_drv_suspend(struct platform_device *dev, pm_message_t state)
+#ifdef CONFIG_PM
+static int ohci_hcd_pxa27x_drv_suspend(struct platform_device *pdev, pm_message_t state)
{
-// struct usb_hcd *hcd = platform_get_drvdata(dev);
- printk("%s: not implemented yet\n", __FUNCTION__);
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ pxa27x_stop_hc(&pdev->dev);
+ hcd->state = HC_STATE_SUSPENDED;
+ pdev->dev.power.power_state = PMSG_SUSPEND;
return 0;
}
-static int ohci_hcd_pxa27x_drv_resume(struct platform_device *dev)
+static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev)
{
-// struct usb_hcd *hcd = platform_get_drvdata(dev);
- printk("%s: not implemented yet\n", __FUNCTION__);
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int status;
+
+ if (time_before(jiffies, ohci->next_statechange))
+ msleep(5);
+ ohci->next_statechange = jiffies;
+
+ if ((status = pxa27x_start_hc(&pdev->dev)) < 0)
+ return status;
+
+ pdev->dev.power.power_state = PMSG_ON;
+ usb_hcd_resume_root_hub(hcd);
return 0;
}
+#endif
static struct platform_driver ohci_hcd_pxa27x_driver = {
.probe = ohci_hcd_pxa27x_drv_probe,
.remove = ohci_hcd_pxa27x_drv_remove,
+#ifdef CONFIG_PM
.suspend = ohci_hcd_pxa27x_drv_suspend,
.resume = ohci_hcd_pxa27x_drv_resume,
+#endif
.driver = {
.name = "pxa27x-ohci",
},
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index e46528c825bf..3ef2c0cdf1db 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -9,12 +9,6 @@
*/
#include <linux/config.h>
-#ifdef CONFIG_USB_DEBUG
-#define DEBUG
-#else
-#undef DEBUG
-#endif
-
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index a7722a6a5a5b..517360b77d8e 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -32,13 +32,6 @@
#undef PACKET_TRACE
#include <linux/config.h>
-
-#ifdef CONFIG_USB_DEBUG
-# define DEBUG
-#else
-# undef DEBUG
-#endif
-
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -1581,7 +1574,9 @@ sl811h_start(struct usb_hcd *hcd)
hcd->state = HC_STATE_RUNNING;
if (sl811->board) {
- hcd->can_wakeup = sl811->board->can_wakeup;
+ if (!device_can_wakeup(hcd->self.controller))
+ device_init_wakeup(hcd->self.controller,
+ sl811->board->can_wakeup);
hcd->power_budget = sl811->board->power * 2;
}
@@ -1805,9 +1800,10 @@ sl811h_resume(struct platform_device *dev)
* let's assume it'd only be powered to enable remote wakeup.
*/
if (dev->dev.power.power_state.event == PM_EVENT_SUSPEND
- || !hcd->can_wakeup) {
+ || !device_can_wakeup(&hcd->self.root_hub->dev)) {
sl811->port1 = 0;
port_power(sl811, 1);
+ usb_root_hub_lost_power(hcd->self.root_hub);
return 0;
}
diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c
index e73faf831b24..5056b7459994 100644
--- a/drivers/usb/host/sl811_cs.c
+++ b/drivers/usb/host/sl811_cs.c
@@ -38,7 +38,7 @@ MODULE_LICENSE("GPL");
/* MACROS */
/*====================================================================*/
-#if defined(DEBUG) || defined(CONFIG_USB_DEBUG) || defined(PCMCIA_DEBUG)
+#if defined(DEBUG) || defined(PCMCIA_DEBUG)
static int pc_debug = 0;
module_param(pc_debug, int, 0644);
@@ -129,7 +129,8 @@ static int sl811_hc_init(struct device *parent, ioaddr_t base_addr, int irq)
resources[2].end = base_addr + 1;
/* The driver core will probe for us. We know sl811-hcd has been
- * initialized already because of the link order dependency.
+ * initialized already because of the link order dependency created
+ * by referencing "sl811h_driver".
*/
platform_dev.name = sl811h_driver.name;
return platform_device_register(&platform_dev);
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index 151154df37fa..5832953086f8 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -2,8 +2,8 @@
* UHCI-specific debugging code. Invaluable when something
* goes wrong, but don't get in my face.
*
- * Kernel visible pointers are surrounded in []'s and bus
- * visible pointers are surrounded in ()'s
+ * Kernel visible pointers are surrounded in []s and bus
+ * visible pointers are surrounded in ()s
*
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999-2001 Johannes Erdfelt
@@ -19,7 +19,7 @@
static struct dentry *uhci_debugfs_root = NULL;
-/* Handle REALLY large printk's so we don't overflow buffers */
+/* Handle REALLY large printks so we don't overflow buffers */
static inline void lprintk(char *buf)
{
char *p;
@@ -160,7 +160,7 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
}
if (active && ni > i) {
- out += sprintf(out, "%*s[skipped %d active TD's]\n", space, "", ni - i);
+ out += sprintf(out, "%*s[skipped %d active TDs]\n", space, "", ni - i);
tmp = ntmp;
td = ntd;
i = ni;
@@ -173,7 +173,7 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
if (list_empty(&urbp->queue_list) || urbp->queued)
goto out;
- out += sprintf(out, "%*sQueued QH's:\n", -space, "--");
+ out += sprintf(out, "%*sQueued QHs:\n", -space, "--");
head = &urbp->queue_list;
tmp = head->next;
@@ -197,7 +197,7 @@ out:
}
#ifdef CONFIG_PROC_FS
-static const char *qh_names[] = {
+static const char * const qh_names[] = {
"skel_int128_qh", "skel_int64_qh",
"skel_int32_qh", "skel_int16_qh",
"skel_int8_qh", "skel_int4_qh",
@@ -464,7 +464,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
} while (tmp != head);
}
- out += sprintf(out, "Skeleton QH's\n");
+ out += sprintf(out, "Skeleton QHs\n");
for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
int shown = 0;
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index ed550132db0b..dfe121d35887 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -23,11 +23,6 @@
*/
#include <linux/config.h>
-#ifdef CONFIG_USB_DEBUG
-#define DEBUG
-#else
-#undef DEBUG
-#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
@@ -67,10 +62,10 @@ Alan Stern"
/*
* debug = 0, no debugging messages
- * debug = 1, dump failed URB's except for stalls
- * debug = 2, dump all failed URB's (including stalls)
+ * debug = 1, dump failed URBs except for stalls
+ * debug = 2, dump all failed URBs (including stalls)
* show all queues in /debug/uhci/[pci_addr]
- * debug = 3, show all TD's in URB's when dumping
+ * debug = 3, show all TDs in URBs when dumping
*/
#ifdef DEBUG
static int debug = 1;
@@ -93,7 +88,7 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
#define FSBR_DELAY msecs_to_jiffies(50)
/* When we timeout an idle transfer for FSBR, we'll switch it over to */
-/* depth first traversal. We'll do it in groups of this number of TD's */
+/* depth first traversal. We'll do it in groups of this number of TDs */
/* to make sure it doesn't hog all of the bandwidth */
#define DEPTH_INTERVAL 5
@@ -478,8 +473,6 @@ static int uhci_start(struct usb_hcd *hcd)
struct dentry *dentry;
hcd->uses_new_polling = 1;
- if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM))
- hcd->can_wakeup = 1; /* Assume it supports PME# */
dentry = debugfs_create_file(hcd->self.bus_name,
S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci,
@@ -573,7 +566,7 @@ static int uhci_start(struct usb_hcd *hcd)
uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;
/* This dummy TD is to work around a bug in Intel PIIX controllers */
- uhci_fill_td(uhci->term_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
+ uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle);
@@ -717,6 +710,7 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
* at the source, so we must turn off PIRQ.
*/
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
+ mb();
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
uhci->hc_inaccessible = 1;
hcd->poll_rh = 0;
@@ -734,10 +728,12 @@ static int uhci_resume(struct usb_hcd *hcd)
dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
- /* We aren't in D3 state anymore, we do that even if dead as I
- * really don't want to keep a stale HCD_FLAG_HW_ACCESSIBLE=0
+ /* Since we aren't in D3 any more, it's safe to set this flag
+ * even if the controller was dead. It might not even be dead
+ * any more, if the firmware or quirks code has reset it.
*/
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ mb();
if (uhci->rh_state == UHCI_RH_RESET) /* Dead */
return 0;
@@ -753,8 +749,12 @@ static int uhci_resume(struct usb_hcd *hcd)
check_and_reset_hc(uhci);
configure_hc(uhci);
- if (uhci->rh_state == UHCI_RH_RESET)
+ if (uhci->rh_state == UHCI_RH_RESET) {
+
+ /* The controller had to be reset */
+ usb_root_hub_lost_power(hcd->self.root_hub);
suspend_rh(uhci, UHCI_RH_SUSPENDED);
+ }
spin_unlock_irq(&uhci->lock);
@@ -880,7 +880,7 @@ static int __init uhci_hcd_init(void)
init_failed:
if (kmem_cache_destroy(uhci_up_cachep))
- warn("not all urb_priv's were freed!");
+ warn("not all urb_privs were freed!");
up_failed:
debugfs_remove(uhci_debugfs_root);
@@ -898,7 +898,7 @@ static void __exit uhci_hcd_cleanup(void)
pci_unregister_driver(&uhci_pci_driver);
if (kmem_cache_destroy(uhci_up_cachep))
- warn("not all urb_priv's were freed!");
+ warn("not all urb_privs were freed!");
debugfs_remove(uhci_debugfs_root);
kfree(errbuf);
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index e576db57a926..8b4b887a7d41 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -71,8 +71,6 @@
#define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
#define USBLEGSUP_RO 0x5040 /* R/O and reserved bits */
-#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */
-
#define UHCI_PTR_BITS cpu_to_le32(0x000F)
#define UHCI_PTR_TERM cpu_to_le32(0x0001)
#define UHCI_PTR_QH cpu_to_le32(0x0002)
@@ -168,9 +166,11 @@ static __le32 inline qh_element(struct uhci_qh *qh) {
#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */
#define TD_TOKEN_PID_MASK 0xFF
-#define uhci_explen(len) ((len) << TD_TOKEN_EXPLEN_SHIFT)
+#define uhci_explen(len) ((((len) - 1) & TD_TOKEN_EXPLEN_MASK) << \
+ TD_TOKEN_EXPLEN_SHIFT)
-#define uhci_expected_length(token) ((((token) >> 21) + 1) & TD_TOKEN_EXPLEN_MASK)
+#define uhci_expected_length(token) ((((token) >> TD_TOKEN_EXPLEN_SHIFT) + \
+ 1) & TD_TOKEN_EXPLEN_MASK)
#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
#define uhci_endpoint(token) (((token) >> 15) & 0xf)
#define uhci_devaddr(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7f)
@@ -223,10 +223,10 @@ static u32 inline td_status(struct uhci_td *td) {
*/
/*
- * The UHCI driver places Interrupt, Control and Bulk into QH's both
- * to group together TD's for one transfer, and also to faciliate queuing
- * of URB's. To make it easy to insert entries into the schedule, we have
- * a skeleton of QH's for each predefined Interrupt latency, low-speed
+ * The UHCI driver places Interrupt, Control and Bulk into QHs both
+ * to group together TDs for one transfer, and also to facilitate queuing
+ * of URBs. To make it easy to insert entries into the schedule, we have
+ * a skeleton of QHs for each predefined Interrupt latency, low-speed
* control, full-speed control and terminating QH (see explanation for
* the terminating QH below).
*
@@ -257,8 +257,8 @@ static u32 inline td_status(struct uhci_td *td) {
* reclamation.
*
* Isochronous transfers are stored before the start of the skeleton
- * schedule and don't use QH's. While the UHCI spec doesn't forbid the
- * use of QH's for Isochronous, it doesn't use them either. And the spec
+ * schedule and don't use QHs. While the UHCI spec doesn't forbid the
+ * use of QHs for Isochronous, it doesn't use them either. And the spec
* says that queues never advance on an error completion status, which
* makes them totally unsuitable for Isochronous transfers.
*/
@@ -359,7 +359,7 @@ struct uhci_hcd {
struct dma_pool *td_pool;
struct uhci_td *term_td; /* Terminating TD, see UHCI bug */
- struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */
+ struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QHs */
spinlock_t lock;
@@ -389,22 +389,22 @@ struct uhci_hcd {
unsigned long resuming_ports;
unsigned long ports_timeout; /* Time to stop signalling */
- /* Main list of URB's currently controlled by this HC */
+ /* Main list of URBs currently controlled by this HC */
struct list_head urb_list;
- /* List of QH's that are done, but waiting to be unlinked (race) */
+ /* List of QHs that are done, but waiting to be unlinked (race) */
struct list_head qh_remove_list;
unsigned int qh_remove_age; /* Age in frames */
- /* List of TD's that are done, but waiting to be freed (race) */
+ /* List of TDs that are done, but waiting to be freed (race) */
struct list_head td_remove_list;
unsigned int td_remove_age; /* Age in frames */
- /* List of asynchronously unlinked URB's */
+ /* List of asynchronously unlinked URBs */
struct list_head urb_remove_list;
unsigned int urb_remove_age; /* Age in frames */
- /* List of URB's awaiting completion callback */
+ /* List of URBs awaiting completion callback */
struct list_head complete_list;
int rh_numports; /* Number of root-hub ports */
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 7e46887d9e12..b6076004a437 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -80,7 +80,7 @@ static inline void uhci_fill_td(struct uhci_td *td, u32 status,
}
/*
- * We insert Isochronous URB's directly into the frame list at the beginning
+ * We insert Isochronous URBs directly into the frame list at the beginning
*/
static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum)
{
@@ -369,7 +369,7 @@ static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, stru
uhci_fixup_toggle(urb,
uhci_toggle(td_token(lltd)) ^ 1));
- /* All qh's in the queue need to link to the next queue */
+ /* All qhs in the queue need to link to the next queue */
urbp->qh->link = eurbp->qh->link;
wmb(); /* Make sure we flush everything */
@@ -502,7 +502,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
}
/* Check to see if the remove list is empty. Set the IOC bit */
- /* to force an interrupt so we can remove the TD's*/
+ /* to force an interrupt so we can remove the TDs*/
if (list_empty(&uhci->td_remove_list))
uhci_set_next_interrupt(uhci);
@@ -596,7 +596,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
return -ENOMEM;
uhci_add_td_to_urb(urb, td);
- uhci_fill_td(td, status, destination | uhci_explen(7),
+ uhci_fill_td(td, status, destination | uhci_explen(8),
urb->setup_dma);
/*
@@ -612,7 +612,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
}
/*
- * Build the DATA TD's
+ * Build the DATA TDs
*/
while (len > 0) {
int pktsze = len;
@@ -628,7 +628,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
destination ^= TD_TOKEN_TOGGLE;
uhci_add_td_to_urb(urb, td);
- uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1),
+ uhci_fill_td(td, status, destination | uhci_explen(pktsze),
data);
data += pktsze;
@@ -658,7 +658,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur
uhci_add_td_to_urb(urb, td);
uhci_fill_td(td, status | TD_CTRL_IOC,
- destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0);
+ destination | uhci_explen(0), 0);
qh = uhci_alloc_qh(uhci);
if (!qh)
@@ -744,7 +744,7 @@ static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
urb->actual_length = 0;
- /* The rest of the TD's (but the last) are data */
+ /* The rest of the TDs (but the last) are data */
tmp = tmp->next;
while (tmp != head && tmp->next != head) {
unsigned int ctrlstat;
@@ -848,7 +848,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
status |= TD_CTRL_SPD;
/*
- * Build the DATA TD's
+ * Build the DATA TDs
*/
do { /* Allow zero length packets */
int pktsze = maxsze;
@@ -864,7 +864,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
return -ENOMEM;
uhci_add_td_to_urb(urb, td);
- uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) |
+ uhci_fill_td(td, status, destination | uhci_explen(pktsze) |
(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
data);
@@ -890,7 +890,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb
return -ENOMEM;
uhci_add_td_to_urb(urb, td);
- uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) |
+ uhci_fill_td(td, status, destination | uhci_explen(0) |
(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
data);
@@ -1025,7 +1025,7 @@ static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsig
list_for_each_entry(up, &uhci->urb_list, urb_list) {
struct urb *u = up->urb;
- /* look for pending URB's with identical pipe handle */
+ /* look for pending URBs with identical pipe handle */
if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
(u->status == -EINPROGRESS) && (u != urb)) {
if (!last_urb)
@@ -1092,7 +1092,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
return -ENOMEM;
uhci_add_td_to_urb(urb, td);
- uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1),
+ uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length),
urb->transfer_dma + urb->iso_frame_desc[i].offset);
if (i + 1 >= urb->number_of_packets)
@@ -1355,7 +1355,7 @@ static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
uhci_delete_queued_urb(uhci, urb);
- /* The interrupt loop will reclaim the QH's */
+ /* The interrupt loop will reclaim the QHs */
uhci_remove_qh(uhci, urbp->qh);
urbp->qh = NULL;
}
@@ -1413,7 +1413,7 @@ static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
list_for_each_entry(td, head, list) {
/*
* Make sure we don't do the last one (since it'll have the
- * TERM bit set) as well as we skip every so many TD's to
+ * TERM bit set) as well as we skip every so many TDs to
* make sure it doesn't hog the bandwidth
*/
if (td->list.next != head && (count % DEPTH_INTERVAL) ==
diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
index 1d973bcf56aa..049871145d63 100644
--- a/drivers/usb/image/mdc800.c
+++ b/drivers/usb/image/mdc800.c
@@ -962,7 +962,6 @@ MODULE_DEVICE_TABLE (usb, mdc800_table);
*/
static struct usb_driver mdc800_usb_driver =
{
- .owner = THIS_MODULE,
.name = "mdc800",
.probe = mdc800_usb_probe,
.disconnect = mdc800_usb_disconnect,
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index 950543aa5ac7..458f2acdeb0a 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -160,7 +160,6 @@ static void mts_usb_disconnect(struct usb_interface *intf);
static struct usb_device_id mts_usb_ids [];
static struct usb_driver mts_usb_driver = {
- .owner = THIS_MODULE,
.name = "microtekX6",
.probe = mts_usb_probe,
.disconnect = mts_usb_disconnect,
diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig
index 1e53934907c0..509dd0a04c54 100644
--- a/drivers/usb/input/Kconfig
+++ b/drivers/usb/input/Kconfig
@@ -273,6 +273,20 @@ config USB_ATI_REMOTE
To compile this driver as a module, choose M here: the module will be
called ati_remote.
+config USB_ATI_REMOTE2
+ tristate "ATI / Philips USB RF remote control"
+ depends on USB && INPUT
+ ---help---
+ Say Y here if you want to use an ATI or Philips USB RF remote control.
+ These are RF remotes with USB receivers.
+ ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards
+ and is also available as a separate product.
+ This driver provides mouse pointer, left and right mouse buttons,
+ and maps all the other remote buttons to keypress events.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ati_remote2.
+
config USB_KEYSPAN_REMOTE
tristate "Keyspan DMR USB remote control (EXPERIMENTAL)"
depends on USB && INPUT && EXPERIMENTAL
diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile
index 07cb17db42fc..d512d9f488fe 100644
--- a/drivers/usb/input/Makefile
+++ b/drivers/usb/input/Makefile
@@ -28,6 +28,7 @@ endif
obj-$(CONFIG_USB_AIPTEK) += aiptek.o
obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o
+obj-$(CONFIG_USB_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_USB_HID) += usbhid.o
obj-$(CONFIG_USB_KBD) += usbkbd.o
obj-$(CONFIG_USB_KBTAB) += kbtab.o
diff --git a/drivers/usb/input/acecad.c b/drivers/usb/input/acecad.c
index a32558b4048e..df29b8078b54 100644
--- a/drivers/usb/input/acecad.c
+++ b/drivers/usb/input/acecad.c
@@ -261,7 +261,6 @@ static struct usb_device_id usb_acecad_id_table [] = {
MODULE_DEVICE_TABLE(usb, usb_acecad_id_table);
static struct usb_driver usb_acecad_driver = {
- .owner = THIS_MODULE,
.name = "usb_acecad",
.probe = usb_acecad_probe,
.disconnect = usb_acecad_disconnect,
diff --git a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c
index 1c3b472a3bca..a6693b0d1c4c 100644
--- a/drivers/usb/input/aiptek.c
+++ b/drivers/usb/input/aiptek.c
@@ -338,7 +338,7 @@ struct aiptek {
* the bitmap which comes from the tablet. This hides the
* issue that the F_keys are not sequentially numbered.
*/
-static int macroKeyEvents[] = {
+static const int macroKeyEvents[] = {
KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11,
KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17,
@@ -2093,7 +2093,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
/* Programming the tablet macro keys needs to be done with a for loop
* as the keycodes are discontiguous.
*/
- for (i = 0; i < sizeof(macroKeyEvents) / sizeof(macroKeyEvents[0]); ++i)
+ for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i)
set_bit(macroKeyEvents[i], inputdev->keybit);
/*
@@ -2103,7 +2103,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
* values.
*/
input_set_abs_params(inputdev, ABS_X, 0, 2999, 0, 0);
- input_set_abs_params(inputdev, ABS_X, 0, 2249, 0, 0);
+ input_set_abs_params(inputdev, ABS_Y, 0, 2249, 0, 0);
input_set_abs_params(inputdev, ABS_PRESSURE, 0, 511, 0, 0);
input_set_abs_params(inputdev, ABS_TILT_X, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0);
input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0);
@@ -2135,7 +2135,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
* not an error :-)
*/
- for (i = 0; i < sizeof(speeds) / sizeof(speeds[0]); ++i) {
+ for (i = 0; i < ARRAY_SIZE(speeds); ++i) {
aiptek->curSetting.programmableDelay = speeds[i];
(void)aiptek_program_tablet(aiptek);
if (aiptek->inputdev->absmax[ABS_X] > 0) {
@@ -2190,7 +2190,6 @@ fail1: input_free_device(inputdev);
static void aiptek_disconnect(struct usb_interface *intf);
static struct usb_driver aiptek_driver = {
- .owner = THIS_MODULE,
.name = "aiptek",
.probe = aiptek_probe,
.disconnect = aiptek_disconnect,
diff --git a/drivers/usb/input/appletouch.c b/drivers/usb/input/appletouch.c
index 15840db092a5..1949b54f41f2 100644
--- a/drivers/usb/input/appletouch.c
+++ b/drivers/usb/input/appletouch.c
@@ -452,7 +452,6 @@ static int atp_resume(struct usb_interface *iface)
}
static struct usb_driver atp_driver = {
- .owner = THIS_MODULE,
.name = "appletouch",
.probe = atp_probe,
.disconnect = atp_disconnect,
diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c
index 9a2a47db9494..f7bdc506e613 100644
--- a/drivers/usb/input/ati_remote.c
+++ b/drivers/usb/input/ati_remote.c
@@ -96,6 +96,7 @@
#include <linux/usb.h>
#include <linux/usb_input.h>
#include <linux/wait.h>
+#include <linux/jiffies.h>
/*
* Module and Version Information, Module Parameters
@@ -146,7 +147,7 @@ static char init1[] = { 0x01, 0x00, 0x20, 0x14 };
static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
/* Acceleration curve for directional control pad */
-static char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
+static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
/* Duplicate event filtering time.
* Sequential, identical KIND_FILTERED inputs with less than
@@ -197,7 +198,7 @@ struct ati_remote {
#define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/
/* Translation table from hardware messages to input events. */
-static struct {
+static const struct {
short kind;
unsigned char data1, data2;
int type;
@@ -295,7 +296,6 @@ static void ati_remote_disconnect (struct usb_interface *interface);
/* usb specific object to register with the usb subsystem */
static struct usb_driver ati_remote_driver = {
- .owner = THIS_MODULE,
.name = "ati_remote",
.probe = ati_remote_probe,
.disconnect = ati_remote_disconnect,
@@ -472,7 +472,7 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs)
/* Filter duplicate events which happen "too close" together. */
if ((ati_remote->old_data[0] == data[1]) &&
(ati_remote->old_data[1] == data[2]) &&
- ((ati_remote->old_jiffies + FILTER_TIME) > jiffies)) {
+ time_before(jiffies, ati_remote->old_jiffies + FILTER_TIME)) {
ati_remote->repeat_count++;
} else {
ati_remote->repeat_count = 0;
@@ -507,16 +507,16 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs)
* pad down, so we increase acceleration, ramping up over two seconds to
* a maximum speed. The acceleration curve is #defined above.
*/
- if ((jiffies - ati_remote->old_jiffies) > (HZ >> 2)) {
+ if (time_after(jiffies, ati_remote->old_jiffies + (HZ >> 2))) {
acc = 1;
ati_remote->acc_jiffies = jiffies;
}
- else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 3)) acc = accel[0];
- else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 2)) acc = accel[1];
- else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 1)) acc = accel[2];
- else if ((jiffies - ati_remote->acc_jiffies) < HZ ) acc = accel[3];
- else if ((jiffies - ati_remote->acc_jiffies) < HZ+(HZ>>1)) acc = accel[4];
- else if ((jiffies - ati_remote->acc_jiffies) < (HZ << 1)) acc = accel[5];
+ else if (time_before(jiffies, ati_remote->acc_jiffies + (HZ >> 3))) acc = accel[0];
+ else if (time_before(jiffies, ati_remote->acc_jiffies + (HZ >> 2))) acc = accel[1];
+ else if (time_before(jiffies, ati_remote->acc_jiffies + (HZ >> 1))) acc = accel[2];
+ else if (time_before(jiffies, ati_remote->acc_jiffies + HZ)) acc = accel[3];
+ else if (time_before(jiffies, ati_remote->acc_jiffies + HZ+(HZ>>1))) acc = accel[4];
+ else if (time_before(jiffies, ati_remote->acc_jiffies + (HZ << 1))) acc = accel[5];
else acc = accel[6];
input_regs(dev, regs);
diff --git a/drivers/usb/input/ati_remote2.c b/drivers/usb/input/ati_remote2.c
new file mode 100644
index 000000000000..ab1a1ae24be9
--- /dev/null
+++ b/drivers/usb/input/ati_remote2.c
@@ -0,0 +1,477 @@
+/*
+ * ati_remote2 - ATI/Philips USB RF remote driver
+ *
+ * Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/usb_input.h>
+
+#define DRIVER_DESC "ATI/Philips USB RF remote driver"
+#define DRIVER_VERSION "0.1"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
+MODULE_LICENSE("GPL");
+
+static unsigned int mode_mask = 0x1F;
+module_param(mode_mask, uint, 0644);
+MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
+
+static struct usb_device_id ati_remote2_id_table[] = {
+ { USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, ati_remote2_id_table);
+
+static struct {
+ int hw_code;
+ int key_code;
+} ati_remote2_key_table[] = {
+ { 0x00, KEY_0 },
+ { 0x01, KEY_1 },
+ { 0x02, KEY_2 },
+ { 0x03, KEY_3 },
+ { 0x04, KEY_4 },
+ { 0x05, KEY_5 },
+ { 0x06, KEY_6 },
+ { 0x07, KEY_7 },
+ { 0x08, KEY_8 },
+ { 0x09, KEY_9 },
+ { 0x0c, KEY_POWER },
+ { 0x0d, KEY_MUTE },
+ { 0x10, KEY_VOLUMEUP },
+ { 0x11, KEY_VOLUMEDOWN },
+ { 0x20, KEY_CHANNELUP },
+ { 0x21, KEY_CHANNELDOWN },
+ { 0x28, KEY_FORWARD },
+ { 0x29, KEY_REWIND },
+ { 0x2c, KEY_PLAY },
+ { 0x30, KEY_PAUSE },
+ { 0x31, KEY_STOP },
+ { 0x37, KEY_RECORD },
+ { 0x38, KEY_DVD },
+ { 0x39, KEY_TV },
+ { 0x54, KEY_MENU },
+ { 0x58, KEY_UP },
+ { 0x59, KEY_DOWN },
+ { 0x5a, KEY_LEFT },
+ { 0x5b, KEY_RIGHT },
+ { 0x5c, KEY_OK },
+ { 0x78, KEY_A },
+ { 0x79, KEY_B },
+ { 0x7a, KEY_C },
+ { 0x7b, KEY_D },
+ { 0x7c, KEY_E },
+ { 0x7d, KEY_F },
+ { 0x82, KEY_ENTER },
+ { 0x8e, KEY_VENDOR },
+ { 0x96, KEY_COFFEE },
+ { 0xa9, BTN_LEFT },
+ { 0xaa, BTN_RIGHT },
+ { 0xbe, KEY_QUESTION },
+ { 0xd5, KEY_FRONT },
+ { 0xd0, KEY_EDIT },
+ { 0xf9, KEY_INFO },
+ { (0x00 << 8) | 0x3f, KEY_PROG1 },
+ { (0x01 << 8) | 0x3f, KEY_PROG2 },
+ { (0x02 << 8) | 0x3f, KEY_PROG3 },
+ { (0x03 << 8) | 0x3f, KEY_PROG4 },
+ { (0x04 << 8) | 0x3f, KEY_PC },
+ { 0, KEY_RESERVED }
+};
+
+struct ati_remote2 {
+ struct input_dev *idev;
+ struct usb_device *udev;
+
+ struct usb_interface *intf[2];
+ struct usb_endpoint_descriptor *ep[2];
+ struct urb *urb[2];
+ void *buf[2];
+ dma_addr_t buf_dma[2];
+
+ unsigned long jiffies;
+ int mode;
+
+ char name[64];
+ char phys[64];
+};
+
+static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
+static void ati_remote2_disconnect(struct usb_interface *interface);
+
+static struct usb_driver ati_remote2_driver = {
+ .name = "ati_remote2",
+ .probe = ati_remote2_probe,
+ .disconnect = ati_remote2_disconnect,
+ .id_table = ati_remote2_id_table,
+};
+
+static int ati_remote2_open(struct input_dev *idev)
+{
+ struct ati_remote2 *ar2 = idev->private;
+ int r;
+
+ r = usb_submit_urb(ar2->urb[0], GFP_KERNEL);
+ if (r) {
+ dev_err(&ar2->intf[0]->dev,
+ "%s: usb_submit_urb() = %d\n", __FUNCTION__, r);
+ return r;
+ }
+ r = usb_submit_urb(ar2->urb[1], GFP_KERNEL);
+ if (r) {
+ usb_kill_urb(ar2->urb[0]);
+ dev_err(&ar2->intf[1]->dev,
+ "%s: usb_submit_urb() = %d\n", __FUNCTION__, r);
+ return r;
+ }
+
+ return 0;
+}
+
+static void ati_remote2_close(struct input_dev *idev)
+{
+ struct ati_remote2 *ar2 = idev->private;
+
+ usb_kill_urb(ar2->urb[0]);
+ usb_kill_urb(ar2->urb[1]);
+}
+
+static void ati_remote2_input_mouse(struct ati_remote2 *ar2, struct pt_regs *regs)
+{
+ struct input_dev *idev = ar2->idev;
+ u8 *data = ar2->buf[0];
+
+ if (data[0] > 4) {
+ dev_err(&ar2->intf[0]->dev,
+ "Unknown mode byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ if (!((1 << data[0]) & mode_mask))
+ return;
+
+ input_regs(idev, regs);
+ input_event(idev, EV_REL, REL_X, (s8) data[1]);
+ input_event(idev, EV_REL, REL_Y, (s8) data[2]);
+ input_sync(idev);
+}
+
+static int ati_remote2_lookup(unsigned int hw_code)
+{
+ int i;
+
+ for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++)
+ if (ati_remote2_key_table[i].hw_code == hw_code)
+ return i;
+
+ return -1;
+}
+
+static void ati_remote2_input_key(struct ati_remote2 *ar2, struct pt_regs *regs)
+{
+ struct input_dev *idev = ar2->idev;
+ u8 *data = ar2->buf[1];
+ int hw_code, index;
+
+ if (data[0] > 4) {
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown mode byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ hw_code = data[2];
+ /*
+ * Mode keys (AUX1-AUX4, PC) all generate the same code byte.
+ * Use the mode byte to figure out which one was pressed.
+ */
+ if (hw_code == 0x3f) {
+ /*
+ * For some incomprehensible reason the mouse pad generates
+ * events which look identical to the events from the last
+ * pressed mode key. Naturally we don't want to generate key
+ * events for the mouse pad so we filter out any subsequent
+ * events from the same mode key.
+ */
+ if (ar2->mode == data[0])
+ return;
+
+ if (data[1] == 0)
+ ar2->mode = data[0];
+
+ hw_code |= data[0] << 8;
+ }
+
+ if (!((1 << data[0]) & mode_mask))
+ return;
+
+ index = ati_remote2_lookup(hw_code);
+ if (index < 0) {
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown code byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ switch (data[1]) {
+ case 0: /* release */
+ break;
+ case 1: /* press */
+ ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]);
+ break;
+ case 2: /* repeat */
+
+ /* No repeat for mouse buttons. */
+ if (ati_remote2_key_table[index].key_code == BTN_LEFT ||
+ ati_remote2_key_table[index].key_code == BTN_RIGHT)
+ return;
+
+ if (!time_after_eq(jiffies, ar2->jiffies))
+ return;
+
+ ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]);
+ break;
+ default:
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown state byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ input_regs(idev, regs);
+ input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]);
+ input_sync(idev);
+}
+
+static void ati_remote2_complete_mouse(struct urb *urb, struct pt_regs *regs)
+{
+ struct ati_remote2 *ar2 = urb->context;
+ int r;
+
+ switch (urb->status) {
+ case 0:
+ ati_remote2_input_mouse(ar2, regs);
+ break;
+ case -ENOENT:
+ case -EILSEQ:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&ar2->intf[0]->dev,
+ "%s(): urb status = %d\n", __FUNCTION__, urb->status);
+ return;
+ default:
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): urb status = %d\n", __FUNCTION__, urb->status);
+ }
+
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r)
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r);
+}
+
+static void ati_remote2_complete_key(struct urb *urb, struct pt_regs *regs)
+{
+ struct ati_remote2 *ar2 = urb->context;
+ int r;
+
+ switch (urb->status) {
+ case 0:
+ ati_remote2_input_key(ar2, regs);
+ break;
+ case -ENOENT:
+ case -EILSEQ:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&ar2->intf[1]->dev,
+ "%s(): urb status = %d\n", __FUNCTION__, urb->status);
+ return;
+ default:
+ dev_err(&ar2->intf[1]->dev,
+ "%s(): urb status = %d\n", __FUNCTION__, urb->status);
+ }
+
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r)
+ dev_err(&ar2->intf[1]->dev,
+ "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r);
+}
+
+static int ati_remote2_input_init(struct ati_remote2 *ar2)
+{
+ struct input_dev *idev;
+ int i;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ ar2->idev = idev;
+ idev->private = ar2;
+
+ idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL);
+ idev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
+ idev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++)
+ set_bit(ati_remote2_key_table[i].key_code, idev->keybit);
+
+ idev->rep[REP_DELAY] = 250;
+ idev->rep[REP_PERIOD] = 33;
+
+ idev->open = ati_remote2_open;
+ idev->close = ati_remote2_close;
+
+ idev->name = ar2->name;
+ idev->phys = ar2->phys;
+
+ usb_to_input_id(ar2->udev, &idev->id);
+ idev->cdev.dev = &ar2->udev->dev;
+
+ i = input_register_device(idev);
+ if (i)
+ input_free_device(idev);
+
+ return i;
+}
+
+static int ati_remote2_urb_init(struct ati_remote2 *ar2)
+{
+ struct usb_device *udev = ar2->udev;
+ int i, pipe, maxp;
+
+ for (i = 0; i < 2; i++) {
+ ar2->buf[i] = usb_buffer_alloc(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]);
+ if (!ar2->buf[i])
+ return -ENOMEM;
+
+ ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ar2->urb[i])
+ return -ENOMEM;
+
+ pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = maxp > 4 ? 4 : maxp;
+
+ usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp,
+ i ? ati_remote2_complete_key : ati_remote2_complete_mouse,
+ ar2, ar2->ep[i]->bInterval);
+ ar2->urb[i]->transfer_dma = ar2->buf_dma[i];
+ ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ }
+
+ return 0;
+}
+
+static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (ar2->urb[i])
+ usb_free_urb(ar2->urb[i]);
+
+ if (ar2->buf[i])
+ usb_buffer_free(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]);
+ }
+}
+
+static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *alt = interface->cur_altsetting;
+ struct ati_remote2 *ar2;
+ int r;
+
+ if (alt->desc.bInterfaceNumber)
+ return -ENODEV;
+
+ ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL);
+ if (!ar2)
+ return -ENOMEM;
+
+ ar2->udev = udev;
+
+ ar2->intf[0] = interface;
+ ar2->ep[0] = &alt->endpoint[0].desc;
+
+ ar2->intf[1] = usb_ifnum_to_if(udev, 1);
+ r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2);
+ if (r)
+ goto fail1;
+ alt = ar2->intf[1]->cur_altsetting;
+ ar2->ep[1] = &alt->endpoint[0].desc;
+
+ r = ati_remote2_urb_init(ar2);
+ if (r)
+ goto fail2;
+
+ usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
+ strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
+
+ strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
+
+ r = ati_remote2_input_init(ar2);
+ if (r)
+ goto fail2;
+
+ usb_set_intfdata(interface, ar2);
+
+ return 0;
+
+ fail2:
+ ati_remote2_urb_cleanup(ar2);
+
+ usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
+ fail1:
+ kfree(ar2);
+
+ return r;
+}
+
+static void ati_remote2_disconnect(struct usb_interface *interface)
+{
+ struct ati_remote2 *ar2;
+ struct usb_host_interface *alt = interface->cur_altsetting;
+
+ if (alt->desc.bInterfaceNumber)
+ return;
+
+ ar2 = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ input_unregister_device(ar2->idev);
+
+ ati_remote2_urb_cleanup(ar2);
+
+ usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
+
+ kfree(ar2);
+}
+
+static int __init ati_remote2_init(void)
+{
+ int r;
+
+ r = usb_register(&ati_remote2_driver);
+ if (r)
+ printk(KERN_ERR "ati_remote2: usb_register() = %d\n", r);
+ else
+ printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n");
+
+ return r;
+}
+
+static void __exit ati_remote2_exit(void)
+{
+ usb_deregister(&ati_remote2_driver);
+}
+
+module_init(ati_remote2_init);
+module_exit(ati_remote2_exit);
diff --git a/drivers/usb/input/fixp-arith.h b/drivers/usb/input/fixp-arith.h
index 26ca5b890a61..b44d398de071 100644
--- a/drivers/usb/input/fixp-arith.h
+++ b/drivers/usb/input/fixp-arith.h
@@ -38,7 +38,7 @@ typedef s16 fixp_t;
#define FRAC_MASK ((1<<FRAC_N)-1)
// Not to be used directly. Use fixp_{cos,sin}
-static fixp_t cos_table[45] = {
+static const fixp_t cos_table[45] = {
0x0100, 0x00FF, 0x00FF, 0x00FE, 0x00FD, 0x00FC, 0x00FA, 0x00F8,
0x00F6, 0x00F3, 0x00F0, 0x00ED, 0x00E9, 0x00E6, 0x00E2, 0x00DD,
0x00D9, 0x00D4, 0x00CF, 0x00C9, 0x00C4, 0x00BE, 0x00B8, 0x00B1,
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
index a3e44ef1df43..5f52979af1c7 100644
--- a/drivers/usb/input/hid-core.c
+++ b/drivers/usb/input/hid-core.c
@@ -1454,7 +1454,7 @@ void hid_init_reports(struct hid_device *hid)
* Alphabetically sorted blacklist by quirk type.
*/
-static struct hid_blacklist {
+static const struct hid_blacklist {
__u16 idVendor;
__u16 idProduct;
unsigned quirks;
@@ -1930,7 +1930,6 @@ static struct usb_device_id hid_usb_ids [] = {
MODULE_DEVICE_TABLE (usb, hid_usb_ids);
static struct usb_driver hid_driver = {
- .owner = THIS_MODULE,
.name = "usbhid",
.probe = hid_probe,
.disconnect = hid_disconnect,
diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c
index 9ff25eb520a6..192a03b28971 100644
--- a/drivers/usb/input/hid-input.c
+++ b/drivers/usb/input/hid-input.c
@@ -39,7 +39,7 @@
#define unk KEY_UNKNOWN
-static unsigned char hid_keyboard[256] = {
+static const unsigned char hid_keyboard[256] = {
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
@@ -58,7 +58,7 @@ static unsigned char hid_keyboard[256] = {
150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk
};
-static struct {
+static const struct {
__s32 x;
__s32 y;
} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
@@ -137,6 +137,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
switch (usage->hid & 0xffff) {
case 0xba: map_abs(ABS_RUDDER); break;
case 0xbb: map_abs(ABS_THROTTLE); break;
+ default: goto ignore;
}
break;
diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c
index 440377c7a0da..4dff8473553d 100644
--- a/drivers/usb/input/hiddev.c
+++ b/drivers/usb/input/hiddev.c
@@ -826,7 +826,6 @@ static int hiddev_usbd_probe(struct usb_interface *intf,
static /* const */ struct usb_driver hiddev_driver = {
- .owner = THIS_MODULE,
.name = "hiddev",
.probe = hiddev_usbd_probe,
};
diff --git a/drivers/usb/input/itmtouch.c b/drivers/usb/input/itmtouch.c
index 4a50acb39d29..7618ae5c104f 100644
--- a/drivers/usb/input/itmtouch.c
+++ b/drivers/usb/input/itmtouch.c
@@ -250,7 +250,6 @@ static void itmtouch_disconnect(struct usb_interface *intf)
MODULE_DEVICE_TABLE(usb, itmtouch_ids);
static struct usb_driver itmtouch_driver = {
- .owner = THIS_MODULE,
.name = "itmtouch",
.probe = itmtouch_probe,
.disconnect = itmtouch_disconnect,
diff --git a/drivers/usb/input/kbtab.c b/drivers/usb/input/kbtab.c
index a248664b5d1d..f6d5cead542b 100644
--- a/drivers/usb/input/kbtab.c
+++ b/drivers/usb/input/kbtab.c
@@ -159,7 +159,7 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH);
input_dev->mscbit[0] |= BIT(MSC_SERIAL);
input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0);
- input_set_abs_params(input_dev, ABS_X, 0, 0x1750, 4, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0);
endpoint = &intf->cur_altsetting->endpoint[0].desc;
@@ -197,7 +197,6 @@ static void kbtab_disconnect(struct usb_interface *intf)
}
static struct usb_driver kbtab_driver = {
- .owner = THIS_MODULE,
.name = "kbtab",
.probe = kbtab_probe,
.disconnect = kbtab_disconnect,
diff --git a/drivers/usb/input/keyspan_remote.c b/drivers/usb/input/keyspan_remote.c
index a32cfe51b77d..b4a051b549d1 100644
--- a/drivers/usb/input/keyspan_remote.c
+++ b/drivers/usb/input/keyspan_remote.c
@@ -95,7 +95,7 @@ struct usb_keyspan {
* Currently there are 15 and 17 button models so RESERVED codes
* are blank areas in the mapping.
*/
-static int keyspan_key_table[] = {
+static const int keyspan_key_table[] = {
KEY_RESERVED, /* 0 is just a place holder. */
KEY_RESERVED,
KEY_STOP,
@@ -559,7 +559,6 @@ static void keyspan_disconnect(struct usb_interface *interface)
*/
static struct usb_driver keyspan_driver =
{
- .owner = THIS_MODULE,
.name = "keyspan_remote",
.probe = keyspan_probe,
.disconnect = keyspan_disconnect,
diff --git a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c
index 52cc18cd247d..f018953a5485 100644
--- a/drivers/usb/input/mtouchusb.c
+++ b/drivers/usb/input/mtouchusb.c
@@ -310,7 +310,6 @@ static void mtouchusb_disconnect(struct usb_interface *intf)
MODULE_DEVICE_TABLE(usb, mtouchusb_devices);
static struct usb_driver mtouchusb_driver = {
- .owner = THIS_MODULE,
.name = "mtouchusb",
.probe = mtouchusb_probe,
.disconnect = mtouchusb_disconnect,
diff --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c
index b7476233ef5d..fdf0f788062c 100644
--- a/drivers/usb/input/powermate.c
+++ b/drivers/usb/input/powermate.c
@@ -441,7 +441,6 @@ static struct usb_device_id powermate_devices [] = {
MODULE_DEVICE_TABLE (usb, powermate_devices);
static struct usb_driver powermate_driver = {
- .owner = THIS_MODULE,
.name = "powermate",
.probe = powermate_probe,
.disconnect = powermate_disconnect,
diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c
index 7420c6b84284..3b3c7b4120a2 100644
--- a/drivers/usb/input/touchkitusb.c
+++ b/drivers/usb/input/touchkitusb.c
@@ -1,7 +1,7 @@
/******************************************************************************
* touchkitusb.c -- Driver for eGalax TouchKit USB Touchscreens
*
- * Copyright (C) 2004 by Daniel Ritz
+ * Copyright (C) 2004-2005 by Daniel Ritz <daniel.ritz@gmx.ch>
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
*
* This program is free software; you can redistribute it and/or
@@ -41,15 +41,13 @@
#define TOUCHKIT_MAX_YC 0x07ff
#define TOUCHKIT_YC_FUZZ 0x0
#define TOUCHKIT_YC_FLAT 0x0
-#define TOUCHKIT_REPORT_DATA_SIZE 8
+#define TOUCHKIT_REPORT_DATA_SIZE 16
#define TOUCHKIT_DOWN 0x01
-#define TOUCHKIT_POINT_TOUCH 0x81
-#define TOUCHKIT_POINT_NOTOUCH 0x80
-#define TOUCHKIT_GET_TOUCHED(dat) ((((dat)[0]) & TOUCHKIT_DOWN) ? 1 : 0)
-#define TOUCHKIT_GET_X(dat) (((dat)[3] << 7) | (dat)[4])
-#define TOUCHKIT_GET_Y(dat) (((dat)[1] << 7) | (dat)[2])
+#define TOUCHKIT_PKT_TYPE_MASK 0xFE
+#define TOUCHKIT_PKT_TYPE_REPT 0x80
+#define TOUCHKIT_PKT_TYPE_DIAG 0x0A
#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>"
@@ -62,6 +60,8 @@ MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped.");
struct touchkit_usb {
unsigned char *data;
dma_addr_t data_dma;
+ char buffer[TOUCHKIT_REPORT_DATA_SIZE];
+ int buf_len;
struct urb *irq;
struct usb_device *udev;
struct input_dev *input;
@@ -77,11 +77,128 @@ static struct usb_device_id touchkit_devices[] = {
{}
};
+/* helpers to read the data */
+static inline int touchkit_get_touched(char *data)
+{
+ return (data[0] & TOUCHKIT_DOWN) ? 1 : 0;
+}
+
+static inline int touchkit_get_x(char *data)
+{
+ return ((data[3] & 0x0F) << 7) | (data[4] & 0x7F);
+}
+
+static inline int touchkit_get_y(char *data)
+{
+ return ((data[1] & 0x0F) << 7) | (data[2] & 0x7F);
+}
+
+
+/* processes one input packet. */
+static void touchkit_process_pkt(struct touchkit_usb *touchkit,
+ struct pt_regs *regs, char *pkt)
+{
+ int x, y;
+
+ /* only process report packets */
+ if ((pkt[0] & TOUCHKIT_PKT_TYPE_MASK) != TOUCHKIT_PKT_TYPE_REPT)
+ return;
+
+ if (swap_xy) {
+ y = touchkit_get_x(pkt);
+ x = touchkit_get_y(pkt);
+ } else {
+ x = touchkit_get_x(pkt);
+ y = touchkit_get_y(pkt);
+ }
+
+ input_regs(touchkit->input, regs);
+ input_report_key(touchkit->input, BTN_TOUCH, touchkit_get_touched(pkt));
+ input_report_abs(touchkit->input, ABS_X, x);
+ input_report_abs(touchkit->input, ABS_Y, y);
+ input_sync(touchkit->input);
+}
+
+
+static int touchkit_get_pkt_len(char *buf)
+{
+ switch (buf[0] & TOUCHKIT_PKT_TYPE_MASK) {
+ case TOUCHKIT_PKT_TYPE_REPT:
+ return 5;
+
+ case TOUCHKIT_PKT_TYPE_DIAG:
+ return buf[1] + 2;
+ }
+
+ return 0;
+}
+
+static void touchkit_process(struct touchkit_usb *touchkit, int len,
+ struct pt_regs *regs)
+{
+ char *buffer;
+ int pkt_len, buf_len, pos;
+
+ /* if the buffer contains data, append */
+ if (unlikely(touchkit->buf_len)) {
+ int tmp;
+
+ /* if only 1 byte in buffer, add another one to get length */
+ if (touchkit->buf_len == 1)
+ touchkit->buffer[1] = touchkit->data[0];
+
+ pkt_len = touchkit_get_pkt_len(touchkit->buffer);
+
+ /* unknown packet: drop everything */
+ if (!pkt_len)
+ return;
+
+ /* append, process */
+ tmp = pkt_len - touchkit->buf_len;
+ memcpy(touchkit->buffer + touchkit->buf_len, touchkit->data, tmp);
+ touchkit_process_pkt(touchkit, regs, touchkit->buffer);
+
+ buffer = touchkit->data + tmp;
+ buf_len = len - tmp;
+ } else {
+ buffer = touchkit->data;
+ buf_len = len;
+ }
+
+ /* only one byte left in buffer */
+ if (unlikely(buf_len == 1)) {
+ touchkit->buffer[0] = buffer[0];
+ touchkit->buf_len = 1;
+ return;
+ }
+
+ /* loop over the buffer */
+ pos = 0;
+ while (pos < buf_len) {
+ /* get packet len */
+ pkt_len = touchkit_get_pkt_len(buffer + pos);
+
+ /* unknown packet: drop everything */
+ if (unlikely(!pkt_len))
+ return;
+
+ /* full packet: process */
+ if (likely(pkt_len <= buf_len)) {
+ touchkit_process_pkt(touchkit, regs, buffer + pos);
+ } else {
+ /* incomplete packet: save in buffer */
+ memcpy(touchkit->buffer, buffer + pos, buf_len - pos);
+ touchkit->buf_len = buf_len - pos;
+ }
+ pos += pkt_len;
+ }
+}
+
+
static void touchkit_irq(struct urb *urb, struct pt_regs *regs)
{
struct touchkit_usb *touchkit = urb->context;
int retval;
- int x, y;
switch (urb->status) {
case 0:
@@ -105,20 +222,7 @@ static void touchkit_irq(struct urb *urb, struct pt_regs *regs)
goto exit;
}
- if (swap_xy) {
- y = TOUCHKIT_GET_X(touchkit->data);
- x = TOUCHKIT_GET_Y(touchkit->data);
- } else {
- x = TOUCHKIT_GET_X(touchkit->data);
- y = TOUCHKIT_GET_Y(touchkit->data);
- }
-
- input_regs(touchkit->input, regs);
- input_report_key(touchkit->input, BTN_TOUCH,
- TOUCHKIT_GET_TOUCHED(touchkit->data));
- input_report_abs(touchkit->input, ABS_X, x);
- input_report_abs(touchkit->input, ABS_Y, y);
- input_sync(touchkit->input);
+ touchkit_process(touchkit, urb->actual_length, regs);
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
@@ -267,7 +371,6 @@ static void touchkit_disconnect(struct usb_interface *intf)
MODULE_DEVICE_TABLE(usb, touchkit_devices);
static struct usb_driver touchkit_driver = {
- .owner = THIS_MODULE,
.name = "touchkitusb",
.probe = touchkit_probe,
.disconnect = touchkit_disconnect,
diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c
index 226b6f90a907..2f3edc26cb50 100644
--- a/drivers/usb/input/usbkbd.c
+++ b/drivers/usb/input/usbkbd.c
@@ -345,7 +345,6 @@ static struct usb_device_id usb_kbd_id_table [] = {
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
static struct usb_driver usb_kbd_driver = {
- .owner = THIS_MODULE,
.name = "usbkbd",
.probe = usb_kbd_probe,
.disconnect = usb_kbd_disconnect,
diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c
index 230f6b1b314a..af526135d210 100644
--- a/drivers/usb/input/usbmouse.c
+++ b/drivers/usb/input/usbmouse.c
@@ -226,7 +226,6 @@ static struct usb_device_id usb_mouse_id_table [] = {
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
static struct usb_driver usb_mouse_driver = {
- .owner = THIS_MODULE,
.name = "usbmouse",
.probe = usb_mouse_probe,
.disconnect = usb_mouse_disconnect,
diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c
index aea1cfae34cc..48df4cfd5a42 100644
--- a/drivers/usb/input/wacom.c
+++ b/drivers/usb/input/wacom.c
@@ -854,7 +854,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
- input_set_abs_params(input_dev, ABS_X, 0, wacom->features->y_max, 4, 0);
+ input_set_abs_params(input_dev, ABS_X, 0, wacom->features->x_max, 4, 0);
input_set_abs_params(input_dev, ABS_Y, 0, wacom->features->y_max, 4, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom->features->pressure_max, 0, 0);
@@ -945,7 +945,6 @@ static void wacom_disconnect(struct usb_interface *intf)
}
static struct usb_driver wacom_driver = {
- .owner = THIS_MODULE,
.name = "wacom",
.probe = wacom_probe,
.disconnect = wacom_disconnect,
diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c
index 43112f040b6d..e278489a80c6 100644
--- a/drivers/usb/input/xpad.c
+++ b/drivers/usb/input/xpad.c
@@ -70,7 +70,7 @@
#define XPAD_PKT_LEN 32
-static struct xpad_device {
+static const struct xpad_device {
u16 idVendor;
u16 idProduct;
char *name;
@@ -81,13 +81,13 @@ static struct xpad_device {
{ 0x0000, 0x0000, "X-Box pad" }
};
-static signed short xpad_btn[] = {
+static const signed short xpad_btn[] = {
BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* "analog" buttons */
BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */
-1 /* terminating entry */
};
-static signed short xpad_abs[] = {
+static const signed short xpad_abs[] = {
ABS_X, ABS_Y, /* left stick */
ABS_RX, ABS_RY, /* right stick */
ABS_Z, ABS_RZ, /* triggers left/right */
@@ -316,7 +316,6 @@ static void xpad_disconnect(struct usb_interface *intf)
}
static struct usb_driver xpad_driver = {
- .owner = THIS_MODULE,
.name = "xpad",
.probe = xpad_probe,
.disconnect = xpad_disconnect,
diff --git a/drivers/usb/input/yealink.c b/drivers/usb/input/yealink.c
index f526aebea502..1bfc105ad4d6 100644
--- a/drivers/usb/input/yealink.c
+++ b/drivers/usb/input/yealink.c
@@ -987,7 +987,6 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
}
static struct usb_driver yealink_driver = {
- .owner = THIS_MODULE,
.name = "yealink",
.probe = usb_probe,
.disconnect = usb_disconnect,
diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c
index 27b23c55bbc7..18d8eaf408d5 100644
--- a/drivers/usb/media/dabusb.c
+++ b/drivers/usb/media/dabusb.c
@@ -812,7 +812,6 @@ static struct usb_device_id dabusb_ids [] = {
MODULE_DEVICE_TABLE (usb, dabusb_ids);
static struct usb_driver dabusb_driver = {
- .owner = THIS_MODULE,
.name = "dabusb",
.probe = dabusb_probe,
.disconnect = dabusb_disconnect,
diff --git a/drivers/usb/media/dsbr100.c b/drivers/usb/media/dsbr100.c
index 7503f5b96f59..6a5700e9d428 100644
--- a/drivers/usb/media/dsbr100.c
+++ b/drivers/usb/media/dsbr100.c
@@ -150,7 +150,6 @@ MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table);
/* USB subsystem interface */
static struct usb_driver usb_dsbr100_driver = {
- .owner = THIS_MODULE,
.name = "dsbr100",
.probe = usb_dsbr100_probe,
.disconnect = usb_dsbr100_disconnect,
diff --git a/drivers/usb/media/ibmcam.c b/drivers/usb/media/ibmcam.c
index ba41fc7b95c2..a42c22294124 100644
--- a/drivers/usb/media/ibmcam.c
+++ b/drivers/usb/media/ibmcam.c
@@ -3457,7 +3457,7 @@ static void ibmcam_model3_setup_after_video_if(struct uvd *uvd)
if(init_model3_input) {
if (debug > 0)
info("Setting input to RCA.");
- for (i=0; i < (sizeof(initData)/sizeof(initData[0])); i++) {
+ for (i=0; i < ARRAY_SIZE(initData); i++) {
ibmcam_veio(uvd, initData[i].req, initData[i].value, initData[i].index);
}
}
diff --git a/drivers/usb/media/konicawc.c b/drivers/usb/media/konicawc.c
index 9fe2c2710d13..e2ede583518f 100644
--- a/drivers/usb/media/konicawc.c
+++ b/drivers/usb/media/konicawc.c
@@ -77,14 +77,14 @@ static int saturation = MAX_SATURATION/2;
static int sharpness = MAX_SHARPNESS/2;
static int whitebal = 3*(MAX_WHITEBAL/4);
-static int spd_to_iface[] = { 1, 0, 3, 2, 4, 5, 6 };
+static const int spd_to_iface[] = { 1, 0, 3, 2, 4, 5, 6 };
/* These FPS speeds are from the windows config box. They are
* indexed on size (0-2) and speed (0-6). Divide by 3 to get the
* real fps.
*/
-static int spd_to_fps[][7] = { { 24, 40, 48, 60, 72, 80, 100 },
+static const int spd_to_fps[][7] = { { 24, 40, 48, 60, 72, 80, 100 },
{ 24, 40, 48, 60, 72, 80, 100 },
{ 18, 30, 36, 45, 54, 60, 75 },
{ 6, 10, 12, 15, 18, 21, 25 } };
@@ -95,7 +95,7 @@ struct cam_size {
u8 cmd;
};
-static struct cam_size camera_sizes[] = { { 160, 120, 0x7 },
+static const struct cam_size camera_sizes[] = { { 160, 120, 0x7 },
{ 160, 136, 0xa },
{ 176, 144, 0x4 },
{ 320, 240, 0x5 } };
diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c
index 036c485d1d1e..3a0e8ce67ebe 100644
--- a/drivers/usb/media/ov511.c
+++ b/drivers/usb/media/ov511.c
@@ -211,7 +211,7 @@ static struct ov51x_decomp_ops *ov518_mmx_decomp_ops;
/* Number of times to retry a failed I2C transaction. Increase this if you
* are getting "Failed to read sensor ID..." */
-static int i2c_detect_tries = 5;
+static const int i2c_detect_tries = 5;
/* MMX support is present in kernel and CPU. Checked upon decomp module load. */
#if defined(__i386__) || defined(__x86_64__)
@@ -6008,7 +6008,6 @@ ov51x_disconnect(struct usb_interface *intf)
}
static struct usb_driver ov511_driver = {
- .owner = THIS_MODULE,
.name = "ov511",
.id_table = device_table,
.probe = ov51x_probe,
diff --git a/drivers/usb/media/pwc/pwc-ctrl.c b/drivers/usb/media/pwc/pwc-ctrl.c
index 53099190952c..359c4b2df735 100644
--- a/drivers/usb/media/pwc/pwc-ctrl.c
+++ b/drivers/usb/media/pwc/pwc-ctrl.c
@@ -109,7 +109,7 @@
#define PT_RESET_CONTROL_FORMATTER 0x02
#define PT_STATUS_FORMATTER 0x03
-static char *size2name[PSZ_MAX] =
+static const char *size2name[PSZ_MAX] =
{
"subQCIF",
"QSIF",
diff --git a/drivers/usb/media/pwc/pwc-if.c b/drivers/usb/media/pwc/pwc-if.c
index 5524fd70210b..09ca6128ac20 100644
--- a/drivers/usb/media/pwc/pwc-if.c
+++ b/drivers/usb/media/pwc/pwc-if.c
@@ -111,7 +111,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
static void usb_pwc_disconnect(struct usb_interface *intf);
static struct usb_driver pwc_driver = {
- .owner = THIS_MODULE,
.name = "Philips webcam", /* name */
.id_table = pwc_device_table,
.probe = usb_pwc_probe, /* probe() */
diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c
index f69e443cd1bc..b2ae29af5940 100644
--- a/drivers/usb/media/se401.c
+++ b/drivers/usb/media/se401.c
@@ -1401,7 +1401,6 @@ static void se401_disconnect(struct usb_interface *intf)
}
static struct usb_driver se401_driver = {
- .owner = THIS_MODULE,
.name = "se401",
.id_table = device_table,
.probe = se401_probe,
diff --git a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c
index b2e66e3b90aa..8d1a1c357d5a 100644
--- a/drivers/usb/media/sn9c102_core.c
+++ b/drivers/usb/media/sn9c102_core.c
@@ -1316,7 +1316,7 @@ static int sn9c102_init(struct sn9c102_device* cam)
struct v4l2_control ctrl;
struct v4l2_queryctrl *qctrl;
struct v4l2_rect* rect;
- u8 i = 0, n = 0;
+ u8 i = 0;
int err = 0;
if (!(cam->state & DEV_INITIALIZED)) {
@@ -1352,7 +1352,7 @@ static int sn9c102_init(struct sn9c102_device* cam)
return err;
if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
- DBG(3, "Compressed video format is active, quality %d",
+ DBG(3, "Compressed video format is active, quality %d",
cam->compression.quality)
else
DBG(3, "Uncompressed video format is active")
@@ -1364,9 +1364,8 @@ static int sn9c102_init(struct sn9c102_device* cam)
}
if (s->set_ctrl) {
- n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
- for (i = 0; i < n; i++)
- if (s->qctrl[i].id != 0 &&
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (s->qctrl[i].id != 0 &&
!(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
ctrl.id = s->qctrl[i].id;
ctrl.value = qctrl[i].default_value;
@@ -1388,7 +1387,7 @@ static int sn9c102_init(struct sn9c102_device* cam)
init_waitqueue_head(&cam->wait_stream);
cam->nreadbuffers = 2;
memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
- memcpy(&(s->_rect), &(s->cropcap.defrect),
+ memcpy(&(s->_rect), &(s->cropcap.defrect),
sizeof(struct v4l2_rect));
cam->state |= DEV_INITIALIZED;
}
@@ -1810,13 +1809,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
{
struct sn9c102_sensor* s = cam->sensor;
struct v4l2_queryctrl qc;
- u8 i, n;
+ u8 i;
if (copy_from_user(&qc, arg, sizeof(qc)))
return -EFAULT;
- n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
- for (i = 0; i < n; i++)
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
if (qc.id && qc.id == s->qctrl[i].id) {
memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
if (copy_to_user(arg, &qc, sizeof(qc)))
@@ -1852,7 +1850,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
{
struct sn9c102_sensor* s = cam->sensor;
struct v4l2_control ctrl;
- u8 i, n;
+ u8 i;
int err = 0;
if (!s->set_ctrl)
@@ -1861,8 +1859,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT;
- n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
- for (i = 0; i < n; i++)
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
if (ctrl.id == s->qctrl[i].id) {
if (ctrl.value < s->qctrl[i].minimum ||
ctrl.value > s->qctrl[i].maximum)
@@ -2544,7 +2541,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
unsigned int i, n;
int err = 0, r;
- n = sizeof(sn9c102_id_table)/sizeof(sn9c102_id_table[0]);
+ n = ARRAY_SIZE(sn9c102_id_table);
for (i = 0; i < n-1; i++)
if (le16_to_cpu(udev->descriptor.idVendor) ==
sn9c102_id_table[i].idVendor &&
@@ -2711,7 +2708,6 @@ static void sn9c102_usb_disconnect(struct usb_interface* intf)
static struct usb_driver sn9c102_usb_driver = {
- .owner = THIS_MODULE,
.name = "sn9c102",
.id_table = sn9c102_id_table,
.probe = sn9c102_usb_probe,
diff --git a/drivers/usb/media/stv680.c b/drivers/usb/media/stv680.c
index 0fd0fa9fec21..774038b352cd 100644
--- a/drivers/usb/media/stv680.c
+++ b/drivers/usb/media/stv680.c
@@ -1477,7 +1477,6 @@ static void stv680_disconnect (struct usb_interface *intf)
}
static struct usb_driver stv680_driver = {
- .owner = THIS_MODULE,
.name = "stv680",
.probe = stv680_probe,
.disconnect = stv680_disconnect,
diff --git a/drivers/usb/media/stv680.h b/drivers/usb/media/stv680.h
index 445940612603..b0551cdb280b 100644
--- a/drivers/usb/media/stv680.h
+++ b/drivers/usb/media/stv680.h
@@ -151,7 +151,7 @@ struct usb_stv {
};
-static unsigned char red[256] = {
+static const unsigned char red[256] = {
0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
18, 18, 18, 18, 18, 18, 18, 25, 30, 35, 38, 42,
44, 47, 50, 53, 54, 57, 59, 61, 63, 65, 67, 69,
@@ -176,7 +176,7 @@ static unsigned char red[256] = {
220, 220, 221, 221
};
-static unsigned char green[256] = {
+static const unsigned char green[256] = {
0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 28, 34, 39, 43, 47,
50, 53, 56, 59, 61, 64, 66, 68, 71, 73, 75, 77,
@@ -201,7 +201,7 @@ static unsigned char green[256] = {
245, 245, 246, 246
};
-static unsigned char blue[256] = {
+static const unsigned char blue[256] = {
0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 30, 37, 42, 47, 51,
55, 58, 61, 64, 67, 70, 72, 74, 78, 80, 82, 84,
diff --git a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c
index 24efb21969c6..4bd113325ef9 100644
--- a/drivers/usb/media/usbvideo.c
+++ b/drivers/usb/media/usbvideo.c
@@ -725,7 +725,7 @@ int usbvideo_register(
/* Allocate user_data separately because of kmalloc's limits */
if (num_extra > 0) {
up->user_size = num_cams * num_extra;
- up->user_data = (char *) kmalloc(up->user_size, GFP_KERNEL);
+ up->user_data = kmalloc(up->user_size, GFP_KERNEL);
if (up->user_data == NULL) {
err("%s: Failed to allocate user_data (%d. bytes)",
__FUNCTION__, up->user_size);
@@ -955,7 +955,7 @@ static struct file_operations usbvideo_fops = {
.ioctl = usbvideo_v4l_ioctl,
.llseek = no_llseek,
};
-static struct video_device usbvideo_template = {
+static const struct video_device usbvideo_template = {
.owner = THIS_MODULE,
.type = VID_TYPE_CAPTURE,
.hardware = VID_HARDWARE_CPIA,
diff --git a/drivers/usb/media/vicam.c b/drivers/usb/media/vicam.c
index 0bc0b1247a6b..1c73155c8d77 100644
--- a/drivers/usb/media/vicam.c
+++ b/drivers/usb/media/vicam.c
@@ -1257,7 +1257,6 @@ static struct usb_device_id vicam_table[] = {
MODULE_DEVICE_TABLE(usb, vicam_table);
static struct usb_driver vicam_driver = {
- .owner = THIS_MODULE,
.name = "vicam",
.probe = vicam_probe,
.disconnect = vicam_disconnect,
diff --git a/drivers/usb/media/w9968cf.c b/drivers/usb/media/w9968cf.c
index 67612c81cb9f..04d69339c054 100644
--- a/drivers/usb/media/w9968cf.c
+++ b/drivers/usb/media/w9968cf.c
@@ -2958,7 +2958,7 @@ static int w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
};
#define V4L1_IOCTL(cmd) \
- ((_IOC_NR((cmd)) < sizeof(v4l1_ioctls)/sizeof(char*)) ? \
+ ((_IOC_NR((cmd)) < ARRAY_SIZE(v4l1_ioctls)) ? \
v4l1_ioctls[_IOC_NR((cmd))] : "?")
cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
@@ -3554,7 +3554,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
/* Allocate 2 bytes of memory for camera control USB transfers */
- if (!(cam->control_buffer = (u16*)kmalloc(2, GFP_KERNEL))) {
+ if (!(cam->control_buffer = kmalloc(2, GFP_KERNEL))) {
DBG(1,"Couldn't allocate memory for camera control transfers")
err = -ENOMEM;
goto fail;
@@ -3562,7 +3562,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
memset(cam->control_buffer, 0, 2);
/* Allocate 8 bytes of memory for USB data transfers to the FSB */
- if (!(cam->data_buffer = (u16*)kmalloc(8, GFP_KERNEL))) {
+ if (!(cam->data_buffer = kmalloc(8, GFP_KERNEL))) {
DBG(1, "Couldn't allocate memory for data "
"transfers to the FSB")
err = -ENOMEM;
@@ -3668,7 +3668,6 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
static struct usb_driver w9968cf_usb_driver = {
- .owner = THIS_MODULE,
.name = "w9968cf",
.id_table = winbond_id_table,
.probe = w9968cf_usb_probe,
diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c
index b293db3c28c3..449b2501acf3 100644
--- a/drivers/usb/misc/auerswald.c
+++ b/drivers/usb/misc/auerswald.c
@@ -767,7 +767,7 @@ static int auerbuf_setup (pauerbufctl_t bcp, unsigned int numElements, unsigned
memset (bep, 0, sizeof (auerbuf_t));
bep->list = bcp;
INIT_LIST_HEAD (&bep->buff_list);
- bep->bufp = (char *) kmalloc (bufsize, GFP_KERNEL);
+ bep->bufp = kmalloc (bufsize, GFP_KERNEL);
if (!bep->bufp)
goto bl_fail;
bep->dr = (struct usb_ctrlrequest *) kmalloc (sizeof (struct usb_ctrlrequest), GFP_KERNEL);
@@ -1123,7 +1123,7 @@ static int auerswald_int_open (pauerswald_t cp)
}
}
if (!cp->intbufp) {
- cp->intbufp = (char *) kmalloc (irqsize, GFP_KERNEL);
+ cp->intbufp = kmalloc (irqsize, GFP_KERNEL);
if (!cp->intbufp) {
ret = -ENOMEM;
goto intoend;
@@ -2103,7 +2103,6 @@ MODULE_DEVICE_TABLE (usb, auerswald_ids);
/* Standard usb driver struct */
static struct usb_driver auerswald_driver = {
- .owner = THIS_MODULE,
.name = "auerswald",
.probe = auerswald_probe,
.disconnect = auerswald_disconnect,
diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c
index b33044d56a1e..6671317b495f 100644
--- a/drivers/usb/misc/cytherm.c
+++ b/drivers/usb/misc/cytherm.c
@@ -50,7 +50,6 @@ static void cytherm_disconnect(struct usb_interface *interface);
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver cytherm_driver = {
- .owner = THIS_MODULE,
.name = "cytherm",
.probe = cytherm_probe,
.disconnect = cytherm_disconnect,
diff --git a/drivers/usb/misc/emi26.c b/drivers/usb/misc/emi26.c
index c8155209bf4b..3824df33094e 100644
--- a/drivers/usb/misc/emi26.c
+++ b/drivers/usb/misc/emi26.c
@@ -227,7 +227,6 @@ static void emi26_disconnect(struct usb_interface *intf)
}
static struct usb_driver emi26_driver = {
- .owner = THIS_MODULE,
.name = "emi26 - firmware loader",
.probe = emi26_probe,
.disconnect = emi26_disconnect,
diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c
index 189986af2ac7..52fea2e08db8 100644
--- a/drivers/usb/misc/emi62.c
+++ b/drivers/usb/misc/emi62.c
@@ -266,7 +266,6 @@ static void emi62_disconnect(struct usb_interface *intf)
}
static struct usb_driver emi62_driver = {
- .owner = THIS_MODULE,
.name = "emi62 - firmware loader",
.probe = emi62_probe,
.disconnect = emi62_disconnect,
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index 1dc3e0f73014..d8cde1017985 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -114,7 +114,6 @@ static struct usb_class_driver idmouse_class = {
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver idmouse_driver = {
- .owner = THIS_MODULE,
.name = DRIVER_SHORT,
.probe = idmouse_probe,
.disconnect = idmouse_disconnect,
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 7e93ac96490f..981d8a5fbfd9 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -763,7 +763,6 @@ static void ld_usb_disconnect(struct usb_interface *intf)
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver ld_usb_driver = {
- .owner = THIS_MODULE,
.name = "ldusb",
.probe = ld_usb_probe,
.disconnect = ld_usb_disconnect,
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 2703e205bc8f..1336745b8f55 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -282,7 +282,6 @@ static struct usb_class_driver tower_class = {
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver tower_driver = {
- .owner = THIS_MODULE,
.name = "legousbtower",
.probe = tower_probe,
.disconnect = tower_disconnect,
diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c
index 067a81486921..605a3c87e05c 100644
--- a/drivers/usb/misc/phidgetkit.c
+++ b/drivers/usb/misc/phidgetkit.c
@@ -555,7 +555,6 @@ static void interfacekit_disconnect(struct usb_interface *interface)
}
static struct usb_driver interfacekit_driver = {
- .owner = THIS_MODULE,
.name = "phidgetkit",
.probe = interfacekit_probe,
.disconnect = interfacekit_disconnect,
diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c
index a30d4a6ee824..b3418d2bcc69 100644
--- a/drivers/usb/misc/phidgetservo.c
+++ b/drivers/usb/misc/phidgetservo.c
@@ -306,7 +306,6 @@ servo_disconnect(struct usb_interface *interface)
}
static struct usb_driver servo_driver = {
- .owner = THIS_MODULE,
.name = "phidgetservo",
.probe = servo_probe,
.disconnect = servo_disconnect,
diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c
index 9590dbac5d9a..384fa3769805 100644
--- a/drivers/usb/misc/rio500.c
+++ b/drivers/usb/misc/rio500.c
@@ -465,14 +465,14 @@ static int probe_rio(struct usb_interface *intf,
rio->rio_dev = dev;
- if (!(rio->obuf = (char *) kmalloc(OBUF_SIZE, GFP_KERNEL))) {
+ if (!(rio->obuf = kmalloc(OBUF_SIZE, GFP_KERNEL))) {
err("probe_rio: Not enough memory for the output buffer");
usb_deregister_dev(intf, &usb_rio_class);
return -ENOMEM;
}
dbg("probe_rio: obuf address:%p", rio->obuf);
- if (!(rio->ibuf = (char *) kmalloc(IBUF_SIZE, GFP_KERNEL))) {
+ if (!(rio->ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL))) {
err("probe_rio: Not enough memory for the input buffer");
usb_deregister_dev(intf, &usb_rio_class);
kfree(rio->obuf);
@@ -522,7 +522,6 @@ static struct usb_device_id rio_table [] = {
MODULE_DEVICE_TABLE (usb, rio_table);
static struct usb_driver rio_driver = {
- .owner = THIS_MODULE,
.name = "rio500",
.probe = probe_rio,
.disconnect = disconnect_rio,
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index 41ef2b606751..3260d595441f 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -863,9 +863,6 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
switch (length) {
- case 0:
- return ret;
-
case 1:
if (userbuffer) {
if (get_user(swap8, (u8 __user *)userbuffer))
@@ -1221,9 +1218,6 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
switch (length) {
- case 0:
- return ret;
-
case 1:
ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM,
@@ -2443,8 +2437,8 @@ sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init)
u8 *tempbuf;
u16 *tempbufb;
size_t written;
- static char bootstring[] = "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer.";
- static char bootlogo[] = "(o_ //\\ V_/_";
+ static const char bootstring[] = "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer.";
+ static const char bootlogo[] = "(o_ //\\ V_/_";
/* sisusb->lock is down */
@@ -3489,7 +3483,6 @@ static struct usb_device_id sisusb_table [] = {
MODULE_DEVICE_TABLE (usb, sisusb_table);
static struct usb_driver sisusb_driver = {
- .owner = THIS_MODULE,
.name = "sisusb",
.probe = sisusb_probe,
.disconnect = sisusb_disconnect,
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index 85f3725334b0..cc3dae3f34e0 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -371,7 +371,6 @@ static void lcd_disconnect(struct usb_interface *interface)
}
static struct usb_driver lcd_driver = {
- .owner = THIS_MODULE,
.name = "usblcd",
.probe = lcd_probe,
.disconnect = lcd_disconnect,
diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c
index 3c93921cb6b3..877b081a3a6e 100644
--- a/drivers/usb/misc/usbled.c
+++ b/drivers/usb/misc/usbled.c
@@ -148,7 +148,6 @@ static void led_disconnect(struct usb_interface *interface)
}
static struct usb_driver led_driver = {
- .owner = THIS_MODULE,
.name = "usbled",
.probe = led_probe,
.disconnect = led_disconnect,
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 605a2afe34ed..84fa1728f052 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -2134,7 +2134,6 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE (usb, id_table);
static struct usb_driver usbtest_driver = {
- .owner = THIS_MODULE,
.name = "usbtest",
.id_table = id_table,
.probe = usbtest_probe,
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index 1cabe7ed91f5..4081990b7d1a 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -780,7 +780,6 @@ MODULE_DEVICE_TABLE (usb, uss720_table);
static struct usb_driver uss720_driver = {
- .owner = THIS_MODULE,
.name = "uss720",
.probe = uss720_probe,
.disconnect = uss720_disconnect,
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index 17d0190ef64e..611612146ae9 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -97,19 +97,12 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
if (len >= DATA_MAX)
len = DATA_MAX;
- /*
- * Bulk is easy to shortcut reliably.
- * XXX Other pipe types need consideration. Currently, we overdo it
- * and collect garbage for them: better more than less.
- */
- if (usb_pipebulk(pipe) || usb_pipecontrol(pipe)) {
- if (usb_pipein(pipe)) {
- if (ev_type == 'S')
- return '<';
- } else {
- if (ev_type == 'C')
- return '>';
- }
+ if (usb_pipein(pipe)) {
+ if (ev_type == 'S')
+ return '<';
+ } else {
+ if (ev_type == 'C')
+ return '>';
}
/*
diff --git a/drivers/usb/net/asix.c b/drivers/usb/net/asix.c
index 542120ef1fd2..541181695040 100644
--- a/drivers/usb/net/asix.c
+++ b/drivers/usb/net/asix.c
@@ -912,13 +912,16 @@ static const struct usb_device_id products [] = {
// ASIX AX88772 10/100
USB_DEVICE (0x0b95, 0x7720),
.driver_info = (unsigned long) &ax88772_info,
+}, {
+ // Linksys USB200M Rev 2
+ USB_DEVICE (0x13b1, 0x0018),
+ .driver_info = (unsigned long) &ax88772_info,
},
{ }, // END
};
MODULE_DEVICE_TABLE(usb, products);
static struct usb_driver asix_driver = {
- .owner = THIS_MODULE,
.name = "asix",
.id_table = products,
.probe = usbnet_probe,
diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c
index 37ef365a2472..be5f5e142dd0 100644
--- a/drivers/usb/net/catc.c
+++ b/drivers/usb/net/catc.c
@@ -934,7 +934,6 @@ static struct usb_device_id catc_id_table [] = {
MODULE_DEVICE_TABLE(usb, catc_id_table);
static struct usb_driver catc_driver = {
- .owner = THIS_MODULE,
.name = driver_name,
.probe = catc_probe,
.disconnect = catc_disconnect,
diff --git a/drivers/usb/net/cdc_ether.c b/drivers/usb/net/cdc_ether.c
index c008c981862b..63f1f3ba8e0b 100644
--- a/drivers/usb/net/cdc_ether.c
+++ b/drivers/usb/net/cdc_ether.c
@@ -476,7 +476,6 @@ static const struct usb_device_id products [] = {
MODULE_DEVICE_TABLE(usb, products);
static struct usb_driver cdc_driver = {
- .owner = THIS_MODULE,
.name = "cdc_ether",
.id_table = products,
.probe = usbnet_probe,
diff --git a/drivers/usb/net/cdc_subset.c b/drivers/usb/net/cdc_subset.c
index f05cfb83c82d..ec801e8bb1bb 100644
--- a/drivers/usb/net/cdc_subset.c
+++ b/drivers/usb/net/cdc_subset.c
@@ -306,7 +306,6 @@ MODULE_DEVICE_TABLE(usb, products);
/*-------------------------------------------------------------------------*/
static struct usb_driver cdc_subset_driver = {
- .owner = THIS_MODULE,
.name = "cdc_subset",
.probe = usbnet_probe,
.suspend = usbnet_suspend,
diff --git a/drivers/usb/net/gl620a.c b/drivers/usb/net/gl620a.c
index 2455e9a85674..faf1e86be687 100644
--- a/drivers/usb/net/gl620a.c
+++ b/drivers/usb/net/gl620a.c
@@ -377,7 +377,6 @@ static const struct usb_device_id products [] = {
MODULE_DEVICE_TABLE(usb, products);
static struct usb_driver gl620a_driver = {
- .owner = THIS_MODULE,
.name = "gl620a",
.id_table = products,
.probe = usbnet_probe,
diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c
index b5776518020f..def3bb8e2290 100644
--- a/drivers/usb/net/kaweth.c
+++ b/drivers/usb/net/kaweth.c
@@ -175,7 +175,6 @@ MODULE_DEVICE_TABLE (usb, usb_klsi_table);
* kaweth_driver
****************************************************************/
static struct usb_driver kaweth_driver = {
- .owner = THIS_MODULE,
.name = driver_name,
.probe = kaweth_probe,
.disconnect = kaweth_disconnect,
diff --git a/drivers/usb/net/net1080.c b/drivers/usb/net/net1080.c
index b3799b1a2b0d..78e6a43b1087 100644
--- a/drivers/usb/net/net1080.c
+++ b/drivers/usb/net/net1080.c
@@ -593,7 +593,6 @@ static const struct usb_device_id products [] = {
MODULE_DEVICE_TABLE(usb, products);
static struct usb_driver net1080_driver = {
- .owner = THIS_MODULE,
.name = "net1080",
.id_table = products,
.probe = usbnet_probe,
diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
index 683e3df5d607..156a2f1cb39a 100644
--- a/drivers/usb/net/pegasus.c
+++ b/drivers/usb/net/pegasus.c
@@ -45,7 +45,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.6.12 (2005/01/13)"
+#define DRIVER_VERSION "v0.6.13 (2005/11/13)"
#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
@@ -57,12 +57,14 @@ static const char driver_name[] = "pegasus";
static int loopback = 0;
static int mii_mode = 0;
+static char *devid=NULL;
static struct usb_eth_dev usb_dev_id[] = {
#define PEGASUS_DEV(pn, vid, pid, flags) \
{.name = pn, .vendor = vid, .device = pid, .private = flags},
#include "pegasus.h"
#undef PEGASUS_DEV
+ {NULL, 0, 0, 0},
{NULL, 0, 0, 0}
};
@@ -71,6 +73,7 @@ static struct usb_device_id pegasus_ids[] = {
{.match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = vid, .idProduct = pid},
#include "pegasus.h"
#undef PEGASUS_DEV
+ {},
{}
};
@@ -79,8 +82,10 @@ MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(loopback, bool, 0);
module_param(mii_mode, bool, 0);
+module_param(devid, charp, 0);
MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)");
MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0");
+MODULE_PARM_DESC(devid, "The format is: 'DEV_name:VendorID:DeviceID:Flags'");
/* use ethtool to change the level for any given device */
static int msg_level = -1;
@@ -113,7 +118,7 @@ static void ctrl_callback(struct urb *urb, struct pt_regs *regs)
break;
default:
if (netif_msg_drv(pegasus))
- dev_err(&pegasus->intf->dev, "%s, status %d\n",
+ dev_dbg(&pegasus->intf->dev, "%s, status %d\n",
__FUNCTION__, urb->status);
}
pegasus->flags &= ~ETH_REGS_CHANGED;
@@ -308,9 +313,9 @@ static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd)
__le16 regdi;
int ret;
- ret = set_register(pegasus, PhyCtrl, 0);
- ret = set_registers(pegasus, PhyAddr, sizeof (data), data);
- ret = set_register(pegasus, PhyCtrl, (indx | PHY_READ));
+ set_register(pegasus, PhyCtrl, 0);
+ set_registers(pegasus, PhyAddr, sizeof (data), data);
+ set_register(pegasus, PhyCtrl, (indx | PHY_READ));
for (i = 0; i < REG_TIMEOUT; i++) {
ret = get_registers(pegasus, PhyCtrl, 1, data);
if (data[0] & PHY_DONE)
@@ -319,12 +324,12 @@ static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd)
if (i < REG_TIMEOUT) {
ret = get_registers(pegasus, PhyData, 2, &regdi);
*regd = le16_to_cpu(regdi);
- return 1;
+ return ret;
}
if (netif_msg_drv(pegasus))
dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
- return 0;
+ return ret;
}
static int mdio_read(struct net_device *dev, int phy_id, int loc)
@@ -344,20 +349,20 @@ static int write_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 regd)
data[1] = (u8) regd;
data[2] = (u8) (regd >> 8);
- ret = set_register(pegasus, PhyCtrl, 0);
- ret = set_registers(pegasus, PhyAddr, sizeof(data), data);
- ret = set_register(pegasus, PhyCtrl, (indx | PHY_WRITE));
+ set_register(pegasus, PhyCtrl, 0);
+ set_registers(pegasus, PhyAddr, sizeof(data), data);
+ set_register(pegasus, PhyCtrl, (indx | PHY_WRITE));
for (i = 0; i < REG_TIMEOUT; i++) {
ret = get_registers(pegasus, PhyCtrl, 1, data);
if (data[0] & PHY_DONE)
break;
}
if (i < REG_TIMEOUT)
- return 0;
+ return ret;
if (netif_msg_drv(pegasus))
dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
- return 1;
+ return -ETIMEDOUT;
}
static void mdio_write(struct net_device *dev, int phy_id, int loc, int val)
@@ -374,9 +379,9 @@ static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata)
__le16 retdatai;
int ret;
- ret = set_register(pegasus, EpromCtrl, 0);
- ret = set_register(pegasus, EpromOffset, index);
- ret = set_register(pegasus, EpromCtrl, EPROM_READ);
+ set_register(pegasus, EpromCtrl, 0);
+ set_register(pegasus, EpromOffset, index);
+ set_register(pegasus, EpromCtrl, EPROM_READ);
for (i = 0; i < REG_TIMEOUT; i++) {
ret = get_registers(pegasus, EpromCtrl, 1, &tmp);
@@ -386,12 +391,12 @@ static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata)
if (i < REG_TIMEOUT) {
ret = get_registers(pegasus, EpromData, 2, &retdatai);
*retdata = le16_to_cpu(retdatai);
- return 0;
+ return ret;
}
if (netif_msg_drv(pegasus))
dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
- return -1;
+ return -ETIMEDOUT;
}
#ifdef PEGASUS_WRITE_EEPROM
@@ -400,8 +405,8 @@ static inline void enable_eprom_write(pegasus_t * pegasus)
__u8 tmp;
int ret;
- ret = get_registers(pegasus, EthCtrl2, 1, &tmp);
- ret = set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE);
+ get_registers(pegasus, EthCtrl2, 1, &tmp);
+ set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE);
}
static inline void disable_eprom_write(pegasus_t * pegasus)
@@ -409,9 +414,9 @@ static inline void disable_eprom_write(pegasus_t * pegasus)
__u8 tmp;
int ret;
- ret = get_registers(pegasus, EthCtrl2, 1, &tmp);
- ret = set_register(pegasus, EpromCtrl, 0);
- ret = set_register(pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE);
+ get_registers(pegasus, EthCtrl2, 1, &tmp);
+ set_register(pegasus, EpromCtrl, 0);
+ set_register(pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE);
}
static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data)
@@ -420,11 +425,11 @@ static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data)
__u8 tmp, d[4] = { 0x3f, 0, 0, EPROM_WRITE };
int ret;
- ret = set_registers(pegasus, EpromOffset, 4, d);
+ set_registers(pegasus, EpromOffset, 4, d);
enable_eprom_write(pegasus);
- ret = set_register(pegasus, EpromOffset, index);
- ret = set_registers(pegasus, EpromData, 2, &data);
- ret = set_register(pegasus, EpromCtrl, EPROM_WRITE);
+ set_register(pegasus, EpromOffset, index);
+ set_registers(pegasus, EpromData, 2, &data);
+ set_register(pegasus, EpromCtrl, EPROM_WRITE);
for (i = 0; i < REG_TIMEOUT; i++) {
ret = get_registers(pegasus, EpromCtrl, 1, &tmp);
@@ -433,10 +438,10 @@ static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data)
}
disable_eprom_write(pegasus);
if (i < REG_TIMEOUT)
- return 0;
+ return ret;
if (netif_msg_drv(pegasus))
dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
- return -1;
+ return -ETIMEDOUT;
}
#endif /* PEGASUS_WRITE_EEPROM */
@@ -454,10 +459,9 @@ static inline void get_node_id(pegasus_t * pegasus, __u8 * id)
static void set_ethernet_addr(pegasus_t * pegasus)
{
__u8 node_id[6];
- int ret;
get_node_id(pegasus, node_id);
- ret = set_registers(pegasus, EthID, sizeof (node_id), node_id);
+ set_registers(pegasus, EthID, sizeof (node_id), node_id);
memcpy(pegasus->net->dev_addr, node_id, sizeof (node_id));
}
@@ -465,30 +469,29 @@ static inline int reset_mac(pegasus_t * pegasus)
{
__u8 data = 0x8;
int i;
- int ret;
- ret = set_register(pegasus, EthCtrl1, data);
+ set_register(pegasus, EthCtrl1, data);
for (i = 0; i < REG_TIMEOUT; i++) {
- ret = get_registers(pegasus, EthCtrl1, 1, &data);
+ get_registers(pegasus, EthCtrl1, 1, &data);
if (~data & 0x08) {
if (loopback & 1)
break;
if (mii_mode && (pegasus->features & HAS_HOME_PNA))
- ret = set_register(pegasus, Gpio1, 0x34);
+ set_register(pegasus, Gpio1, 0x34);
else
- ret = set_register(pegasus, Gpio1, 0x26);
- ret = set_register(pegasus, Gpio0, pegasus->features);
- ret = set_register(pegasus, Gpio0, DEFAULT_GPIO_SET);
+ set_register(pegasus, Gpio1, 0x26);
+ set_register(pegasus, Gpio0, pegasus->features);
+ set_register(pegasus, Gpio0, DEFAULT_GPIO_SET);
break;
}
}
if (i == REG_TIMEOUT)
- return 1;
+ return -ETIMEDOUT;
if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
- ret = set_register(pegasus, Gpio0, 0x24);
- ret = set_register(pegasus, Gpio0, 0x26);
+ set_register(pegasus, Gpio0, 0x24);
+ set_register(pegasus, Gpio0, 0x26);
}
if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) {
__u16 auxmode;
@@ -527,7 +530,7 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb)
write_mii_word(pegasus, 0, 0x1b, auxmode | 4);
}
- return 0;
+ return ret;
}
static void fill_skb_pool(pegasus_t * pegasus)
@@ -881,9 +884,8 @@ static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
static inline void disable_net_traffic(pegasus_t * pegasus)
{
int tmp = 0;
- int ret;
- ret = set_registers(pegasus, EthCtrl0, 2, &tmp);
+ set_registers(pegasus, EthCtrl0, 2, &tmp);
}
static inline void get_interrupt_interval(pegasus_t * pegasus)
@@ -1206,18 +1208,17 @@ static __u8 mii_phy_probe(pegasus_t * pegasus)
static inline void setup_pegasus_II(pegasus_t * pegasus)
{
__u8 data = 0xa5;
- int ret;
- ret = set_register(pegasus, Reg1d, 0);
- ret = set_register(pegasus, Reg7b, 1);
+ set_register(pegasus, Reg1d, 0);
+ set_register(pegasus, Reg7b, 1);
mdelay(100);
if ((pegasus->features & HAS_HOME_PNA) && mii_mode)
- ret = set_register(pegasus, Reg7b, 0);
+ set_register(pegasus, Reg7b, 0);
else
- ret = set_register(pegasus, Reg7b, 2);
+ set_register(pegasus, Reg7b, 2);
- ret = set_register(pegasus, 0x83, data);
- ret = get_registers(pegasus, 0x83, 1, &data);
+ set_register(pegasus, 0x83, data);
+ get_registers(pegasus, 0x83, 1, &data);
if (data == 0xa5) {
pegasus->chip = 0x8513;
@@ -1225,14 +1226,14 @@ static inline void setup_pegasus_II(pegasus_t * pegasus)
pegasus->chip = 0;
}
- ret = set_register(pegasus, 0x80, 0xc0);
- ret = set_register(pegasus, 0x83, 0xff);
- ret = set_register(pegasus, 0x84, 0x01);
+ set_register(pegasus, 0x80, 0xc0);
+ set_register(pegasus, 0x83, 0xff);
+ set_register(pegasus, 0x84, 0x01);
if (pegasus->features & HAS_HOME_PNA && mii_mode)
- ret = set_register(pegasus, Reg81, 6);
+ set_register(pegasus, Reg81, 6);
else
- ret = set_register(pegasus, Reg81, 2);
+ set_register(pegasus, Reg81, 2);
}
@@ -1414,9 +1415,42 @@ static struct usb_driver pegasus_driver = {
.resume = pegasus_resume,
};
+static void parse_id(char *id)
+{
+ unsigned int vendor_id=0, device_id=0, flags=0, i=0;
+ char *token, *name=NULL;
+
+ if ((token = strsep(&id, ":")) != NULL)
+ name = token;
+ /* name now points to a null terminated string*/
+ if ((token = strsep(&id, ":")) != NULL)
+ vendor_id = simple_strtoul(token, NULL, 16);
+ if ((token = strsep(&id, ":")) != NULL)
+ device_id = simple_strtoul(token, NULL, 16);
+ flags = simple_strtoul(id, NULL, 16);
+ pr_info("%s: new device %s, vendor ID 0x%04x, device ID 0x%04x, flags: 0x%x\n",
+ driver_name, name, vendor_id, device_id, flags);
+
+ if (vendor_id > 0x10000 || vendor_id == 0)
+ return;
+ if (device_id > 0x10000 || device_id == 0)
+ return;
+
+ for (i=0; usb_dev_id[i].name; i++);
+ usb_dev_id[i].name = name;
+ usb_dev_id[i].vendor = vendor_id;
+ usb_dev_id[i].device = device_id;
+ usb_dev_id[i].private = flags;
+ pegasus_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
+ pegasus_ids[i].idVendor = vendor_id;
+ pegasus_ids[i].idProduct = device_id;
+}
+
static int __init pegasus_init(void)
{
pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION);
+ if (devid)
+ parse_id(devid);
pegasus_workqueue = create_singlethread_workqueue("pegasus");
if (!pegasus_workqueue)
return -ENOMEM;
diff --git a/drivers/usb/net/plusb.c b/drivers/usb/net/plusb.c
index 89856aa0e3b8..4fe863389cb7 100644
--- a/drivers/usb/net/plusb.c
+++ b/drivers/usb/net/plusb.c
@@ -127,7 +127,6 @@ static const struct usb_device_id products [] = {
MODULE_DEVICE_TABLE(usb, products);
static struct usb_driver plusb_driver = {
- .owner = THIS_MODULE,
.name = "plusb",
.id_table = products,
.probe = usbnet_probe,
diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c
index c0ecbab6f6ba..49991ac1bf3b 100644
--- a/drivers/usb/net/rndis_host.c
+++ b/drivers/usb/net/rndis_host.c
@@ -586,7 +586,6 @@ static const struct usb_device_id products [] = {
MODULE_DEVICE_TABLE(usb, products);
static struct usb_driver rndis_driver = {
- .owner = THIS_MODULE,
.name = "rndis_host",
.id_table = products,
.probe = usbnet_probe,
diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
index 787dd3591d6a..8ca52be23976 100644
--- a/drivers/usb/net/rtl8150.c
+++ b/drivers/usb/net/rtl8150.c
@@ -177,7 +177,6 @@ static int rtl8150_probe(struct usb_interface *intf,
static const char driver_name [] = "rtl8150";
static struct usb_driver rtl8150_driver = {
- .owner = THIS_MODULE,
.name = driver_name,
.probe = rtl8150_probe,
.disconnect = rtl8150_disconnect,
diff --git a/drivers/usb/net/zaurus.c b/drivers/usb/net/zaurus.c
index 680d13957af4..9c5ab251370c 100644
--- a/drivers/usb/net/zaurus.c
+++ b/drivers/usb/net/zaurus.c
@@ -357,7 +357,6 @@ static const struct usb_device_id products [] = {
MODULE_DEVICE_TABLE(usb, products);
static struct usb_driver zaurus_driver = {
- .owner = THIS_MODULE,
.name = "zaurus",
.id_table = products,
.probe = usbnet_probe,
diff --git a/drivers/usb/net/zd1201.c b/drivers/usb/net/zd1201.c
index 2f52261c7cc1..f3a8e2807c3b 100644
--- a/drivers/usb/net/zd1201.c
+++ b/drivers/usb/net/zd1201.c
@@ -1722,7 +1722,7 @@ static const struct iw_priv_args zd1201_private_args[] = {
IW_PRIV_TYPE_NONE, "sethostauth" },
{ ZD1201GIWHOSTAUTH, IW_PRIV_TYPE_NONE,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostauth" },
- { ZD1201SIWAUTHSTA, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1,
+ { ZD1201SIWAUTHSTA, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1,
IW_PRIV_TYPE_NONE, "authstation" },
{ ZD1201SIWMAXASSOC, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
IW_PRIV_TYPE_NONE, "setmaxassoc" },
@@ -1731,9 +1731,9 @@ static const struct iw_priv_args zd1201_private_args[] = {
};
static const struct iw_handler_def zd1201_iw_handlers = {
- .num_standard = sizeof(zd1201_iw_handler)/sizeof(iw_handler),
- .num_private = sizeof(zd1201_private_handler)/sizeof(iw_handler),
- .num_private_args = sizeof(zd1201_private_args)/sizeof(struct iw_priv_args),
+ .num_standard = ARRAY_SIZE(zd1201_iw_handler),
+ .num_private = ARRAY_SIZE(zd1201_private_handler),
+ .num_private_args = ARRAY_SIZE(zd1201_private_args),
.standard = (iw_handler *)zd1201_iw_handler,
.private = (iw_handler *)zd1201_private_handler,
.private_args = (struct iw_priv_args *) zd1201_private_args,
@@ -1829,6 +1829,8 @@ static int zd1201_probe(struct usb_interface *interface,
if (err)
goto err_net;
+ SET_NETDEV_DEV(zd->dev, &usb->dev);
+
err = register_netdev(zd->dev);
if (err)
goto err_net;
@@ -1923,7 +1925,6 @@ static int zd1201_resume(struct usb_interface *interface)
#endif
static struct usb_driver zd1201_usb = {
- .owner = THIS_MODULE,
.name = "zd1201",
.probe = zd1201_probe,
.disconnect = zd1201_disconnect,
diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c
index 1f29d8837327..dbf1f063098c 100644
--- a/drivers/usb/serial/airprime.c
+++ b/drivers/usb/serial/airprime.c
@@ -23,11 +23,11 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_driver airprime_driver = {
- .owner = THIS_MODULE,
.name = "airprime",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
static struct usb_serial_driver airprime_device = {
diff --git a/drivers/usb/serial/anydata.c b/drivers/usb/serial/anydata.c
index 18022a74a3dc..343f6f228220 100644
--- a/drivers/usb/serial/anydata.c
+++ b/drivers/usb/serial/anydata.c
@@ -27,11 +27,11 @@ static int buffer_size;
static int debug;
static struct usb_driver anydata_driver = {
- .owner = THIS_MODULE,
.name = "anydata",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
static int anydata_open(struct usb_serial_port *port, struct file *filp)
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
index 84bc0ee4f061..4144777ea18b 100644
--- a/drivers/usb/serial/belkin_sa.c
+++ b/drivers/usb/serial/belkin_sa.c
@@ -113,11 +113,11 @@ static struct usb_device_id id_table_combined [] = {
MODULE_DEVICE_TABLE (usb, id_table_combined);
static struct usb_driver belkin_driver = {
- .owner = THIS_MODULE,
.name = "belkin",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table_combined,
+ .no_dynamic_id = 1,
};
/* All of the device info needed for the serial converters */
diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c
index c9787001cf2a..da46b351e188 100644
--- a/drivers/usb/serial/cp2101.c
+++ b/drivers/usb/serial/cp2101.c
@@ -67,11 +67,11 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE (usb, id_table);
static struct usb_driver cp2101_driver = {
- .owner = THIS_MODULE,
.name = "cp2101",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
static struct usb_serial_driver cp2101_device = {
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index e581e4ae8483..6d18d4eaba35 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -76,11 +76,11 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE (usb, id_table);
static struct usb_driver cyberjack_driver = {
- .owner = THIS_MODULE,
.name = "cyberjack",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
static struct usb_serial_driver cyberjack_device = {
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index af9290ed257b..af18355e94cc 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -112,6 +112,7 @@ static struct usb_driver cypress_driver = {
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table_combined,
+ .no_dynamic_id = 1,
};
struct cypress_private {
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index dc74644a603d..8fc414bd5b24 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -493,11 +493,11 @@ static struct usb_device_id id_table_4 [] = {
MODULE_DEVICE_TABLE (usb, id_table_combined);
static struct usb_driver digi_driver = {
- .owner = THIS_MODULE,
.name = "digi_acceleport",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table_combined,
+ .no_dynamic_id = 1,
};
diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c
index 0b0546dcc7b9..79a766e9ca23 100644
--- a/drivers/usb/serial/empeg.c
+++ b/drivers/usb/serial/empeg.c
@@ -105,11 +105,11 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE (usb, id_table);
static struct usb_driver empeg_driver = {
- .owner = THIS_MODULE,
.name = "empeg",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
static struct usb_serial_driver empeg_device = {
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 06e04b442ff1..eb863b3f2d79 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -471,12 +471,15 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_MHAM_Y6_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_MHAM_Y8_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_VCP_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_D2XX_PID) },
{ USB_DEVICE(EVOLUTION_VID, EVOLUTION_ER1_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ARTEMIS_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HR_PID) },
{ USB_DEVICE(KOBIL_VID, KOBIL_CONV_B1_PID) },
{ USB_DEVICE(KOBIL_VID, KOBIL_CONV_KAAN_PID) },
+ { USB_DEVICE(POSIFLEX_VID, POSIFLEX_PP7000_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
@@ -488,9 +491,10 @@ static struct usb_driver ftdi_driver = {
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table_combined,
+ .no_dynamic_id = 1,
};
-static char *ftdi_chip_name[] = {
+static const char *ftdi_chip_name[] = {
[SIO] = "SIO", /* the serial part of FT8U100AX */
[FT8U232AM] = "FT8U232AM",
[FT232BM] = "FT232BM",
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 773ea3eca086..f380f9eaff71 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -343,6 +343,13 @@
#define XSENS_CONVERTER_7_PID 0xD38F
/*
+ * Teratronik product ids.
+ * Submitted by O. Wölfelschneider.
+ */
+#define FTDI_TERATRONIK_VCP_PID 0xEC88 /* Teratronik device (preferring VCP driver on windows) */
+#define FTDI_TERATRONIK_D2XX_PID 0xEC89 /* Teratronik device (preferring D2XX driver on windows) */
+
+/*
* Evolution Robotics products (http://www.evolution.com/).
* Submitted by Shawn M. Lavelle.
*/
@@ -352,6 +359,12 @@
/* Pyramid Computer GmbH */
#define FTDI_PYRAMID_PID 0xE6C8 /* Pyramid Appliance Display */
+/*
+ * Posiflex inc retail equipment (http://www.posiflex.com.tw)
+ */
+#define POSIFLEX_VID 0x0d3a /* Vendor ID */
+#define POSIFLEX_PP7000_PID 0x0300 /* PP-7000II thermal printer */
+
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index 35820bda7ae1..452efce72714 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -222,11 +222,11 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE (usb, id_table);
static struct usb_driver garmin_driver = {
- .owner = THIS_MODULE,
.name = "garmin_gps",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 53a47c31cd0e..4ddac620fc0c 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -68,11 +68,11 @@ static int generic_probe(struct usb_interface *interface,
}
static struct usb_driver generic_driver = {
- .owner = THIS_MODULE,
.name = "usbserial_generic",
.probe = generic_probe,
.disconnect = usb_serial_disconnect,
.id_table = generic_serial_ids,
+ .no_dynamic_id = 1,
};
#endif
diff --git a/drivers/usb/serial/hp4x.c b/drivers/usb/serial/hp4x.c
index 8eadfb705601..e9719da2aca1 100644
--- a/drivers/usb/serial/hp4x.c
+++ b/drivers/usb/serial/hp4x.c
@@ -37,11 +37,11 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_driver hp49gp_driver = {
- .owner = THIS_MODULE,
.name = "hp4X",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
static struct usb_serial_driver hp49gp_device = {
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index dc4c498bd1ed..faedbeb6ba49 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -184,7 +184,7 @@ struct divisor_table_entry {
// These assume a 3.6864MHz crystal, the standard /16, and
// MCR.7 = 0.
//
-static struct divisor_table_entry divisor_table[] = {
+static const struct divisor_table_entry divisor_table[] = {
{ 50, 4608},
{ 75, 3072},
{ 110, 2095}, /* 2094.545455 => 230450 => .0217 % over */
@@ -242,11 +242,11 @@ static void edge_shutdown (struct usb_serial *serial);
#include "io_tables.h" /* all of the devices that this driver supports */
static struct usb_driver io_driver = {
- .owner = THIS_MODULE,
.name = "io_edgeport",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table_combined,
+ .no_dynamic_id = 1,
};
/* function prototypes for all of our local functions */
@@ -2353,7 +2353,7 @@ static int calc_baud_rate_divisor (int baudrate, int *divisor)
dbg("%s - %d", __FUNCTION__, baudrate);
- for (i = 0; i < NUM_ENTRIES(divisor_table); i++) {
+ for (i = 0; i < ARRAY_SIZE(divisor_table); i++) {
if ( divisor_table[i].BaudRate == baudrate ) {
*divisor = divisor_table[i].Divisor;
return 0;
diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h
index 5112d7aac055..123fa8a904e6 100644
--- a/drivers/usb/serial/io_edgeport.h
+++ b/drivers/usb/serial/io_edgeport.h
@@ -31,9 +31,6 @@
#ifndef HIGH8
#define HIGH8(a) ((unsigned char)((a & 0xff00) >> 8))
#endif
-#ifndef NUM_ENTRIES
- #define NUM_ENTRIES(x) (sizeof(x)/sizeof((x)[0]))
-#endif
#ifndef __KERNEL__
#define __KERNEL__
diff --git a/drivers/usb/serial/io_fw_boot2.h b/drivers/usb/serial/io_fw_boot2.h
index c7c3a3c305fe..e3463de99de4 100644
--- a/drivers/usb/serial/io_fw_boot2.h
+++ b/drivers/usb/serial/io_fw_boot2.h
@@ -537,7 +537,7 @@ static unsigned char IMAGE_ARRAY_NAME[] = {
};
-static struct edge_firmware_version_info IMAGE_VERSION_NAME = {
+static const struct edge_firmware_version_info IMAGE_VERSION_NAME = {
2, 0, 3 }; // Major, Minor, Build
#undef IMAGE_VERSION_NAME
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 832b6d6734c0..2edf9cabad20 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -216,11 +216,11 @@ static struct usb_device_id id_table_combined [] = {
MODULE_DEVICE_TABLE (usb, id_table_combined);
static struct usb_driver io_driver = {
- .owner = THIS_MODULE,
.name = "io_ti",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table_combined,
+ .no_dynamic_id = 1,
};
@@ -2843,7 +2843,7 @@ static struct edge_buf *edge_buf_alloc(unsigned int size)
* Free the buffer and all associated memory.
*/
-void edge_buf_free(struct edge_buf *eb)
+static void edge_buf_free(struct edge_buf *eb)
{
if (eb) {
kfree(eb->buf_buf);
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index d5d066488100..06d07cea0b70 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -542,11 +542,11 @@ static struct usb_device_id ipaq_id_table [] = {
MODULE_DEVICE_TABLE (usb, ipaq_id_table);
static struct usb_driver ipaq_driver = {
- .owner = THIS_MODULE,
.name = "ipaq",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = ipaq_id_table,
+ .no_dynamic_id = 1,
};
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index 7744b8148bc5..2dd191f5fe76 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -152,11 +152,11 @@ static struct usb_device_id usb_ipw_ids[] = {
MODULE_DEVICE_TABLE(usb, usb_ipw_ids);
static struct usb_driver usb_ipw_driver = {
- .owner = THIS_MODULE,
.name = "ipwtty",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = usb_ipw_ids,
+ .no_dynamic_id = 1,
};
static int debug;
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 19f329e9bdcf..a59010421444 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -125,11 +125,11 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE (usb, id_table);
static struct usb_driver ir_driver = {
- .owner = THIS_MODULE,
.name = "ir-usb",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h
index 5cfc13b5e56f..7472ed6bf626 100644
--- a/drivers/usb/serial/keyspan.h
+++ b/drivers/usb/serial/keyspan.h
@@ -520,11 +520,11 @@ static struct usb_device_id keyspan_ids_combined[] = {
MODULE_DEVICE_TABLE(usb, keyspan_ids_combined);
static struct usb_driver keyspan_driver = {
- .owner = THIS_MODULE,
.name = "keyspan",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = keyspan_ids_combined,
+ .no_dynamic_id = 1,
};
/* usb_device_id table for the pre-firmware download keyspan devices */
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index cd4f48bd83b6..b0441c35f98f 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -150,11 +150,11 @@ static struct usb_device_id id_table_combined [] = {
MODULE_DEVICE_TABLE (usb, id_table_combined);
static struct usb_driver keyspan_pda_driver = {
- .owner = THIS_MODULE,
.name = "keyspan_pda",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table_combined,
+ .no_dynamic_id = 1,
};
static struct usb_device_id id_table_std [] = {
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index a8951c0fd020..4e2f7dfb58b2 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -116,11 +116,11 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE (usb, id_table);
static struct usb_driver kl5kusb105d_driver = {
- .owner = THIS_MODULE,
.name = "kl5kusb105d",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
static struct usb_serial_driver kl5kusb105d_device = {
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index 9456dd9dd136..d9c21e275130 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -97,11 +97,11 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE (usb, id_table);
static struct usb_driver kobil_driver = {
- .owner = THIS_MODULE,
.name = "kobil",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index ca5dbadb9b7e..b6d6cab9c859 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -125,11 +125,11 @@ static struct usb_device_id id_table_combined [] = {
MODULE_DEVICE_TABLE (usb, id_table_combined);
static struct usb_driver mct_u232_driver = {
- .owner = THIS_MODULE,
.name = "mct_u232",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table_combined,
+ .no_dynamic_id = 1,
};
static struct usb_serial_driver mct_u232_device = {
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 3caf97072ac0..762d8ff9a1e4 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -80,11 +80,11 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE (usb, id_table);
static struct usb_driver omninet_driver = {
- .owner = THIS_MODULE,
.name = "omninet",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 7716000045b7..3fd2405304fd 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -95,11 +95,11 @@ static struct usb_device_id option_ids[] = {
MODULE_DEVICE_TABLE(usb, option_ids);
static struct usb_driver option_driver = {
- .owner = THIS_MODULE,
.name = "option",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = option_ids,
+ .no_dynamic_id = 1,
};
/* The card has three separate interfaces, wich the serial driver
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 41a45a5025b2..f03721056190 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -82,11 +82,11 @@ static struct usb_device_id id_table [] = {
MODULE_DEVICE_TABLE (usb, id_table);
static struct usb_driver pl2303_driver = {
- .owner = THIS_MODULE,
.name = "pl2303",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
#define SET_LINE_REQUEST_TYPE 0x21
@@ -810,7 +810,7 @@ static void pl2303_update_line_status(struct usb_serial_port *port,
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
u8 status_idx = UART_STATE;
- u8 length = UART_STATE;
+ u8 length = UART_STATE + 1;
if ((le16_to_cpu(port->serial->dev->descriptor.idVendor) == SIEMENS_VENDOR_ID) &&
(le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_X65 ||
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index c22bdc0c4dfd..f0215f850d2d 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -160,14 +160,14 @@ static struct usb_device_id id_table[] = {
MODULE_DEVICE_TABLE (usb, id_table);
static struct usb_driver safe_driver = {
- .owner = THIS_MODULE,
.name = "safe_serial",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
+ .no_dynamic_id = 1,
};
-static __u16 crc10_table[256] = {
+static const __u16 crc10_table[256] = {
0x000, 0x233, 0x255, 0x066, 0x299, 0x0aa, 0x0cc, 0x2ff, 0x301, 0x132, 0x154, 0x367, 0x198, 0x3ab, 0x3cd, 0x1fe,
0x031, 0x202, 0x264, 0x057, 0x2a8, 0x09b, 0x0fd, 0x2ce, 0x330, 0x103, 0x165, 0x356, 0x1a9, 0x39a, 0x3fc, 0x1cf,
0x062, 0x251, 0x237, 0x004, 0x2fb, 0x0c8, 0x0ae, 0x29d, 0x363, 0x150, 0x136, 0x305, 0x1fa, 0x3c9, 0x3af, 0x19c,
@@ -425,7 +425,7 @@ static int __init safe_init (void)
if (vendor || product) {
info ("vendor: %x product: %x\n", vendor, product);
- for (i = 0; i < (sizeof (id_table) / sizeof (struct usb_device_id)); i++) {
+ for (i = 0; i < ARRAY_SIZE(id_table); i++) {
if (!id_table[i].idVendor && !id_table[i].idProduct) {
id_table[i].idVendor = vendor;
id_table[i].idProduct = product;
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 205dbf7201da..abb830cb77bd 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -248,11 +248,11 @@ static struct usb_device_id ti_id_table_combined[] = {
};
static struct usb_driver ti_usb_driver = {
- .owner = THIS_MODULE,
.name = "ti_usb_3410_5052",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = ti_id_table_combined,
+ .no_dynamic_id = 1,
};
static struct usb_serial_driver ti_1port_device = {
@@ -351,17 +351,14 @@ static int __init ti_init(void)
int i,j;
int ret;
-
/* insert extra vendor and product ids */
- j = sizeof(ti_id_table_3410)/sizeof(struct usb_device_id)
- - TI_EXTRA_VID_PID_COUNT - 1;
+ j = ARRAY_SIZE(ti_id_table_3410) - TI_EXTRA_VID_PID_COUNT - 1;
for (i=0; i<min(vendor_3410_count,product_3410_count); i++,j++) {
ti_id_table_3410[j].idVendor = vendor_3410[i];
ti_id_table_3410[j].idProduct = product_3410[i];
ti_id_table_3410[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
}
- j = sizeof(ti_id_table_5052)/sizeof(struct usb_device_id)
- - TI_EXTRA_VID_PID_COUNT - 1;
+ j = ARRAY_SIZE(ti_id_table_5052) - TI_EXTRA_VID_PID_COUNT - 1;
for (i=0; i<min(vendor_5052_count,product_5052_count); i++,j++) {
ti_id_table_5052[j].idVendor = vendor_5052[i];
ti_id_table_5052[j].idProduct = product_5052[i];
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 0c4881d18cd5..8bc8337c99c4 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -30,6 +30,7 @@
#include <linux/list.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
+#include <asm/semaphore.h>
#include <linux/usb.h>
#include "usb-serial.h"
#include "pl2303.h"
@@ -42,10 +43,10 @@
/* Driver structure we register with the USB core */
static struct usb_driver usb_serial_driver = {
- .owner = THIS_MODULE,
.name = "usbserial",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
+ .no_dynamic_id = 1,
};
/* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead
@@ -188,6 +189,11 @@ static int serial_open (struct tty_struct *tty, struct file * filp)
portNumber = tty->index - serial->minor;
port = serial->port[portNumber];
+ if (!port)
+ return -ENODEV;
+
+ if (down_interruptible(&port->sem))
+ return -ERESTARTSYS;
++port->open_count;
@@ -213,6 +219,7 @@ static int serial_open (struct tty_struct *tty, struct file * filp)
goto bailout_module_put;
}
+ up(&port->sem);
return 0;
bailout_module_put:
@@ -220,6 +227,7 @@ bailout_module_put:
bailout_kref_put:
kref_put(&serial->kref, destroy_serial);
port->open_count = 0;
+ up(&port->sem);
return retval;
}
@@ -232,8 +240,10 @@ static void serial_close(struct tty_struct *tty, struct file * filp)
dbg("%s - port %d", __FUNCTION__, port->number);
+ down(&port->sem);
+
if (port->open_count == 0)
- return;
+ goto out;
--port->open_count;
if (port->open_count == 0) {
@@ -251,6 +261,9 @@ static void serial_close(struct tty_struct *tty, struct file * filp)
}
kref_put(&port->serial->kref, destroy_serial);
+
+out:
+ up(&port->sem);
}
static int serial_write (struct tty_struct * tty, const unsigned char *buf, int count)
@@ -258,6 +271,9 @@ static int serial_write (struct tty_struct * tty, const unsigned char *buf, int
struct usb_serial_port *port = tty->driver_data;
int retval = -EINVAL;
+ if (!port)
+ goto exit;
+
dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);
if (!port->open_count) {
@@ -277,6 +293,9 @@ static int serial_write_room (struct tty_struct *tty)
struct usb_serial_port *port = tty->driver_data;
int retval = -EINVAL;
+ if (!port)
+ goto exit;
+
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port->open_count) {
@@ -296,6 +315,9 @@ static int serial_chars_in_buffer (struct tty_struct *tty)
struct usb_serial_port *port = tty->driver_data;
int retval = -EINVAL;
+ if (!port)
+ goto exit;
+
dbg("%s = port %d", __FUNCTION__, port->number);
if (!port->open_count) {
@@ -314,6 +336,9 @@ static void serial_throttle (struct tty_struct * tty)
{
struct usb_serial_port *port = tty->driver_data;
+ if (!port)
+ return;
+
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port->open_count) {
@@ -330,6 +355,9 @@ static void serial_unthrottle (struct tty_struct * tty)
{
struct usb_serial_port *port = tty->driver_data;
+ if (!port)
+ return;
+
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port->open_count) {
@@ -347,6 +375,9 @@ static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned in
struct usb_serial_port *port = tty->driver_data;
int retval = -ENODEV;
+ if (!port)
+ goto exit;
+
dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd);
if (!port->open_count) {
@@ -368,6 +399,9 @@ static void serial_set_termios (struct tty_struct *tty, struct termios * old)
{
struct usb_serial_port *port = tty->driver_data;
+ if (!port)
+ return;
+
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port->open_count) {
@@ -384,6 +418,9 @@ static void serial_break (struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
+ if (!port)
+ return;
+
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port->open_count) {
@@ -445,6 +482,9 @@ static int serial_tiocmget (struct tty_struct *tty, struct file *file)
{
struct usb_serial_port *port = tty->driver_data;
+ if (!port)
+ goto exit;
+
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port->open_count) {
@@ -464,6 +504,9 @@ static int serial_tiocmset (struct tty_struct *tty, struct file *file,
{
struct usb_serial_port *port = tty->driver_data;
+ if (!port)
+ goto exit;
+
dbg("%s - port %d", __FUNCTION__, port->number);
if (!port->open_count) {
@@ -742,6 +785,7 @@ int usb_serial_probe(struct usb_interface *interface,
port->number = i + serial->minor;
port->serial = serial;
spin_lock_init(&port->lock);
+ sema_init(&port->sem, 1);
INIT_WORK(&port->work, usb_serial_port_softint, port);
serial->port[i] = port;
}
diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h
index 238a5a871ed6..d7d27c3385b3 100644
--- a/drivers/usb/serial/usb-serial.h
+++ b/drivers/usb/serial/usb-serial.h
@@ -16,6 +16,7 @@
#include <linux/config.h>
#include <linux/kref.h>
+#include <asm/semaphore.h>
#define SERIAL_TTY_MAJOR 188 /* Nice legal number now */
#define SERIAL_TTY_MINORS 255 /* loads of devices :) */
@@ -30,6 +31,8 @@
* @serial: pointer back to the struct usb_serial owner of this port.
* @tty: pointer to the corresponding tty for this port.
* @lock: spinlock to grab when updating portions of this structure.
+ * @sem: semaphore used to synchronize serial_open() and serial_close()
+ * access for this port.
* @number: the number of the port (the minor number).
* @interrupt_in_buffer: pointer to the interrupt in buffer for this port.
* @interrupt_in_urb: pointer to the interrupt in struct urb for this port.
@@ -60,6 +63,7 @@ struct usb_serial_port {
struct usb_serial * serial;
struct tty_struct * tty;
spinlock_t lock;
+ struct semaphore sem;
unsigned char number;
unsigned char * interrupt_in_buffer;
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index a473c1c34559..49b1fbe61f25 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -173,11 +173,11 @@ static struct usb_device_id id_table_combined [] = {
MODULE_DEVICE_TABLE (usb, id_table_combined);
static struct usb_driver visor_driver = {
- .owner = THIS_MODULE,
.name = "visor",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table_combined,
+ .no_dynamic_id = 1,
};
/* All of the device info needed for the Handspring Visor, and Palm 4.0 devices */
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 18c3183be769..a7c3c4734d83 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -127,11 +127,11 @@ static struct usb_device_id id_table_combined [] = {
MODULE_DEVICE_TABLE (usb, id_table_combined);
static struct usb_driver whiteheat_driver = {
- .owner = THIS_MODULE,
.name = "whiteheat",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table_combined,
+ .no_dynamic_id = 1,
};
/* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index c41d64dbb0f0..92be101feba7 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -112,6 +112,15 @@ config USB_STORAGE_JUMPSHOT
Say Y here to include additional code to support the Lexar Jumpshot
USB CompactFlash reader.
+config USB_STORAGE_ALAUDA
+ bool "Olympus MAUSB-10/Fuji DPC-R1 support (EXPERIMENTAL)"
+ depends on USB_STORAGE && EXPERIMENTAL
+ help
+ Say Y here to include additional code to support the Olympus MAUSB-10
+ and Fujifilm DPC-R1 USB Card reader/writer devices.
+
+ These devices are based on the Alauda chip and support support both
+ XD and SmartMedia cards.
config USB_STORAGE_ONETOUCH
bool "Support OneTouch Button on Maxtor Hard Drives (EXPERIMENTAL)"
@@ -124,3 +133,17 @@ config USB_STORAGE_ONETOUCH
hard drive's as an input device. An action can be associated with
this input in any keybinding software. (e.g. gnome's keyboard short-
cuts)
+
+config USB_LIBUSUAL
+ bool "The shared table of common (or usual) storage devices"
+ depends on USB
+ help
+ This module contains a table of common (or usual) devices
+ for usb-storage and ub drivers, and allows to switch binding
+ of these devices without rebuilding modules.
+
+ Typical syntax of /etc/modprobe.conf is:
+
+ options libusual bias="ub"
+
+ If unsure, say N.
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
index 44ab8f9978fe..8cbba22508a4 100644
--- a/drivers/usb/storage/Makefile
+++ b/drivers/usb/storage/Makefile
@@ -18,7 +18,12 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o
usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o
usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o
usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_ALAUDA) += alauda.o
usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o
usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \
initializers.o $(usb-storage-obj-y)
+
+ifneq ($(CONFIG_USB_LIBUSUAL),)
+ obj-$(CONFIG_USB) += libusual.o
+endif
diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c
new file mode 100644
index 000000000000..4d3cbb12b713
--- /dev/null
+++ b/drivers/usb/storage/alauda.c
@@ -0,0 +1,1119 @@
+/*
+ * Driver for Alauda-based card readers
+ *
+ * Current development and maintenance by:
+ * (c) 2005 Daniel Drake <dsd@gentoo.org>
+ *
+ * The 'Alauda' is a chip manufacturered by RATOC for OEM use.
+ *
+ * Alauda implements a vendor-specific command set to access two media reader
+ * ports (XD, SmartMedia). This driver converts SCSI commands to the commands
+ * which are accepted by these devices.
+ *
+ * The driver was developed through reverse-engineering, with the help of the
+ * sddr09 driver which has many similarities, and with some help from the
+ * (very old) vendor-supplied GPL sma03 driver.
+ *
+ * For protocol info, see http://alauda.sourceforge.net
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "protocol.h"
+#include "debug.h"
+#include "alauda.h"
+
+#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) )
+#define LSB_of(s) ((s)&0xFF)
+#define MSB_of(s) ((s)>>8)
+
+#define MEDIA_PORT(us) us->srb->device->lun
+#define MEDIA_INFO(us) ((struct alauda_info *)us->extra)->port[MEDIA_PORT(us)]
+
+#define PBA_LO(pba) ((pba & 0xF) << 5)
+#define PBA_HI(pba) (pba >> 3)
+#define PBA_ZONE(pba) (pba >> 11)
+
+/*
+ * Media handling
+ */
+
+struct alauda_card_info {
+ unsigned char id; /* id byte */
+ unsigned char chipshift; /* 1<<cs bytes total capacity */
+ unsigned char pageshift; /* 1<<ps bytes in a page */
+ unsigned char blockshift; /* 1<<bs pages per block */
+ unsigned char zoneshift; /* 1<<zs blocks per zone */
+};
+
+static struct alauda_card_info alauda_card_ids[] = {
+ /* NAND flash */
+ { 0x6e, 20, 8, 4, 8}, /* 1 MB */
+ { 0xe8, 20, 8, 4, 8}, /* 1 MB */
+ { 0xec, 20, 8, 4, 8}, /* 1 MB */
+ { 0x64, 21, 8, 4, 9}, /* 2 MB */
+ { 0xea, 21, 8, 4, 9}, /* 2 MB */
+ { 0x6b, 22, 9, 4, 9}, /* 4 MB */
+ { 0xe3, 22, 9, 4, 9}, /* 4 MB */
+ { 0xe5, 22, 9, 4, 9}, /* 4 MB */
+ { 0xe6, 23, 9, 4, 10}, /* 8 MB */
+ { 0x73, 24, 9, 5, 10}, /* 16 MB */
+ { 0x75, 25, 9, 5, 10}, /* 32 MB */
+ { 0x76, 26, 9, 5, 10}, /* 64 MB */
+ { 0x79, 27, 9, 5, 10}, /* 128 MB */
+ { 0x71, 28, 9, 5, 10}, /* 256 MB */
+
+ /* MASK ROM */
+ { 0x5d, 21, 9, 4, 8}, /* 2 MB */
+ { 0xd5, 22, 9, 4, 9}, /* 4 MB */
+ { 0xd6, 23, 9, 4, 10}, /* 8 MB */
+ { 0x57, 24, 9, 4, 11}, /* 16 MB */
+ { 0x58, 25, 9, 4, 12}, /* 32 MB */
+ { 0,}
+};
+
+static struct alauda_card_info *alauda_card_find_id(unsigned char id) {
+ int i;
+
+ for (i = 0; alauda_card_ids[i].id != 0; i++)
+ if (alauda_card_ids[i].id == id)
+ return &(alauda_card_ids[i]);
+ return NULL;
+}
+
+/*
+ * ECC computation.
+ */
+
+static unsigned char parity[256];
+static unsigned char ecc2[256];
+
+static void nand_init_ecc(void) {
+ int i, j, a;
+
+ parity[0] = 0;
+ for (i = 1; i < 256; i++)
+ parity[i] = (parity[i&(i-1)] ^ 1);
+
+ for (i = 0; i < 256; i++) {
+ a = 0;
+ for (j = 0; j < 8; j++) {
+ if (i & (1<<j)) {
+ if ((j & 1) == 0)
+ a ^= 0x04;
+ if ((j & 2) == 0)
+ a ^= 0x10;
+ if ((j & 4) == 0)
+ a ^= 0x40;
+ }
+ }
+ ecc2[i] = ~(a ^ (a<<1) ^ (parity[i] ? 0xa8 : 0));
+ }
+}
+
+/* compute 3-byte ecc on 256 bytes */
+static void nand_compute_ecc(unsigned char *data, unsigned char *ecc) {
+ int i, j, a;
+ unsigned char par, bit, bits[8];
+
+ par = 0;
+ for (j = 0; j < 8; j++)
+ bits[j] = 0;
+
+ /* collect 16 checksum bits */
+ for (i = 0; i < 256; i++) {
+ par ^= data[i];
+ bit = parity[data[i]];
+ for (j = 0; j < 8; j++)
+ if ((i & (1<<j)) == 0)
+ bits[j] ^= bit;
+ }
+
+ /* put 4+4+4 = 12 bits in the ecc */
+ a = (bits[3] << 6) + (bits[2] << 4) + (bits[1] << 2) + bits[0];
+ ecc[0] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0));
+
+ a = (bits[7] << 6) + (bits[6] << 4) + (bits[5] << 2) + bits[4];
+ ecc[1] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0));
+
+ ecc[2] = ecc2[par];
+}
+
+static int nand_compare_ecc(unsigned char *data, unsigned char *ecc) {
+ return (data[0] == ecc[0] && data[1] == ecc[1] && data[2] == ecc[2]);
+}
+
+static void nand_store_ecc(unsigned char *data, unsigned char *ecc) {
+ memcpy(data, ecc, 3);
+}
+
+/*
+ * Alauda driver
+ */
+
+/*
+ * Forget our PBA <---> LBA mappings for a particular port
+ */
+static void alauda_free_maps (struct alauda_media_info *media_info)
+{
+ unsigned int shift = media_info->zoneshift
+ + media_info->blockshift + media_info->pageshift;
+ unsigned int num_zones = media_info->capacity >> shift;
+ unsigned int i;
+
+ if (media_info->lba_to_pba != NULL)
+ for (i = 0; i < num_zones; i++) {
+ kfree(media_info->lba_to_pba[i]);
+ media_info->lba_to_pba[i] = NULL;
+ }
+
+ if (media_info->pba_to_lba != NULL)
+ for (i = 0; i < num_zones; i++) {
+ kfree(media_info->pba_to_lba[i]);
+ media_info->pba_to_lba[i] = NULL;
+ }
+}
+
+/*
+ * Returns 2 bytes of status data
+ * The first byte describes media status, and second byte describes door status
+ */
+static int alauda_get_media_status(struct us_data *us, unsigned char *data)
+{
+ int rc;
+ unsigned char command;
+
+ if (MEDIA_PORT(us) == ALAUDA_PORT_XD)
+ command = ALAUDA_GET_XD_MEDIA_STATUS;
+ else
+ command = ALAUDA_GET_SM_MEDIA_STATUS;
+
+ rc = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe,
+ command, 0xc0, 0, 1, data, 2);
+
+ US_DEBUGP("alauda_get_media_status: Media status %02X %02X\n",
+ data[0], data[1]);
+
+ return rc;
+}
+
+/*
+ * Clears the "media was changed" bit so that we know when it changes again
+ * in the future.
+ */
+static int alauda_ack_media(struct us_data *us)
+{
+ unsigned char command;
+
+ if (MEDIA_PORT(us) == ALAUDA_PORT_XD)
+ command = ALAUDA_ACK_XD_MEDIA_CHANGE;
+ else
+ command = ALAUDA_ACK_SM_MEDIA_CHANGE;
+
+ return usb_stor_ctrl_transfer(us, us->send_ctrl_pipe,
+ command, 0x40, 0, 1, NULL, 0);
+}
+
+/*
+ * Retrieves a 4-byte media signature, which indicates manufacturer, capacity,
+ * and some other details.
+ */
+static int alauda_get_media_signature(struct us_data *us, unsigned char *data)
+{
+ unsigned char command;
+
+ if (MEDIA_PORT(us) == ALAUDA_PORT_XD)
+ command = ALAUDA_GET_XD_MEDIA_SIG;
+ else
+ command = ALAUDA_GET_SM_MEDIA_SIG;
+
+ return usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe,
+ command, 0xc0, 0, 0, data, 4);
+}
+
+/*
+ * Resets the media status (but not the whole device?)
+ */
+static int alauda_reset_media(struct us_data *us)
+{
+ unsigned char *command = us->iobuf;
+
+ memset(command, 0, 9);
+ command[0] = ALAUDA_BULK_CMD;
+ command[1] = ALAUDA_BULK_RESET_MEDIA;
+ command[8] = MEDIA_PORT(us);
+
+ return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+ command, 9, NULL);
+}
+
+/*
+ * Examines the media and deduces capacity, etc.
+ */
+static int alauda_init_media(struct us_data *us)
+{
+ unsigned char *data = us->iobuf;
+ int ready = 0;
+ struct alauda_card_info *media_info;
+ unsigned int num_zones;
+
+ while (ready == 0) {
+ msleep(20);
+
+ if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ if (data[0] & 0x10)
+ ready = 1;
+ }
+
+ US_DEBUGP("alauda_init_media: We are ready for action!\n");
+
+ if (alauda_ack_media(us) != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ msleep(10);
+
+ if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ if (data[0] != 0x14) {
+ US_DEBUGP("alauda_init_media: Media not ready after ack\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ if (alauda_get_media_signature(us, data) != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ US_DEBUGP("alauda_init_media: Media signature: %02X %02X %02X %02X\n",
+ data[0], data[1], data[2], data[3]);
+ media_info = alauda_card_find_id(data[1]);
+ if (media_info == NULL) {
+ printk("alauda_init_media: Unrecognised media signature: "
+ "%02X %02X %02X %02X\n",
+ data[0], data[1], data[2], data[3]);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ MEDIA_INFO(us).capacity = 1 << media_info->chipshift;
+ US_DEBUGP("Found media with capacity: %ldMB\n",
+ MEDIA_INFO(us).capacity >> 20);
+
+ MEDIA_INFO(us).pageshift = media_info->pageshift;
+ MEDIA_INFO(us).blockshift = media_info->blockshift;
+ MEDIA_INFO(us).zoneshift = media_info->zoneshift;
+
+ MEDIA_INFO(us).pagesize = 1 << media_info->pageshift;
+ MEDIA_INFO(us).blocksize = 1 << media_info->blockshift;
+ MEDIA_INFO(us).zonesize = 1 << media_info->zoneshift;
+
+ MEDIA_INFO(us).uzonesize = ((1 << media_info->zoneshift) / 128) * 125;
+ MEDIA_INFO(us).blockmask = MEDIA_INFO(us).blocksize - 1;
+
+ num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift
+ + MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift);
+ MEDIA_INFO(us).pba_to_lba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO);
+ MEDIA_INFO(us).lba_to_pba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO);
+
+ if (alauda_reset_media(us) != USB_STOR_XFER_GOOD)
+ return USB_STOR_TRANSPORT_ERROR;
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Examines the media status and does the right thing when the media has gone,
+ * appeared, or changed.
+ */
+static int alauda_check_media(struct us_data *us)
+{
+ struct alauda_info *info = (struct alauda_info *) us->extra;
+ unsigned char status[2];
+ int rc;
+
+ rc = alauda_get_media_status(us, status);
+
+ /* Check for no media or door open */
+ if ((status[0] & 0x80) || ((status[0] & 0x1F) == 0x10)
+ || ((status[1] & 0x01) == 0)) {
+ US_DEBUGP("alauda_check_media: No media, or door open\n");
+ alauda_free_maps(&MEDIA_INFO(us));
+ info->sense_key = 0x02;
+ info->sense_asc = 0x3A;
+ info->sense_ascq = 0x00;
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+
+ /* Check for media change */
+ if (status[0] & 0x08) {
+ US_DEBUGP("alauda_check_media: Media change detected\n");
+ alauda_free_maps(&MEDIA_INFO(us));
+ alauda_init_media(us);
+
+ info->sense_key = UNIT_ATTENTION;
+ info->sense_asc = 0x28;
+ info->sense_ascq = 0x00;
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Checks the status from the 2nd status register
+ * Returns 3 bytes of status data, only the first is known
+ */
+static int alauda_check_status2(struct us_data *us)
+{
+ int rc;
+ unsigned char command[] = {
+ ALAUDA_BULK_CMD, ALAUDA_BULK_GET_STATUS2,
+ 0, 0, 0, 0, 3, 0, MEDIA_PORT(us)
+ };
+ unsigned char data[3];
+
+ rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+ command, 9, NULL);
+ if (rc != USB_STOR_XFER_GOOD)
+ return rc;
+
+ rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+ data, 3, NULL);
+ if (rc != USB_STOR_XFER_GOOD)
+ return rc;
+
+ US_DEBUGP("alauda_check_status2: %02X %02X %02X\n", data[0], data[1], data[2]);
+ if (data[0] & ALAUDA_STATUS_ERROR)
+ return USB_STOR_XFER_ERROR;
+
+ return USB_STOR_XFER_GOOD;
+}
+
+/*
+ * Gets the redundancy data for the first page of a PBA
+ * Returns 16 bytes.
+ */
+static int alauda_get_redu_data(struct us_data *us, u16 pba, unsigned char *data)
+{
+ int rc;
+ unsigned char command[] = {
+ ALAUDA_BULK_CMD, ALAUDA_BULK_GET_REDU_DATA,
+ PBA_HI(pba), PBA_ZONE(pba), 0, PBA_LO(pba), 0, 0, MEDIA_PORT(us)
+ };
+
+ rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+ command, 9, NULL);
+ if (rc != USB_STOR_XFER_GOOD)
+ return rc;
+
+ return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+ data, 16, NULL);
+}
+
+/*
+ * Finds the first unused PBA in a zone
+ * Returns the absolute PBA of an unused PBA, or 0 if none found.
+ */
+static u16 alauda_find_unused_pba(struct alauda_media_info *info,
+ unsigned int zone)
+{
+ u16 *pba_to_lba = info->pba_to_lba[zone];
+ unsigned int i;
+
+ for (i = 0; i < info->zonesize; i++)
+ if (pba_to_lba[i] == UNDEF)
+ return (zone << info->zoneshift) + i;
+
+ return 0;
+}
+
+/*
+ * Reads the redundancy data for all PBA's in a zone
+ * Produces lba <--> pba mappings
+ */
+static int alauda_read_map(struct us_data *us, unsigned int zone)
+{
+ unsigned char *data = us->iobuf;
+ int result;
+ int i, j;
+ unsigned int zonesize = MEDIA_INFO(us).zonesize;
+ unsigned int uzonesize = MEDIA_INFO(us).uzonesize;
+ unsigned int lba_offset, lba_real, blocknum;
+ unsigned int zone_base_lba = zone * uzonesize;
+ unsigned int zone_base_pba = zone * zonesize;
+ u16 *lba_to_pba = kcalloc(zonesize, sizeof(u16), GFP_NOIO);
+ u16 *pba_to_lba = kcalloc(zonesize, sizeof(u16), GFP_NOIO);
+ if (lba_to_pba == NULL || pba_to_lba == NULL) {
+ result = USB_STOR_TRANSPORT_ERROR;
+ goto error;
+ }
+
+ US_DEBUGP("alauda_read_map: Mapping blocks for zone %d\n", zone);
+
+ /* 1024 PBA's per zone */
+ for (i = 0; i < zonesize; i++)
+ lba_to_pba[i] = pba_to_lba[i] = UNDEF;
+
+ for (i = 0; i < zonesize; i++) {
+ blocknum = zone_base_pba + i;
+
+ result = alauda_get_redu_data(us, blocknum, data);
+ if (result != USB_STOR_XFER_GOOD) {
+ result = USB_STOR_TRANSPORT_ERROR;
+ goto error;
+ }
+
+ /* special PBAs have control field 0^16 */
+ for (j = 0; j < 16; j++)
+ if (data[j] != 0)
+ goto nonz;
+ pba_to_lba[i] = UNUSABLE;
+ US_DEBUGP("alauda_read_map: PBA %d has no logical mapping\n", blocknum);
+ continue;
+
+ nonz:
+ /* unwritten PBAs have control field FF^16 */
+ for (j = 0; j < 16; j++)
+ if (data[j] != 0xff)
+ goto nonff;
+ continue;
+
+ nonff:
+ /* normal PBAs start with six FFs */
+ if (j < 6) {
+ US_DEBUGP("alauda_read_map: PBA %d has no logical mapping: "
+ "reserved area = %02X%02X%02X%02X "
+ "data status %02X block status %02X\n",
+ blocknum, data[0], data[1], data[2], data[3],
+ data[4], data[5]);
+ pba_to_lba[i] = UNUSABLE;
+ continue;
+ }
+
+ if ((data[6] >> 4) != 0x01) {
+ US_DEBUGP("alauda_read_map: PBA %d has invalid address "
+ "field %02X%02X/%02X%02X\n",
+ blocknum, data[6], data[7], data[11], data[12]);
+ pba_to_lba[i] = UNUSABLE;
+ continue;
+ }
+
+ /* check even parity */
+ if (parity[data[6] ^ data[7]]) {
+ printk("alauda_read_map: Bad parity in LBA for block %d"
+ " (%02X %02X)\n", i, data[6], data[7]);
+ pba_to_lba[i] = UNUSABLE;
+ continue;
+ }
+
+ lba_offset = short_pack(data[7], data[6]);
+ lba_offset = (lba_offset & 0x07FF) >> 1;
+ lba_real = lba_offset + zone_base_lba;
+
+ /*
+ * Every 1024 physical blocks ("zone"), the LBA numbers
+ * go back to zero, but are within a higher block of LBA's.
+ * Also, there is a maximum of 1000 LBA's per zone.
+ * In other words, in PBA 1024-2047 you will find LBA 0-999
+ * which are really LBA 1000-1999. This allows for 24 bad
+ * or special physical blocks per zone.
+ */
+
+ if (lba_offset >= uzonesize) {
+ printk("alauda_read_map: Bad low LBA %d for block %d\n",
+ lba_real, blocknum);
+ continue;
+ }
+
+ if (lba_to_pba[lba_offset] != UNDEF) {
+ printk("alauda_read_map: LBA %d seen for PBA %d and %d\n",
+ lba_real, lba_to_pba[lba_offset], blocknum);
+ continue;
+ }
+
+ pba_to_lba[i] = lba_real;
+ lba_to_pba[lba_offset] = blocknum;
+ continue;
+ }
+
+ MEDIA_INFO(us).lba_to_pba[zone] = lba_to_pba;
+ MEDIA_INFO(us).pba_to_lba[zone] = pba_to_lba;
+ result = 0;
+ goto out;
+
+error:
+ kfree(lba_to_pba);
+ kfree(pba_to_lba);
+out:
+ return result;
+}
+
+/*
+ * Checks to see whether we have already mapped a certain zone
+ * If we haven't, the map is generated
+ */
+static void alauda_ensure_map_for_zone(struct us_data *us, unsigned int zone)
+{
+ if (MEDIA_INFO(us).lba_to_pba[zone] == NULL
+ || MEDIA_INFO(us).pba_to_lba[zone] == NULL)
+ alauda_read_map(us, zone);
+}
+
+/*
+ * Erases an entire block
+ */
+static int alauda_erase_block(struct us_data *us, u16 pba)
+{
+ int rc;
+ unsigned char command[] = {
+ ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba),
+ PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, MEDIA_PORT(us)
+ };
+ unsigned char buf[2];
+
+ US_DEBUGP("alauda_erase_block: Erasing PBA %d\n", pba);
+
+ rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+ command, 9, NULL);
+ if (rc != USB_STOR_XFER_GOOD)
+ return rc;
+
+ rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+ buf, 2, NULL);
+ if (rc != USB_STOR_XFER_GOOD)
+ return rc;
+
+ US_DEBUGP("alauda_erase_block: Erase result: %02X %02X\n",
+ buf[0], buf[1]);
+ return rc;
+}
+
+/*
+ * Reads data from a certain offset page inside a PBA, including interleaved
+ * redundancy data. Returns (pagesize+64)*pages bytes in data.
+ */
+static int alauda_read_block_raw(struct us_data *us, u16 pba,
+ unsigned int page, unsigned int pages, unsigned char *data)
+{
+ int rc;
+ unsigned char command[] = {
+ ALAUDA_BULK_CMD, ALAUDA_BULK_READ_BLOCK, PBA_HI(pba),
+ PBA_ZONE(pba), 0, PBA_LO(pba) + page, pages, 0, MEDIA_PORT(us)
+ };
+
+ US_DEBUGP("alauda_read_block: pba %d page %d count %d\n",
+ pba, page, pages);
+
+ rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+ command, 9, NULL);
+ if (rc != USB_STOR_XFER_GOOD)
+ return rc;
+
+ return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+ data, (MEDIA_INFO(us).pagesize + 64) * pages, NULL);
+}
+
+/*
+ * Reads data from a certain offset page inside a PBA, excluding redundancy
+ * data. Returns pagesize*pages bytes in data. Note that data must be big enough
+ * to hold (pagesize+64)*pages bytes of data, but you can ignore those 'extra'
+ * trailing bytes outside this function.
+ */
+static int alauda_read_block(struct us_data *us, u16 pba,
+ unsigned int page, unsigned int pages, unsigned char *data)
+{
+ int i, rc;
+ unsigned int pagesize = MEDIA_INFO(us).pagesize;
+
+ rc = alauda_read_block_raw(us, pba, page, pages, data);
+ if (rc != USB_STOR_XFER_GOOD)
+ return rc;
+
+ /* Cut out the redundancy data */
+ for (i = 0; i < pages; i++) {
+ int dest_offset = i * pagesize;
+ int src_offset = i * (pagesize + 64);
+ memmove(data + dest_offset, data + src_offset, pagesize);
+ }
+
+ return rc;
+}
+
+/*
+ * Writes an entire block of data and checks status after write.
+ * Redundancy data must be already included in data. Data should be
+ * (pagesize+64)*blocksize bytes in length.
+ */
+static int alauda_write_block(struct us_data *us, u16 pba, unsigned char *data)
+{
+ int rc;
+ struct alauda_info *info = (struct alauda_info *) us->extra;
+ unsigned char command[] = {
+ ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_BLOCK, PBA_HI(pba),
+ PBA_ZONE(pba), 0, PBA_LO(pba), 32, 0, MEDIA_PORT(us)
+ };
+
+ US_DEBUGP("alauda_write_block: pba %d\n", pba);
+
+ rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+ command, 9, NULL);
+ if (rc != USB_STOR_XFER_GOOD)
+ return rc;
+
+ rc = usb_stor_bulk_transfer_buf(us, info->wr_ep, data,
+ (MEDIA_INFO(us).pagesize + 64) * MEDIA_INFO(us).blocksize,
+ NULL);
+ if (rc != USB_STOR_XFER_GOOD)
+ return rc;
+
+ return alauda_check_status2(us);
+}
+
+/*
+ * Write some data to a specific LBA.
+ */
+static int alauda_write_lba(struct us_data *us, u16 lba,
+ unsigned int page, unsigned int pages,
+ unsigned char *ptr, unsigned char *blockbuffer)
+{
+ u16 pba, lbap, new_pba;
+ unsigned char *bptr, *cptr, *xptr;
+ unsigned char ecc[3];
+ int i, result;
+ unsigned int uzonesize = MEDIA_INFO(us).uzonesize;
+ unsigned int zonesize = MEDIA_INFO(us).zonesize;
+ unsigned int pagesize = MEDIA_INFO(us).pagesize;
+ unsigned int blocksize = MEDIA_INFO(us).blocksize;
+ unsigned int lba_offset = lba % uzonesize;
+ unsigned int new_pba_offset;
+ unsigned int zone = lba / uzonesize;
+
+ alauda_ensure_map_for_zone(us, zone);
+
+ pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset];
+ if (pba == 1) {
+ /* Maybe it is impossible to write to PBA 1.
+ Fake success, but don't do anything. */
+ printk("alauda_write_lba: avoid writing to pba 1\n");
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ new_pba = alauda_find_unused_pba(&MEDIA_INFO(us), zone);
+ if (!new_pba) {
+ printk("alauda_write_lba: Out of unused blocks\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* read old contents */
+ if (pba != UNDEF) {
+ result = alauda_read_block_raw(us, pba, 0,
+ blocksize, blockbuffer);
+ if (result != USB_STOR_XFER_GOOD)
+ return result;
+ } else {
+ memset(blockbuffer, 0, blocksize * (pagesize + 64));
+ }
+
+ lbap = (lba_offset << 1) | 0x1000;
+ if (parity[MSB_of(lbap) ^ LSB_of(lbap)])
+ lbap ^= 1;
+
+ /* check old contents and fill lba */
+ for (i = 0; i < blocksize; i++) {
+ bptr = blockbuffer + (i * (pagesize + 64));
+ cptr = bptr + pagesize;
+ nand_compute_ecc(bptr, ecc);
+ if (!nand_compare_ecc(cptr+13, ecc)) {
+ US_DEBUGP("Warning: bad ecc in page %d- of pba %d\n",
+ i, pba);
+ nand_store_ecc(cptr+13, ecc);
+ }
+ nand_compute_ecc(bptr + (pagesize / 2), ecc);
+ if (!nand_compare_ecc(cptr+8, ecc)) {
+ US_DEBUGP("Warning: bad ecc in page %d+ of pba %d\n",
+ i, pba);
+ nand_store_ecc(cptr+8, ecc);
+ }
+ cptr[6] = cptr[11] = MSB_of(lbap);
+ cptr[7] = cptr[12] = LSB_of(lbap);
+ }
+
+ /* copy in new stuff and compute ECC */
+ xptr = ptr;
+ for (i = page; i < page+pages; i++) {
+ bptr = blockbuffer + (i * (pagesize + 64));
+ cptr = bptr + pagesize;
+ memcpy(bptr, xptr, pagesize);
+ xptr += pagesize;
+ nand_compute_ecc(bptr, ecc);
+ nand_store_ecc(cptr+13, ecc);
+ nand_compute_ecc(bptr + (pagesize / 2), ecc);
+ nand_store_ecc(cptr+8, ecc);
+ }
+
+ result = alauda_write_block(us, new_pba, blockbuffer);
+ if (result != USB_STOR_XFER_GOOD)
+ return result;
+
+ new_pba_offset = new_pba - (zone * zonesize);
+ MEDIA_INFO(us).pba_to_lba[zone][new_pba_offset] = lba;
+ MEDIA_INFO(us).lba_to_pba[zone][lba_offset] = new_pba;
+ US_DEBUGP("alauda_write_lba: Remapped LBA %d to PBA %d\n",
+ lba, new_pba);
+
+ if (pba != UNDEF) {
+ unsigned int pba_offset = pba - (zone * zonesize);
+ result = alauda_erase_block(us, pba);
+ if (result != USB_STOR_XFER_GOOD)
+ return result;
+ MEDIA_INFO(us).pba_to_lba[zone][pba_offset] = UNDEF;
+ }
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/*
+ * Read data from a specific sector address
+ */
+static int alauda_read_data(struct us_data *us, unsigned long address,
+ unsigned int sectors)
+{
+ unsigned char *buffer;
+ u16 lba, max_lba;
+ unsigned int page, len, index, offset;
+ unsigned int blockshift = MEDIA_INFO(us).blockshift;
+ unsigned int pageshift = MEDIA_INFO(us).pageshift;
+ unsigned int blocksize = MEDIA_INFO(us).blocksize;
+ unsigned int pagesize = MEDIA_INFO(us).pagesize;
+ unsigned int uzonesize = MEDIA_INFO(us).uzonesize;
+ int result;
+
+ /*
+ * Since we only read in one block at a time, we have to create
+ * a bounce buffer and move the data a piece at a time between the
+ * bounce buffer and the actual transfer buffer.
+ * We make this buffer big enough to hold temporary redundancy data,
+ * which we use when reading the data blocks.
+ */
+
+ len = min(sectors, blocksize) * (pagesize + 64);
+ buffer = kmalloc(len, GFP_NOIO);
+ if (buffer == NULL) {
+ printk("alauda_read_data: Out of memory\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* Figure out the initial LBA and page */
+ lba = address >> blockshift;
+ page = (address & MEDIA_INFO(us).blockmask);
+ max_lba = MEDIA_INFO(us).capacity >> (blockshift + pageshift);
+
+ result = USB_STOR_TRANSPORT_GOOD;
+ index = offset = 0;
+
+ while (sectors > 0) {
+ unsigned int zone = lba / uzonesize; /* integer division */
+ unsigned int lba_offset = lba - (zone * uzonesize);
+ unsigned int pages;
+ u16 pba;
+ alauda_ensure_map_for_zone(us, zone);
+
+ /* Not overflowing capacity? */
+ if (lba >= max_lba) {
+ US_DEBUGP("Error: Requested lba %u exceeds "
+ "maximum %u\n", lba, max_lba);
+ result = USB_STOR_TRANSPORT_ERROR;
+ break;
+ }
+
+ /* Find number of pages we can read in this block */
+ pages = min(sectors, blocksize - page);
+ len = pages << pageshift;
+
+ /* Find where this lba lives on disk */
+ pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset];
+
+ if (pba == UNDEF) { /* this lba was never written */
+ US_DEBUGP("Read %d zero pages (LBA %d) page %d\n",
+ pages, lba, page);
+
+ /* This is not really an error. It just means
+ that the block has never been written.
+ Instead of returning USB_STOR_TRANSPORT_ERROR
+ it is better to return all zero data. */
+
+ memset(buffer, 0, len);
+ } else {
+ US_DEBUGP("Read %d pages, from PBA %d"
+ " (LBA %d) page %d\n",
+ pages, pba, lba, page);
+
+ result = alauda_read_block(us, pba, page, pages, buffer);
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ break;
+ }
+
+ /* Store the data in the transfer buffer */
+ usb_stor_access_xfer_buf(buffer, len, us->srb,
+ &index, &offset, TO_XFER_BUF);
+
+ page = 0;
+ lba++;
+ sectors -= pages;
+ }
+
+ kfree(buffer);
+ return result;
+}
+
+/*
+ * Write data to a specific sector address
+ */
+static int alauda_write_data(struct us_data *us, unsigned long address,
+ unsigned int sectors)
+{
+ unsigned char *buffer, *blockbuffer;
+ unsigned int page, len, index, offset;
+ unsigned int blockshift = MEDIA_INFO(us).blockshift;
+ unsigned int pageshift = MEDIA_INFO(us).pageshift;
+ unsigned int blocksize = MEDIA_INFO(us).blocksize;
+ unsigned int pagesize = MEDIA_INFO(us).pagesize;
+ u16 lba, max_lba;
+ int result;
+
+ /*
+ * Since we don't write the user data directly to the device,
+ * we have to create a bounce buffer and move the data a piece
+ * at a time between the bounce buffer and the actual transfer buffer.
+ */
+
+ len = min(sectors, blocksize) * pagesize;
+ buffer = kmalloc(len, GFP_NOIO);
+ if (buffer == NULL) {
+ printk("alauda_write_data: Out of memory\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /*
+ * We also need a temporary block buffer, where we read in the old data,
+ * overwrite parts with the new data, and manipulate the redundancy data
+ */
+ blockbuffer = kmalloc((pagesize + 64) * blocksize, GFP_NOIO);
+ if (blockbuffer == NULL) {
+ printk("alauda_write_data: Out of memory\n");
+ kfree(buffer);
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* Figure out the initial LBA and page */
+ lba = address >> blockshift;
+ page = (address & MEDIA_INFO(us).blockmask);
+ max_lba = MEDIA_INFO(us).capacity >> (pageshift + blockshift);
+
+ result = USB_STOR_TRANSPORT_GOOD;
+ index = offset = 0;
+
+ while (sectors > 0) {
+ /* Write as many sectors as possible in this block */
+ unsigned int pages = min(sectors, blocksize - page);
+ len = pages << pageshift;
+
+ /* Not overflowing capacity? */
+ if (lba >= max_lba) {
+ US_DEBUGP("alauda_write_data: Requested lba %u exceeds "
+ "maximum %u\n", lba, max_lba);
+ result = USB_STOR_TRANSPORT_ERROR;
+ break;
+ }
+
+ /* Get the data from the transfer buffer */
+ usb_stor_access_xfer_buf(buffer, len, us->srb,
+ &index, &offset, FROM_XFER_BUF);
+
+ result = alauda_write_lba(us, lba, page, pages, buffer,
+ blockbuffer);
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ break;
+
+ page = 0;
+ lba++;
+ sectors -= pages;
+ }
+
+ kfree(buffer);
+ kfree(blockbuffer);
+ return result;
+}
+
+/*
+ * Our interface with the rest of the world
+ */
+
+static void alauda_info_destructor(void *extra)
+{
+ struct alauda_info *info = (struct alauda_info *) extra;
+ int port;
+
+ if (!info)
+ return;
+
+ for (port = 0; port < 2; port++) {
+ struct alauda_media_info *media_info = &info->port[port];
+
+ alauda_free_maps(media_info);
+ kfree(media_info->lba_to_pba);
+ kfree(media_info->pba_to_lba);
+ }
+}
+
+/*
+ * Initialize alauda_info struct and find the data-write endpoint
+ */
+int init_alauda(struct us_data *us)
+{
+ struct alauda_info *info;
+ struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting;
+ nand_init_ecc();
+
+ us->extra = kzalloc(sizeof(struct alauda_info), GFP_NOIO);
+ if (!us->extra) {
+ US_DEBUGP("init_alauda: Gah! Can't allocate storage for"
+ "alauda info struct!\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ info = (struct alauda_info *) us->extra;
+ us->extra_destructor = alauda_info_destructor;
+
+ info->wr_ep = usb_sndbulkpipe(us->pusb_dev,
+ altsetting->endpoint[0].desc.bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK);
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+int alauda_transport(struct scsi_cmnd *srb, struct us_data *us)
+{
+ int rc;
+ struct alauda_info *info = (struct alauda_info *) us->extra;
+ unsigned char *ptr = us->iobuf;
+ static unsigned char inquiry_response[36] = {
+ 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
+ };
+
+ if (srb->cmnd[0] == INQUIRY) {
+ US_DEBUGP("alauda_transport: INQUIRY. "
+ "Returning bogus response.\n");
+ memcpy(ptr, inquiry_response, sizeof(inquiry_response));
+ fill_inquiry_response(us, ptr, 36);
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ if (srb->cmnd[0] == TEST_UNIT_READY) {
+ US_DEBUGP("alauda_transport: TEST_UNIT_READY.\n");
+ return alauda_check_media(us);
+ }
+
+ if (srb->cmnd[0] == READ_CAPACITY) {
+ unsigned int num_zones;
+ unsigned long capacity;
+
+ rc = alauda_check_media(us);
+ if (rc != USB_STOR_TRANSPORT_GOOD)
+ return rc;
+
+ num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift
+ + MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift);
+
+ capacity = num_zones * MEDIA_INFO(us).uzonesize
+ * MEDIA_INFO(us).blocksize;
+
+ /* Report capacity and page size */
+ ((__be32 *) ptr)[0] = cpu_to_be32(capacity - 1);
+ ((__be32 *) ptr)[1] = cpu_to_be32(512);
+
+ usb_stor_set_xfer_buf(ptr, 8, srb);
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ if (srb->cmnd[0] == READ_10) {
+ unsigned int page, pages;
+
+ rc = alauda_check_media(us);
+ if (rc != USB_STOR_TRANSPORT_GOOD)
+ return rc;
+
+ page = short_pack(srb->cmnd[3], srb->cmnd[2]);
+ page <<= 16;
+ page |= short_pack(srb->cmnd[5], srb->cmnd[4]);
+ pages = short_pack(srb->cmnd[8], srb->cmnd[7]);
+
+ US_DEBUGP("alauda_transport: READ_10: page %d pagect %d\n",
+ page, pages);
+
+ return alauda_read_data(us, page, pages);
+ }
+
+ if (srb->cmnd[0] == WRITE_10) {
+ unsigned int page, pages;
+
+ rc = alauda_check_media(us);
+ if (rc != USB_STOR_TRANSPORT_GOOD)
+ return rc;
+
+ page = short_pack(srb->cmnd[3], srb->cmnd[2]);
+ page <<= 16;
+ page |= short_pack(srb->cmnd[5], srb->cmnd[4]);
+ pages = short_pack(srb->cmnd[8], srb->cmnd[7]);
+
+ US_DEBUGP("alauda_transport: WRITE_10: page %d pagect %d\n",
+ page, pages);
+
+ return alauda_write_data(us, page, pages);
+ }
+
+ if (srb->cmnd[0] == REQUEST_SENSE) {
+ US_DEBUGP("alauda_transport: REQUEST_SENSE.\n");
+
+ memset(ptr, 0, 18);
+ ptr[0] = 0xF0;
+ ptr[2] = info->sense_key;
+ ptr[7] = 11;
+ ptr[12] = info->sense_asc;
+ ptr[13] = info->sense_ascq;
+ usb_stor_set_xfer_buf(ptr, 18, srb);
+
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
+ /* sure. whatever. not like we can stop the user from popping
+ the media out of the device (no locking doors, etc) */
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ US_DEBUGP("alauda_transport: Gah! Unknown command: %d (0x%x)\n",
+ srb->cmnd[0], srb->cmnd[0]);
+ info->sense_key = 0x05;
+ info->sense_asc = 0x20;
+ info->sense_ascq = 0x00;
+ return USB_STOR_TRANSPORT_FAILED;
+}
+
diff --git a/drivers/usb/storage/alauda.h b/drivers/usb/storage/alauda.h
new file mode 100644
index 000000000000..a700f87d0803
--- /dev/null
+++ b/drivers/usb/storage/alauda.h
@@ -0,0 +1,100 @@
+/*
+ * Driver for Alauda-based card readers
+ *
+ * Current development and maintenance by:
+ * (c) 2005 Daniel Drake <dsd@gentoo.org>
+ *
+ * See alauda.c for more explanation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _USB_ALAUDA_H
+#define _USB_ALAUDA_H
+
+/*
+ * Status bytes
+ */
+#define ALAUDA_STATUS_ERROR 0x01
+#define ALAUDA_STATUS_READY 0x40
+
+/*
+ * Control opcodes (for request field)
+ */
+#define ALAUDA_GET_XD_MEDIA_STATUS 0x08
+#define ALAUDA_GET_SM_MEDIA_STATUS 0x98
+#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a
+#define ALAUDA_ACK_SM_MEDIA_CHANGE 0x9a
+#define ALAUDA_GET_XD_MEDIA_SIG 0x86
+#define ALAUDA_GET_SM_MEDIA_SIG 0x96
+
+/*
+ * Bulk command identity (byte 0)
+ */
+#define ALAUDA_BULK_CMD 0x40
+
+/*
+ * Bulk opcodes (byte 1)
+ */
+#define ALAUDA_BULK_GET_REDU_DATA 0x85
+#define ALAUDA_BULK_READ_BLOCK 0x94
+#define ALAUDA_BULK_ERASE_BLOCK 0xa3
+#define ALAUDA_BULK_WRITE_BLOCK 0xb4
+#define ALAUDA_BULK_GET_STATUS2 0xb7
+#define ALAUDA_BULK_RESET_MEDIA 0xe0
+
+/*
+ * Port to operate on (byte 8)
+ */
+#define ALAUDA_PORT_XD 0x00
+#define ALAUDA_PORT_SM 0x01
+
+/*
+ * LBA and PBA are unsigned ints. Special values.
+ */
+#define UNDEF 0xffff
+#define SPARE 0xfffe
+#define UNUSABLE 0xfffd
+
+int init_alauda(struct us_data *us);
+int alauda_transport(struct scsi_cmnd *srb, struct us_data *us);
+
+struct alauda_media_info {
+ unsigned long capacity; /* total media size in bytes */
+ unsigned int pagesize; /* page size in bytes */
+ unsigned int blocksize; /* number of pages per block */
+ unsigned int uzonesize; /* number of usable blocks per zone */
+ unsigned int zonesize; /* number of blocks per zone */
+ unsigned int blockmask; /* mask to get page from address */
+
+ unsigned char pageshift;
+ unsigned char blockshift;
+ unsigned char zoneshift;
+
+ u16 **lba_to_pba; /* logical to physical block map */
+ u16 **pba_to_lba; /* physical to logical block map */
+};
+
+struct alauda_info {
+ struct alauda_media_info port[2];
+ int wr_ep; /* endpoint to write data out of */
+
+ unsigned char sense_key;
+ unsigned long sense_asc; /* additional sense code */
+ unsigned long sense_ascq; /* additional sense code qualifier */
+};
+
+#endif
+
diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c
index 5a9321705a74..01e430654a13 100644
--- a/drivers/usb/storage/debug.c
+++ b/drivers/usb/storage/debug.c
@@ -132,6 +132,7 @@ void usb_stor_show_command(struct scsi_cmnd *srb)
case 0x5C: what = "READ BUFFER CAPACITY"; break;
case 0x5D: what = "SEND CUE SHEET"; break;
case GPCMD_BLANK: what = "BLANK"; break;
+ case REPORT_LUNS: what = "REPORT LUNS"; break;
case MOVE_MEDIUM: what = "MOVE_MEDIUM or PLAY AUDIO (12)"; break;
case READ_12: what = "READ_12"; break;
case WRITE_12: what = "WRITE_12"; break;
diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h
index 7372386f33d5..4c1b2bd2e2e4 100644
--- a/drivers/usb/storage/initializers.h
+++ b/drivers/usb/storage/initializers.h
@@ -45,10 +45,6 @@
* mode */
int usb_stor_euscsi_init(struct us_data *us);
-#ifdef CONFIG_USB_STORAGE_SDDR09
-int sddr09_init(struct us_data *us);
-#endif
-
/* This function is required to activate all four slots on the UCR-61S2B
* flash reader */
int usb_stor_ucr61s2b_init(struct us_data *us);
diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c
new file mode 100644
index 000000000000..b28151d1b609
--- /dev/null
+++ b/drivers/usb/storage/libusual.c
@@ -0,0 +1,266 @@
+/*
+ * libusual
+ *
+ * The libusual contains the table of devices common for ub and usb-storage.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb_usual.h>
+#include <linux/vmalloc.h>
+
+/*
+ */
+#define USU_MOD_FL_THREAD 1 /* Thread is running */
+#define USU_MOD_FL_PRESENT 2 /* The module is loaded */
+
+struct mod_status {
+ unsigned long fls;
+};
+
+static struct mod_status stat[3];
+static DEFINE_SPINLOCK(usu_lock);
+
+/*
+ */
+#define USB_US_DEFAULT_BIAS USB_US_TYPE_STOR
+static atomic_t usu_bias = ATOMIC_INIT(USB_US_DEFAULT_BIAS);
+
+#define BIAS_NAME_SIZE (sizeof("usb-storage"))
+static const char *bias_names[3] = { "none", "usb-storage", "ub" };
+
+static DECLARE_MUTEX_LOCKED(usu_init_notify);
+static DECLARE_COMPLETION(usu_end_notify);
+static atomic_t total_threads = ATOMIC_INIT(0);
+
+static int usu_probe_thread(void *arg);
+
+/*
+ * The table.
+ */
+#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
+ vendorName, productName,useProtocol, useTransport, \
+ initFunction, flags) \
+{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax), \
+ .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+
+#define USUAL_DEV(useProto, useTrans, useType) \
+{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \
+ .driver_info = ((useType)<<24) }
+
+struct usb_device_id storage_usb_ids [] = {
+# include "unusual_devs.h"
+ { } /* Terminating entry */
+};
+
+#undef USUAL_DEV
+#undef UNUSUAL_DEV
+
+MODULE_DEVICE_TABLE(usb, storage_usb_ids);
+EXPORT_SYMBOL_GPL(storage_usb_ids);
+
+/*
+ * @type: the module type as an integer
+ */
+void usb_usual_set_present(int type)
+{
+ struct mod_status *st;
+ unsigned long flags;
+
+ if (type <= 0 || type >= 3)
+ return;
+ st = &stat[type];
+ spin_lock_irqsave(&usu_lock, flags);
+ st->fls |= USU_MOD_FL_PRESENT;
+ spin_unlock_irqrestore(&usu_lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_usual_set_present);
+
+void usb_usual_clear_present(int type)
+{
+ struct mod_status *st;
+ unsigned long flags;
+
+ if (type <= 0 || type >= 3)
+ return;
+ st = &stat[type];
+ spin_lock_irqsave(&usu_lock, flags);
+ st->fls &= ~USU_MOD_FL_PRESENT;
+ spin_unlock_irqrestore(&usu_lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_usual_clear_present);
+
+/*
+ * Match the calling driver type against the table.
+ * Returns: 0 if the device matches.
+ */
+int usb_usual_check_type(const struct usb_device_id *id, int caller_type)
+{
+ int id_type = USB_US_TYPE(id->driver_info);
+
+ if (caller_type <= 0 || caller_type >= 3)
+ return -EINVAL;
+
+ /* Drivers grab fixed assignment devices */
+ if (id_type == caller_type)
+ return 0;
+ /* Drivers grab devices biased to them */
+ if (id_type == USB_US_TYPE_NONE && caller_type == atomic_read(&usu_bias))
+ return 0;
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(usb_usual_check_type);
+
+/*
+ */
+static int usu_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int type;
+ int rc;
+ unsigned long flags;
+
+ type = USB_US_TYPE(id->driver_info);
+ if (type == 0)
+ type = atomic_read(&usu_bias);
+
+ spin_lock_irqsave(&usu_lock, flags);
+ if ((stat[type].fls & (USU_MOD_FL_THREAD|USU_MOD_FL_PRESENT)) != 0) {
+ spin_unlock_irqrestore(&usu_lock, flags);
+ return -ENXIO;
+ }
+ stat[type].fls |= USU_MOD_FL_THREAD;
+ spin_unlock_irqrestore(&usu_lock, flags);
+
+ rc = kernel_thread(usu_probe_thread, (void*)type, CLONE_VM);
+ if (rc < 0) {
+ printk(KERN_WARNING "libusual: "
+ "Unable to start the thread for %s: %d\n",
+ bias_names[type], rc);
+ spin_lock_irqsave(&usu_lock, flags);
+ stat[type].fls &= ~USU_MOD_FL_THREAD;
+ spin_unlock_irqrestore(&usu_lock, flags);
+ return rc; /* Not being -ENXIO causes a message printed */
+ }
+ atomic_inc(&total_threads);
+
+ return -ENXIO;
+}
+
+static void usu_disconnect(struct usb_interface *intf)
+{
+ ; /* We should not be here. */
+}
+
+static struct usb_driver usu_driver = {
+ .name = "libusual",
+ .probe = usu_probe,
+ .disconnect = usu_disconnect,
+ .id_table = storage_usb_ids,
+};
+
+/*
+ * A whole new thread for a purpose of request_module seems quite stupid.
+ * The request_module forks once inside again. However, if we attempt
+ * to load a storage module from our own modprobe thread, that module
+ * references our symbols, which cannot be resolved until our module is
+ * initialized. I wish there was a way to wait for the end of initialization.
+ * The module notifier reports MODULE_STATE_COMING only.
+ * So, we wait until module->init ends as the next best thing.
+ */
+static int usu_probe_thread(void *arg)
+{
+ int type = (unsigned long) arg;
+ struct mod_status *st = &stat[type];
+ int rc;
+ unsigned long flags;
+
+ daemonize("libusual_%d", type); /* "usb-storage" is kinda too long */
+
+ /* A completion does not work here because it's counted. */
+ down(&usu_init_notify);
+ up(&usu_init_notify);
+
+ rc = request_module(bias_names[type]);
+ spin_lock_irqsave(&usu_lock, flags);
+ if (rc == 0 && (st->fls & USU_MOD_FL_PRESENT) == 0) {
+ /*
+ * This should not happen, but let us keep tabs on it.
+ */
+ printk(KERN_NOTICE "libusual: "
+ "modprobe for %s succeeded, but module is not present\n",
+ bias_names[type]);
+ }
+ st->fls &= ~USU_MOD_FL_THREAD;
+ spin_unlock_irqrestore(&usu_lock, flags);
+
+ complete_and_exit(&usu_end_notify, 0);
+}
+
+/*
+ */
+static int __init usb_usual_init(void)
+{
+ int rc;
+
+ rc = usb_register(&usu_driver);
+ up(&usu_init_notify);
+ return rc;
+}
+
+static void __exit usb_usual_exit(void)
+{
+ /*
+ * We do not check for any drivers present, because
+ * they keep us pinned with symbol references.
+ */
+
+ usb_deregister(&usu_driver);
+
+ while (atomic_read(&total_threads) > 0) {
+ wait_for_completion(&usu_end_notify);
+ atomic_dec(&total_threads);
+ }
+}
+
+/*
+ * Validate and accept the bias parameter.
+ */
+static int usu_set_bias(const char *bias_s, struct kernel_param *kp)
+{
+ int i;
+ int len;
+ int bias_n = 0;
+
+ len = strlen(bias_s);
+ if (len == 0)
+ return -EDOM;
+ if (bias_s[len-1] == '\n')
+ --len;
+
+ for (i = 1; i < 3; i++) {
+ if (strncmp(bias_s, bias_names[i], len) == 0) {
+ bias_n = i;
+ break;
+ }
+ }
+ if (bias_n == 0)
+ return -EINVAL;
+
+ atomic_set(&usu_bias, bias_n);
+ return 0;
+}
+
+static int usu_get_bias(char *buffer, struct kernel_param *kp)
+{
+ return strlen(strcpy(buffer, bias_names[atomic_read(&usu_bias)]));
+}
+
+module_init(usb_usual_init);
+module_exit(usb_usual_exit);
+
+module_param_call(bias, usu_set_bias, usu_get_bias, NULL, S_IRUGO|S_IWUSR);
+__MODULE_PARM_TYPE(bias, "string");
+MODULE_PARM_DESC(bias, "Bias to usb-storage or ub");
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c
index 89401a59f952..55ee2d36d585 100644
--- a/drivers/usb/storage/onetouch.c
+++ b/drivers/usb/storage/onetouch.c
@@ -52,6 +52,7 @@ struct usb_onetouch {
struct urb *irq; /* urb for interrupt in report */
unsigned char *data; /* input data */
dma_addr_t data_dma;
+ unsigned int is_open:1;
};
static void usb_onetouch_irq(struct urb *urb, struct pt_regs *regs)
@@ -89,6 +90,7 @@ static int usb_onetouch_open(struct input_dev *dev)
{
struct usb_onetouch *onetouch = dev->private;
+ onetouch->is_open = 1;
onetouch->irq->dev = onetouch->udev;
if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) {
err("usb_submit_urb failed");
@@ -103,8 +105,30 @@ static void usb_onetouch_close(struct input_dev *dev)
struct usb_onetouch *onetouch = dev->private;
usb_kill_urb(onetouch->irq);
+ onetouch->is_open = 0;
}
+#ifdef CONFIG_PM
+static void usb_onetouch_pm_hook(struct us_data *us, int action)
+{
+ struct usb_onetouch *onetouch = (struct usb_onetouch *) us->extra;
+
+ if (onetouch->is_open) {
+ switch (action) {
+ case US_SUSPEND:
+ usb_kill_urb(onetouch->irq);
+ break;
+ case US_RESUME:
+ if (usb_submit_urb(onetouch->irq, GFP_KERNEL) != 0)
+ err("usb_submit_urb failed");
+ break;
+ default:
+ break;
+ }
+ }
+}
+#endif /* CONFIG_PM */
+
int onetouch_connect_input(struct us_data *ss)
{
struct usb_device *udev = ss->pusb_dev;
@@ -185,6 +209,9 @@ int onetouch_connect_input(struct us_data *ss)
ss->extra_destructor = onetouch_release_input;
ss->extra = onetouch;
+#ifdef CONFIG_PM
+ ss->suspend_resume_hook = usb_onetouch_pm_hook;
+#endif
input_register_device(onetouch->dev);
diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h
index 02bff01ab09c..845bed4b8031 100644
--- a/drivers/usb/storage/protocol.h
+++ b/drivers/usb/storage/protocol.h
@@ -41,20 +41,6 @@
#ifndef _PROTOCOL_H_
#define _PROTOCOL_H_
-/* Sub Classes */
-
-#define US_SC_RBC 0x01 /* Typically, flash devices */
-#define US_SC_8020 0x02 /* CD-ROM */
-#define US_SC_QIC 0x03 /* QIC-157 Tapes */
-#define US_SC_UFI 0x04 /* Floppy */
-#define US_SC_8070 0x05 /* Removable media */
-#define US_SC_SCSI 0x06 /* Transparent */
-#define US_SC_ISD200 0x07 /* ISD200 ATA */
-#define US_SC_MIN US_SC_RBC
-#define US_SC_MAX US_SC_ISD200
-
-#define US_SC_DEVICE 0xff /* Use device's value */
-
/* Protocol handling routines */
extern void usb_stor_ATAPI_command(struct scsi_cmnd*, struct us_data*);
extern void usb_stor_qic157_command(struct scsi_cmnd*, struct us_data*);
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 4837524eada7..4ef5527028c5 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -109,7 +109,7 @@ static int slave_configure(struct scsi_device *sdev)
* data comes from.
*/
if (sdev->scsi_level < SCSI_2)
- sdev->scsi_level = SCSI_2;
+ sdev->scsi_level = sdev->sdev_target->scsi_level = SCSI_2;
/* According to the technical support people at Genesys Logic,
* devices using their chips have problems transferring more than
@@ -162,7 +162,7 @@ static int slave_configure(struct scsi_device *sdev)
* a Get-Max-LUN request, we won't lose much by setting the
* revision level down to 2. The only devices that would be
* affected are those with sparse LUNs. */
- sdev->scsi_level = SCSI_2;
+ sdev->scsi_level = sdev->sdev_target->scsi_level = SCSI_2;
/* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable
* Hardware Error) when any low-level error occurs,
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index 0ea2f5ab66ba..fb8bacaae27c 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -133,13 +133,11 @@ static struct nand_flash_dev nand_flash_ids[] = {
{ 0,}
};
-#define SIZE(a) (sizeof(a)/sizeof((a)[0]))
-
static struct nand_flash_dev *
nand_find_id(unsigned char id) {
int i;
- for (i = 0; i < SIZE(nand_flash_ids); i++)
+ for (i = 0; i < ARRAY_SIZE(nand_flash_ids); i++)
if (nand_flash_ids[i].model_id == id)
return &(nand_flash_ids[i]);
return NULL;
@@ -214,6 +212,20 @@ static void nand_store_ecc(unsigned char *data, unsigned char *ecc) {
* The actual driver starts here.
*/
+struct sddr09_card_info {
+ unsigned long capacity; /* Size of card in bytes */
+ int pagesize; /* Size of page in bytes */
+ int pageshift; /* log2 of pagesize */
+ int blocksize; /* Size of block in pages */
+ int blockshift; /* log2 of blocksize */
+ int blockmask; /* 2^blockshift - 1 */
+ int *lba_to_pba; /* logical to physical map */
+ int *pba_to_lba; /* physical to logical map */
+ int lbact; /* number of available pages */
+ int flags;
+#define SDDR09_WP 1 /* write protected */
+};
+
/*
* On my 16MB card, control blocks have size 64 (16 real control bytes,
* and 48 junk bytes). In reality of course the card uses 16 control bytes,
@@ -237,7 +249,7 @@ static void nand_store_ecc(unsigned char *data, unsigned char *ecc) {
#define SPARE 0xfffffffe
#define UNUSABLE 0xfffffffd
-static int erase_bad_lba_entries = 0;
+static const int erase_bad_lba_entries = 0;
/* send vendor interface command (0x41) */
/* called for requests 0, 1, 8 */
@@ -260,8 +272,11 @@ sddr09_send_command(struct us_data *us,
rc = usb_stor_ctrl_transfer(us, pipe, request, requesttype,
0, 0, xfer_data, xfer_len);
- return (rc == USB_STOR_XFER_GOOD ? USB_STOR_TRANSPORT_GOOD :
- USB_STOR_TRANSPORT_ERROR);
+ switch (rc) {
+ case USB_STOR_XFER_GOOD: return 0;
+ case USB_STOR_XFER_STALLED: return -EPIPE;
+ default: return -EIO;
+ }
}
static int
@@ -308,20 +323,12 @@ sddr09_request_sense(struct us_data *us, unsigned char *sensebuf, int buflen) {
command[4] = buflen;
result = sddr09_send_scsi_command(us, command, 12);
- if (result != USB_STOR_TRANSPORT_GOOD) {
- US_DEBUGP("request sense failed\n");
+ if (result)
return result;
- }
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
sensebuf, buflen, NULL);
- if (result != USB_STOR_XFER_GOOD) {
- US_DEBUGP("request sense bulk in failed\n");
- return USB_STOR_TRANSPORT_ERROR;
- } else {
- US_DEBUGP("request sense worked\n");
- return USB_STOR_TRANSPORT_GOOD;
- }
+ return (result == USB_STOR_XFER_GOOD ? 0 : -EIO);
}
/*
@@ -369,7 +376,7 @@ sddr09_readX(struct us_data *us, int x, unsigned long fromaddress,
result = sddr09_send_scsi_command(us, command, 12);
- if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (result) {
US_DEBUGP("Result for send_control in sddr09_read2%d %d\n",
x, result);
return result;
@@ -381,9 +388,9 @@ sddr09_readX(struct us_data *us, int x, unsigned long fromaddress,
if (result != USB_STOR_XFER_GOOD) {
US_DEBUGP("Result for bulk_transfer in sddr09_read2%d %d\n",
x, result);
- return USB_STOR_TRANSPORT_ERROR;
+ return -EIO;
}
- return USB_STOR_TRANSPORT_GOOD;
+ return 0;
}
/*
@@ -497,7 +504,7 @@ sddr09_erase(struct us_data *us, unsigned long Eaddress) {
result = sddr09_send_scsi_command(us, command, 12);
- if (result != USB_STOR_TRANSPORT_GOOD)
+ if (result)
US_DEBUGP("Result for send_control in sddr09_erase %d\n",
result);
@@ -555,7 +562,7 @@ sddr09_writeX(struct us_data *us,
result = sddr09_send_scsi_command(us, command, 12);
- if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (result) {
US_DEBUGP("Result for send_control in sddr09_writeX %d\n",
result);
return result;
@@ -567,9 +574,9 @@ sddr09_writeX(struct us_data *us,
if (result != USB_STOR_XFER_GOOD) {
US_DEBUGP("Result for bulk_transfer in sddr09_writeX %d\n",
result);
- return USB_STOR_TRANSPORT_ERROR;
+ return -EIO;
}
- return USB_STOR_TRANSPORT_GOOD;
+ return 0;
}
/* erase address, write same address */
@@ -633,7 +640,7 @@ sddr09_read_sg_test_only(struct us_data *us) {
result = sddr09_send_scsi_command(us, command, 4*nsg+3);
- if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (result) {
US_DEBUGP("Result for send_control in sddr09_read_sg %d\n",
result);
return result;
@@ -641,7 +648,7 @@ sddr09_read_sg_test_only(struct us_data *us) {
buf = (unsigned char *) kmalloc(bulklen, GFP_NOIO);
if (!buf)
- return USB_STOR_TRANSPORT_ERROR;
+ return -ENOMEM;
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
buf, bulklen, NULL);
@@ -649,10 +656,10 @@ sddr09_read_sg_test_only(struct us_data *us) {
if (result != USB_STOR_XFER_GOOD) {
US_DEBUGP("Result for bulk_transfer in sddr09_read_sg %d\n",
result);
- return USB_STOR_TRANSPORT_ERROR;
+ return -EIO;
}
- return USB_STOR_TRANSPORT_GOOD;
+ return 0;
}
#endif
@@ -681,14 +688,13 @@ sddr09_read_status(struct us_data *us, unsigned char *status) {
command[1] = LUNBITS;
result = sddr09_send_scsi_command(us, command, 12);
- if (result != USB_STOR_TRANSPORT_GOOD)
+ if (result)
return result;
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
data, 64, NULL);
*status = data[0];
- return (result == USB_STOR_XFER_GOOD ?
- USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR);
+ return (result == USB_STOR_XFER_GOOD ? 0 : -EIO);
}
static int
@@ -703,6 +709,13 @@ sddr09_read_data(struct us_data *us,
unsigned int len, index, offset;
int result;
+ // Figure out the initial LBA and page
+ lba = address >> info->blockshift;
+ page = (address & info->blockmask);
+ maxlba = info->capacity >> (info->pageshift + info->blockshift);
+ if (lba >= maxlba)
+ return -EIO;
+
// Since we only read in one block at a time, we have to create
// a bounce buffer and move the data a piece at a time between the
// bounce buffer and the actual transfer buffer.
@@ -711,18 +724,13 @@ sddr09_read_data(struct us_data *us,
buffer = kmalloc(len, GFP_NOIO);
if (buffer == NULL) {
printk("sddr09_read_data: Out of memory\n");
- return USB_STOR_TRANSPORT_ERROR;
+ return -ENOMEM;
}
- // Figure out the initial LBA and page
- lba = address >> info->blockshift;
- page = (address & info->blockmask);
- maxlba = info->capacity >> (info->pageshift + info->blockshift);
-
// This could be made much more efficient by checking for
// contiguous LBA's. Another exercise left to the student.
- result = USB_STOR_TRANSPORT_GOOD;
+ result = 0;
index = offset = 0;
while (sectors > 0) {
@@ -735,7 +743,7 @@ sddr09_read_data(struct us_data *us,
if (lba >= maxlba) {
US_DEBUGP("Error: Requested lba %u exceeds "
"maximum %u\n", lba, maxlba);
- result = USB_STOR_TRANSPORT_ERROR;
+ result = -EIO;
break;
}
@@ -749,7 +757,7 @@ sddr09_read_data(struct us_data *us,
/* This is not really an error. It just means
that the block has never been written.
- Instead of returning USB_STOR_TRANSPORT_ERROR
+ Instead of returning an error
it is better to return all zero data. */
memset(buffer, 0, len);
@@ -764,7 +772,7 @@ sddr09_read_data(struct us_data *us,
result = sddr09_read20(us, address>>1,
pages, info->pageshift, buffer, 0);
- if (result != USB_STOR_TRANSPORT_GOOD)
+ if (result)
break;
}
@@ -830,7 +838,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
pba = sddr09_find_unused_pba(info, lba);
if (!pba) {
printk("sddr09_write_lba: Out of unused blocks\n");
- return USB_STOR_TRANSPORT_ERROR;
+ return -ENOSPC;
}
info->pba_to_lba[pba] = lba;
info->lba_to_pba[lba] = pba;
@@ -841,7 +849,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
/* Maybe it is impossible to write to PBA 1.
Fake success, but don't do anything. */
printk("sddr09: avoid writing to pba 1\n");
- return USB_STOR_TRANSPORT_GOOD;
+ return 0;
}
pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT);
@@ -850,7 +858,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
address = (pba << (info->pageshift + info->blockshift));
result = sddr09_read22(us, address>>1, info->blocksize,
info->pageshift, blockbuffer, 0);
- if (result != USB_STOR_TRANSPORT_GOOD)
+ if (result)
return result;
/* check old contents and fill lba */
@@ -897,7 +905,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
{
unsigned char status = 0;
int result2 = sddr09_read_status(us, &status);
- if (result2 != USB_STOR_TRANSPORT_GOOD)
+ if (result2)
US_DEBUGP("sddr09_write_inplace: cannot read status\n");
else if (status != 0xc0)
US_DEBUGP("sddr09_write_inplace: status after write: 0x%x\n",
@@ -920,13 +928,20 @@ sddr09_write_data(struct us_data *us,
unsigned int sectors) {
struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
- unsigned int lba, page, pages;
+ unsigned int lba, maxlba, page, pages;
unsigned int pagelen, blocklen;
unsigned char *blockbuffer;
unsigned char *buffer;
unsigned int len, index, offset;
int result;
+ // Figure out the initial LBA and page
+ lba = address >> info->blockshift;
+ page = (address & info->blockmask);
+ maxlba = info->capacity >> (info->pageshift + info->blockshift);
+ if (lba >= maxlba)
+ return -EIO;
+
// blockbuffer is used for reading in the old data, overwriting
// with the new data, and performing ECC calculations
@@ -938,7 +953,7 @@ sddr09_write_data(struct us_data *us,
blockbuffer = kmalloc(blocklen, GFP_NOIO);
if (!blockbuffer) {
printk("sddr09_write_data: Out of memory\n");
- return USB_STOR_TRANSPORT_ERROR;
+ return -ENOMEM;
}
// Since we don't write the user data directly to the device,
@@ -950,14 +965,10 @@ sddr09_write_data(struct us_data *us,
if (buffer == NULL) {
printk("sddr09_write_data: Out of memory\n");
kfree(blockbuffer);
- return USB_STOR_TRANSPORT_ERROR;
+ return -ENOMEM;
}
- // Figure out the initial LBA and page
- lba = address >> info->blockshift;
- page = (address & info->blockmask);
-
- result = USB_STOR_TRANSPORT_GOOD;
+ result = 0;
index = offset = 0;
while (sectors > 0) {
@@ -967,13 +978,21 @@ sddr09_write_data(struct us_data *us,
pages = min(sectors, info->blocksize - page);
len = (pages << info->pageshift);
+ /* Not overflowing capacity? */
+ if (lba >= maxlba) {
+ US_DEBUGP("Error: Requested lba %u exceeds "
+ "maximum %u\n", lba, maxlba);
+ result = -EIO;
+ break;
+ }
+
// Get the data from the transfer buffer
usb_stor_access_xfer_buf(buffer, len, us->srb,
&index, &offset, FROM_XFER_BUF);
result = sddr09_write_lba(us, lba, page, pages,
buffer, blockbuffer);
- if (result != USB_STOR_TRANSPORT_GOOD)
+ if (result)
break;
page = 0;
@@ -1022,7 +1041,7 @@ sddr09_read_deviceID(struct us_data *us, unsigned char *deviceID) {
command[1] = LUNBITS;
result = sddr09_send_scsi_command(us, command, 12);
- if (result != USB_STOR_TRANSPORT_GOOD)
+ if (result)
return result;
result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
@@ -1031,8 +1050,7 @@ sddr09_read_deviceID(struct us_data *us, unsigned char *deviceID) {
for (i = 0; i < 4; i++)
deviceID[i] = content[i];
- return (result == USB_STOR_XFER_GOOD ?
- USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR);
+ return (result == USB_STOR_XFER_GOOD ? 0 : -EIO);
}
static int
@@ -1041,7 +1059,7 @@ sddr09_get_wp(struct us_data *us, struct sddr09_card_info *info) {
unsigned char status;
result = sddr09_read_status(us, &status);
- if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (result) {
US_DEBUGP("sddr09_get_wp: read_status fails\n");
return result;
}
@@ -1057,7 +1075,7 @@ sddr09_get_wp(struct us_data *us, struct sddr09_card_info *info) {
if (status & 0x1)
US_DEBUGP(" Error");
US_DEBUGP("\n");
- return USB_STOR_TRANSPORT_GOOD;
+ return 0;
}
#if 0
@@ -1089,7 +1107,7 @@ sddr09_get_cardinfo(struct us_data *us, unsigned char flags) {
result = sddr09_read_deviceID(us, deviceID);
- if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (result) {
US_DEBUGP("Result of read_deviceID is %d\n", result);
printk("sddr09: could not read card info\n");
return NULL;
@@ -1200,7 +1218,7 @@ sddr09_read_map(struct us_data *us) {
us, address>>1,
min(alloc_blocks, numblocks - i),
buffer, 0);
- if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (result) {
result = -1;
goto done;
}
@@ -1342,29 +1360,53 @@ sddr09_card_info_destructor(void *extra) {
kfree(info->pba_to_lba);
}
-static void
-sddr09_init_card_info(struct us_data *us) {
- if (!us->extra) {
- us->extra = kmalloc(sizeof(struct sddr09_card_info), GFP_NOIO);
- if (us->extra) {
- memset(us->extra, 0, sizeof(struct sddr09_card_info));
- us->extra_destructor = sddr09_card_info_destructor;
- }
+static int
+sddr09_common_init(struct us_data *us) {
+ int result;
+
+ /* set the configuration -- STALL is an acceptable response here */
+ if (us->pusb_dev->actconfig->desc.bConfigurationValue != 1) {
+ US_DEBUGP("active config #%d != 1 ??\n", us->pusb_dev
+ ->actconfig->desc.bConfigurationValue);
+ return -EINVAL;
+ }
+
+ result = usb_reset_configuration(us->pusb_dev);
+ US_DEBUGP("Result of usb_reset_configuration is %d\n", result);
+ if (result == -EPIPE) {
+ US_DEBUGP("-- stall on control interface\n");
+ } else if (result != 0) {
+ /* it's not a stall, but another error -- time to bail */
+ US_DEBUGP("-- Unknown error. Rejecting device\n");
+ return -EINVAL;
}
+
+ us->extra = kzalloc(sizeof(struct sddr09_card_info), GFP_NOIO);
+ if (!us->extra)
+ return -ENOMEM;
+ us->extra_destructor = sddr09_card_info_destructor;
+
+ nand_init_ecc();
+ return 0;
}
+
/*
* This is needed at a very early stage. If this is not listed in the
* unusual devices list but called from here then LUN 0 of the combo reader
* is not recognized. But I do not know what precisely these calls do.
*/
int
-sddr09_init(struct us_data *us) {
+usb_stor_sddr09_dpcm_init(struct us_data *us) {
int result;
unsigned char *data = us->iobuf;
+ result = sddr09_common_init(us);
+ if (result)
+ return result;
+
result = sddr09_send_command(us, 0x01, USB_DIR_IN, data, 2);
- if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (result) {
US_DEBUGP("sddr09_init: send_command fails\n");
return result;
}
@@ -1373,7 +1415,7 @@ sddr09_init(struct us_data *us) {
// get 07 02
result = sddr09_send_command(us, 0x08, USB_DIR_IN, data, 2);
- if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (result) {
US_DEBUGP("sddr09_init: 2nd send_command fails\n");
return result;
}
@@ -1382,7 +1424,7 @@ sddr09_init(struct us_data *us) {
// get 07 00
result = sddr09_request_sense(us, data, 18);
- if (result == USB_STOR_TRANSPORT_GOOD && data[2] != 0) {
+ if (result == 0 && data[2] != 0) {
int j;
for (j=0; j<18; j++)
printk(" %02X", data[j]);
@@ -1398,7 +1440,7 @@ sddr09_init(struct us_data *us) {
// test unit ready
- return USB_STOR_TRANSPORT_GOOD; /* not result */
+ return 0; /* not result */
}
/*
@@ -1427,13 +1469,6 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us)
};
info = (struct sddr09_card_info *)us->extra;
- if (!info) {
- nand_init_ecc();
- sddr09_init_card_info(us);
- info = (struct sddr09_card_info *)us->extra;
- if (!info)
- return USB_STOR_TRANSPORT_ERROR;
- }
if (srb->cmnd[0] == REQUEST_SENSE && havefakesense) {
/* for a faked command, we have to follow with a faked sense */
@@ -1536,7 +1571,9 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us)
US_DEBUGP("READ_10: read page %d pagect %d\n",
page, pages);
- return sddr09_read_data(us, page, pages);
+ result = sddr09_read_data(us, page, pages);
+ return (result == 0 ? USB_STOR_TRANSPORT_GOOD :
+ USB_STOR_TRANSPORT_ERROR);
}
if (srb->cmnd[0] == WRITE_10) {
@@ -1549,7 +1586,9 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us)
US_DEBUGP("WRITE_10: write page %d pagect %d\n",
page, pages);
- return sddr09_write_data(us, page, pages);
+ result = sddr09_write_data(us, page, pages);
+ return (result == 0 ? USB_STOR_TRANSPORT_GOOD :
+ USB_STOR_TRANSPORT_ERROR);
}
/* catch-all for all other commands, except
@@ -1575,10 +1614,10 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us)
US_DEBUGP("SDDR09: Send control for command %s\n", ptr);
result = sddr09_send_scsi_command(us, srb->cmnd, 12);
- if (result != USB_STOR_TRANSPORT_GOOD) {
+ if (result) {
US_DEBUGP("sddr09_transport: sddr09_send_scsi_command "
"returns %d\n", result);
- return result;
+ return USB_STOR_TRANSPORT_ERROR;
}
if (srb->request_bufflen == 0)
@@ -1606,3 +1645,10 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us)
return USB_STOR_TRANSPORT_GOOD;
}
+/*
+ * Initialization routine for the sddr09 subdriver
+ */
+int
+usb_stor_sddr09_init(struct us_data *us) {
+ return sddr09_common_init(us);
+}
diff --git a/drivers/usb/storage/sddr09.h b/drivers/usb/storage/sddr09.h
index c9d78d6188b1..c03089a9ec38 100644
--- a/drivers/usb/storage/sddr09.h
+++ b/drivers/usb/storage/sddr09.h
@@ -31,18 +31,7 @@
extern int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us);
-struct sddr09_card_info {
- unsigned long capacity; /* Size of card in bytes */
- int pagesize; /* Size of page in bytes */
- int pageshift; /* log2 of pagesize */
- int blocksize; /* Size of block in pages */
- int blockshift; /* log2 of blocksize */
- int blockmask; /* 2^blockshift - 1 */
- int *lba_to_pba; /* logical to physical map */
- int *pba_to_lba; /* physical to logical map */
- int lbact; /* number of available pages */
- int flags;
-#define SDDR09_WP 1 /* write protected */
-};
+extern int usb_stor_sddr09_dpcm_init(struct us_data *us);
+extern int usb_stor_sddr09_init(struct us_data *us);
#endif
diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h
index 0a362cc781ad..633a715850a4 100644
--- a/drivers/usb/storage/transport.h
+++ b/drivers/usb/storage/transport.h
@@ -41,39 +41,8 @@
#ifndef _TRANSPORT_H_
#define _TRANSPORT_H_
-#include <linux/config.h>
#include <linux/blkdev.h>
-/* Protocols */
-
-#define US_PR_CBI 0x00 /* Control/Bulk/Interrupt */
-#define US_PR_CB 0x01 /* Control/Bulk w/o interrupt */
-#define US_PR_BULK 0x50 /* bulk only */
-#ifdef CONFIG_USB_STORAGE_USBAT
-#define US_PR_USBAT 0x80 /* SCM-ATAPI bridge */
-#endif
-#ifdef CONFIG_USB_STORAGE_SDDR09
-#define US_PR_EUSB_SDDR09 0x81 /* SCM-SCSI bridge for SDDR-09 */
-#endif
-#ifdef CONFIG_USB_STORAGE_SDDR55
-#define US_PR_SDDR55 0x82 /* SDDR-55 (made up) */
-#endif
-#define US_PR_DPCM_USB 0xf0 /* Combination CB/SDDR09 */
-
-#ifdef CONFIG_USB_STORAGE_FREECOM
-#define US_PR_FREECOM 0xf1 /* Freecom */
-#endif
-
-#ifdef CONFIG_USB_STORAGE_DATAFAB
-#define US_PR_DATAFAB 0xf2 /* Datafab chipsets */
-#endif
-
-#ifdef CONFIG_USB_STORAGE_JUMPSHOT
-#define US_PR_JUMPSHOT 0xf3 /* Lexar Jumpshot */
-#endif
-
-#define US_PR_DEVICE 0xff /* Use device's value */
-
/*
* Bulk only data structures
*/
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index f5f47a34b168..dc301e567cfc 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -79,13 +79,6 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001,
US_SC_8070, US_PR_USBAT, init_usbat, 0),
#endif
-/* Patch submitted by Mihnea-Costin Grigore <mihnea@zulu.ro> */
-UNUSUAL_DEV( 0x040d, 0x6205, 0x0003, 0x0003,
- "VIA Technologies Inc.",
- "USB 2.0 Card Reader",
- US_SC_DEVICE, US_PR_DEVICE, NULL,
- US_FL_IGNORE_RESIDUE ),
-
/* Reported by Sebastian Kapfer <sebastian_kapfer@gmx.net>
* and Olaf Hering <olh@suse.de> (different bcd's, same vendor/product)
* for USB floppies that need the SINGLE_LUN enforcement.
@@ -96,6 +89,13 @@ UNUSUAL_DEV( 0x0409, 0x0040, 0x0000, 0x9999,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_SINGLE_LUN ),
+/* Patch submitted by Mihnea-Costin Grigore <mihnea@zulu.ro> */
+UNUSUAL_DEV( 0x040d, 0x6205, 0x0003, 0x0003,
+ "VIA Technologies Inc.",
+ "USB 2.0 Card Reader",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
/* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
* Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message
* always fails and confuses drive.
@@ -187,6 +187,14 @@ UNUSUAL_DEV( 0x04b0, 0x0405, 0x0100, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY),
+/* Patch for Nikon coolpix 2000
+ * Submitted by Fabien Cosse <fabien.cosse@wanadoo.fr>*/
+UNUSUAL_DEV( 0x04b0, 0x0301, 0x0010, 0x0010,
+ "NIKON",
+ "NIKON DSC E2000",
+ US_SC_DEVICE, US_PR_DEVICE,NULL,
+ US_FL_NOT_LOCKABLE ),
+
/* BENQ DC5330
* Reported by Manuel Fombuena <mfombuena@ya.com> and
* Frank Copeland <fjc@thingy.apana.org.au> */
@@ -276,14 +284,14 @@ UNUSUAL_DEV( 0x04e6, 0x0002, 0x0100, 0x0100,
UNUSUAL_DEV( 0x04e6, 0x0003, 0x0000, 0x9999,
"Sandisk",
"ImageMate SDDR09",
- US_SC_SCSI, US_PR_EUSB_SDDR09, NULL,
- US_FL_SINGLE_LUN ),
+ US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init,
+ 0),
/* This entry is from Andries.Brouwer@cwi.nl */
UNUSUAL_DEV( 0x04e6, 0x0005, 0x0100, 0x0208,
"SCM Microsystems",
"eUSB SmartMedia / CompactFlash Adapter",
- US_SC_SCSI, US_PR_DPCM_USB, sddr09_init,
+ US_SC_SCSI, US_PR_DPCM_USB, usb_stor_sddr09_dpcm_init,
0),
#endif
@@ -527,6 +535,13 @@ UNUSUAL_DEV( 0x057b, 0x0022, 0x0000, 0x9999,
"Silicon Media R/W",
US_SC_DEVICE, US_PR_DEVICE, NULL, 0),
+#ifdef CONFIG_USB_STORAGE_ALAUDA
+UNUSUAL_DEV( 0x0584, 0x0008, 0x0102, 0x0102,
+ "Fujifilm",
+ "DPC-R1 (Alauda)",
+ US_SC_SCSI, US_PR_ALAUDA, init_alauda, 0 ),
+#endif
+
/* Fabrizio Fellini <fello@libero.it> */
UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210,
"Fujifilm",
@@ -673,8 +688,8 @@ UNUSUAL_DEV( 0x0644, 0x0000, 0x0100, 0x0100,
UNUSUAL_DEV( 0x066b, 0x0105, 0x0100, 0x0100,
"Olympus",
"Camedia MAUSB-2",
- US_SC_SCSI, US_PR_EUSB_SDDR09, NULL,
- US_FL_SINGLE_LUN ),
+ US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init,
+ 0),
#endif
/* Reported by Darsen Lu <darsen@micro.ee.nthu.edu.tw> */
@@ -739,8 +754,8 @@ UNUSUAL_DEV( 0x0781, 0x0100, 0x0100, 0x0100,
UNUSUAL_DEV( 0x0781, 0x0200, 0x0000, 0x9999,
"Sandisk",
"ImageMate SDDR-09",
- US_SC_SCSI, US_PR_EUSB_SDDR09, NULL,
- US_FL_SINGLE_LUN ),
+ US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init,
+ 0),
#endif
#ifdef CONFIG_USB_STORAGE_FREECOM
@@ -776,6 +791,13 @@ UNUSUAL_DEV( 0x07af, 0x0006, 0x0100, 0x0100,
US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ),
#endif
+#ifdef CONFIG_USB_STORAGE_ALAUDA
+UNUSUAL_DEV( 0x07b4, 0x010a, 0x0102, 0x0102,
+ "Olympus",
+ "MAUSB-10 (Alauda)",
+ US_SC_SCSI, US_PR_ALAUDA, init_alauda, 0 ),
+#endif
+
#ifdef CONFIG_USB_STORAGE_DATAFAB
UNUSUAL_DEV( 0x07c4, 0xa000, 0x0000, 0x0015,
"Datafab",
@@ -1134,3 +1156,27 @@ UNUSUAL_DEV( 0x55aa, 0xa103, 0x0000, 0x9999,
US_SC_SCSI, US_PR_SDDR55, NULL,
US_FL_SINGLE_LUN),
#endif
+
+/* Control/Bulk transport for all SubClass values */
+USUAL_DEV(US_SC_RBC, US_PR_CB, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_8020, US_PR_CB, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_QIC, US_PR_CB, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_UFI, US_PR_CB, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_8070, US_PR_CB, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_SCSI, US_PR_CB, USB_US_TYPE_STOR),
+
+/* Control/Bulk/Interrupt transport for all SubClass values */
+USUAL_DEV(US_SC_RBC, US_PR_CBI, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_8020, US_PR_CBI, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_QIC, US_PR_CBI, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_UFI, US_PR_CBI, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_8070, US_PR_CBI, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_SCSI, US_PR_CBI, USB_US_TYPE_STOR),
+
+/* Bulk-only transport for all SubClass values */
+USUAL_DEV(US_SC_RBC, US_PR_BULK, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_8020, US_PR_BULK, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_QIC, US_PR_BULK, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_UFI, US_PR_BULK, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_8070, US_PR_BULK, USB_US_TYPE_STOR),
+USUAL_DEV(US_SC_SCSI, US_PR_BULK, 0),
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 3847ebed2aa4..dbcf23980ff1 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -94,6 +94,9 @@
#ifdef CONFIG_USB_STORAGE_ONETOUCH
#include "onetouch.h"
#endif
+#ifdef CONFIG_USB_STORAGE_ALAUDA
+#include "alauda.h"
+#endif
/* Some informational data */
MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
@@ -112,49 +115,33 @@ static atomic_t total_threads = ATOMIC_INIT(0);
static DECLARE_COMPLETION(threads_gone);
-/* The entries in this table, except for final ones here
- * (USB_MASS_STORAGE_CLASS and the empty entry), correspond,
- * line for line with the entries of us_unsuaul_dev_list[].
+/*
+ * The entries in this table correspond, line for line,
+ * with the entries of us_unusual_dev_list[].
*/
+#ifndef CONFIG_USB_LIBUSUAL
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
vendorName, productName,useProtocol, useTransport, \
initFunction, flags) \
-{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax) }
+{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax), \
+ .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+
+#define USUAL_DEV(useProto, useTrans, useType) \
+{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \
+ .driver_info = (USB_US_TYPE_STOR<<24) }
static struct usb_device_id storage_usb_ids [] = {
# include "unusual_devs.h"
#undef UNUSUAL_DEV
- /* Control/Bulk transport for all SubClass values */
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CB) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CB) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CB) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CB) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CB) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CB) },
-
- /* Control/Bulk/Interrupt transport for all SubClass values */
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CBI) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CBI) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CBI) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CBI) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CBI) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CBI) },
-
- /* Bulk-only transport for all SubClass values */
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_BULK) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_BULK) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_BULK) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_BULK) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_BULK) },
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) },
-
+#undef USUAL_DEV
/* Terminating entry */
{ }
};
MODULE_DEVICE_TABLE (usb, storage_usb_ids);
+#endif /* CONFIG_USB_LIBUSUAL */
/* This is the list of devices we recognize, along with their flag data */
@@ -167,7 +154,6 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids);
* are free to use as many characters as you like.
*/
-#undef UNUSUAL_DEV
#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
vendor_name, product_name, use_protocol, use_transport, \
init_function, Flags) \
@@ -177,53 +163,18 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids);
.useProtocol = use_protocol, \
.useTransport = use_transport, \
.initFunction = init_function, \
- .flags = Flags, \
+}
+
+#define USUAL_DEV(use_protocol, use_transport, use_type) \
+{ \
+ .useProtocol = use_protocol, \
+ .useTransport = use_transport, \
}
static struct us_unusual_dev us_unusual_dev_list[] = {
# include "unusual_devs.h"
# undef UNUSUAL_DEV
- /* Control/Bulk transport for all SubClass values */
- { .useProtocol = US_SC_RBC,
- .useTransport = US_PR_CB},
- { .useProtocol = US_SC_8020,
- .useTransport = US_PR_CB},
- { .useProtocol = US_SC_QIC,
- .useTransport = US_PR_CB},
- { .useProtocol = US_SC_UFI,
- .useTransport = US_PR_CB},
- { .useProtocol = US_SC_8070,
- .useTransport = US_PR_CB},
- { .useProtocol = US_SC_SCSI,
- .useTransport = US_PR_CB},
-
- /* Control/Bulk/Interrupt transport for all SubClass values */
- { .useProtocol = US_SC_RBC,
- .useTransport = US_PR_CBI},
- { .useProtocol = US_SC_8020,
- .useTransport = US_PR_CBI},
- { .useProtocol = US_SC_QIC,
- .useTransport = US_PR_CBI},
- { .useProtocol = US_SC_UFI,
- .useTransport = US_PR_CBI},
- { .useProtocol = US_SC_8070,
- .useTransport = US_PR_CBI},
- { .useProtocol = US_SC_SCSI,
- .useTransport = US_PR_CBI},
-
- /* Bulk-only transport for all SubClass values */
- { .useProtocol = US_SC_RBC,
- .useTransport = US_PR_BULK},
- { .useProtocol = US_SC_8020,
- .useTransport = US_PR_BULK},
- { .useProtocol = US_SC_QIC,
- .useTransport = US_PR_BULK},
- { .useProtocol = US_SC_UFI,
- .useTransport = US_PR_BULK},
- { .useProtocol = US_SC_8070,
- .useTransport = US_PR_BULK},
- { .useProtocol = US_SC_SCSI,
- .useTransport = US_PR_BULK},
+# undef USUAL_DEV
/* Terminating entry */
{ NULL }
@@ -240,6 +191,8 @@ static int storage_suspend(struct usb_interface *iface, pm_message_t message)
down(&us->dev_semaphore);
US_DEBUGP("%s\n", __FUNCTION__);
+ if (us->suspend_resume_hook)
+ (us->suspend_resume_hook)(us, US_SUSPEND);
iface->dev.power.power_state.event = message.event;
/* When runtime PM is working, we'll set a flag to indicate
@@ -256,6 +209,8 @@ static int storage_resume(struct usb_interface *iface)
down(&us->dev_semaphore);
US_DEBUGP("%s\n", __FUNCTION__);
+ if (us->suspend_resume_hook)
+ (us->suspend_resume_hook)(us, US_RESUME);
iface->dev.power.power_state.event = PM_EVENT_ON;
up(&us->dev_semaphore);
@@ -484,14 +439,20 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf)
return 0;
}
+/* Find an unusual_dev descriptor (always succeeds in the current code) */
+static struct us_unusual_dev *find_unusual(const struct usb_device_id *id)
+{
+ const int id_index = id - storage_usb_ids;
+ return &us_unusual_dev_list[id_index];
+}
+
/* Get the unusual_devs entries and the string descriptors */
-static void get_device_info(struct us_data *us, int id_index)
+static void get_device_info(struct us_data *us, const struct usb_device_id *id)
{
struct usb_device *dev = us->pusb_dev;
struct usb_interface_descriptor *idesc =
&us->pusb_intf->cur_altsetting->desc;
- struct us_unusual_dev *unusual_dev = &us_unusual_dev_list[id_index];
- struct usb_device_id *id = &storage_usb_ids[id_index];
+ struct us_unusual_dev *unusual_dev = find_unusual(id);
/* Store the entries */
us->unusual_dev = unusual_dev;
@@ -501,7 +462,7 @@ static void get_device_info(struct us_data *us, int id_index)
us->protocol = (unusual_dev->useTransport == US_PR_DEVICE) ?
idesc->bInterfaceProtocol :
unusual_dev->useTransport;
- us->flags = unusual_dev->flags;
+ us->flags = USB_US_ORIG_FLAGS(id->driver_info);
/*
* This flag is only needed when we're in high-speed, so let's
@@ -516,7 +477,7 @@ static void get_device_info(struct us_data *us, int id_index)
* from the unusual_devs.h table.
*/
if (id->idVendor || id->idProduct) {
- static char *msgs[3] = {
+ static const char *msgs[3] = {
"an unneeded SubClass entry",
"an unneeded Protocol entry",
"unneeded SubClass and Protocol entries"};
@@ -529,7 +490,7 @@ static void get_device_info(struct us_data *us, int id_index)
if (unusual_dev->useTransport != US_PR_DEVICE &&
us->protocol == idesc->bInterfaceProtocol)
msg += 2;
- if (msg >= 0 && !(unusual_dev->flags & US_FL_NEED_OVERRIDE))
+ if (msg >= 0 && !(us->flags & US_FL_NEED_OVERRIDE))
printk(KERN_NOTICE USB_STORAGE "This device "
"(%04x,%04x,%04x S %02x P %02x)"
" has %s in unusual_devs.h\n"
@@ -686,6 +647,15 @@ static int get_protocol(struct us_data *us)
break;
#endif
+#ifdef CONFIG_USB_STORAGE_ALAUDA
+ case US_PR_ALAUDA:
+ us->transport_name = "Alauda Control/Bulk";
+ us->transport = alauda_transport;
+ us->transport_reset = usb_stor_Bulk_reset;
+ us->max_lun = 1;
+ break;
+#endif
+
default:
return -EIO;
}
@@ -921,10 +891,12 @@ static int storage_probe(struct usb_interface *intf,
{
struct Scsi_Host *host;
struct us_data *us;
- const int id_index = id - storage_usb_ids;
int result;
struct task_struct *th;
+ if (usb_usual_check_type(id, USB_US_TYPE_STOR))
+ return -ENXIO;
+
US_DEBUGP("USB Mass Storage device detected\n");
/*
@@ -957,29 +929,7 @@ static int storage_probe(struct usb_interface *intf,
* of the match from the usb_device_id table, so we can find the
* corresponding entry in the private table.
*/
- get_device_info(us, id_index);
-
-#ifdef CONFIG_USB_STORAGE_SDDR09
- if (us->protocol == US_PR_EUSB_SDDR09 ||
- us->protocol == US_PR_DPCM_USB) {
- /* set the configuration -- STALL is an acceptable response here */
- if (us->pusb_dev->actconfig->desc.bConfigurationValue != 1) {
- US_DEBUGP("active config #%d != 1 ??\n", us->pusb_dev
- ->actconfig->desc.bConfigurationValue);
- goto BadDevice;
- }
- result = usb_reset_configuration(us->pusb_dev);
-
- US_DEBUGP("Result of usb_reset_configuration is %d\n", result);
- if (result == -EPIPE) {
- US_DEBUGP("-- stall on control interface\n");
- } else if (result != 0) {
- /* it's not a stall, but another error -- time to bail */
- US_DEBUGP("-- Unknown error. Rejecting device\n");
- goto BadDevice;
- }
- }
-#endif
+ get_device_info(us, id);
/* Get the transport, protocol, and pipe settings */
result = get_transport(us);
@@ -1044,7 +994,6 @@ static void storage_disconnect(struct usb_interface *intf)
***********************************************************************/
static struct usb_driver usb_storage_driver = {
- .owner = THIS_MODULE,
.name = "usb-storage",
.probe = storage_probe,
.disconnect = storage_disconnect,
@@ -1062,9 +1011,10 @@ static int __init usb_stor_init(void)
/* register the driver, return usb_register return code if error */
retval = usb_register(&usb_storage_driver);
- if (retval == 0)
+ if (retval == 0) {
printk(KERN_INFO "USB Mass Storage support registered.\n");
-
+ usb_usual_set_present(USB_US_TYPE_STOR);
+ }
return retval;
}
@@ -1088,6 +1038,8 @@ static void __exit usb_stor_exit(void)
wait_for_completion(&threads_gone);
atomic_dec(&total_threads);
}
+
+ usb_usual_clear_present(USB_US_TYPE_STOR);
}
module_init(usb_stor_init);
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index 98b09711a739..7259fd1f6b0d 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -45,6 +45,7 @@
#define _USB_H_
#include <linux/usb.h>
+#include <linux/usb_usual.h>
#include <linux/blkdev.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
@@ -63,38 +64,8 @@ struct us_unusual_dev {
__u8 useProtocol;
__u8 useTransport;
int (*initFunction)(struct us_data *);
- unsigned int flags;
};
-/*
- * Static flag definitions. We use this roundabout technique so that the
- * proc_info() routine can automatically display a message for each flag.
- */
-#define US_DO_ALL_FLAGS \
- US_FLAG(SINGLE_LUN, 0x00000001) \
- /* allow access to only LUN 0 */ \
- US_FLAG(NEED_OVERRIDE, 0x00000002) \
- /* unusual_devs entry is necessary */ \
- US_FLAG(SCM_MULT_TARG, 0x00000004) \
- /* supports multiple targets */ \
- US_FLAG(FIX_INQUIRY, 0x00000008) \
- /* INQUIRY response needs faking */ \
- US_FLAG(FIX_CAPACITY, 0x00000010) \
- /* READ CAPACITY response too big */ \
- US_FLAG(IGNORE_RESIDUE, 0x00000020) \
- /* reported residue is wrong */ \
- US_FLAG(BULK32, 0x00000040) \
- /* Uses 32-byte CBW length */ \
- US_FLAG(NOT_LOCKABLE, 0x00000080) \
- /* PREVENT/ALLOW not supported */ \
- US_FLAG(GO_SLOW, 0x00000100) \
- /* Need delay after Command phase */ \
- US_FLAG(NO_WP_DETECT, 0x00000200) \
- /* Don't check for write-protect */ \
-
-#define US_FLAG(name, value) US_FL_##name = value ,
-enum { US_DO_ALL_FLAGS };
-#undef US_FLAG
/* Dynamic flag definitions: used in set_bit() etc. */
#define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */
@@ -122,7 +93,11 @@ enum { US_DO_ALL_FLAGS };
typedef int (*trans_cmnd)(struct scsi_cmnd *, struct us_data*);
typedef int (*trans_reset)(struct us_data*);
typedef void (*proto_cmnd)(struct scsi_cmnd*, struct us_data*);
-typedef void (*extra_data_destructor)(void *); /* extra data destructor */
+typedef void (*extra_data_destructor)(void *); /* extra data destructor */
+typedef void (*pm_hook)(struct us_data *, int); /* power management hook */
+
+#define US_SUSPEND 0
+#define US_RESUME 1
/* we allocate one of these for every device that we remember */
struct us_data {
@@ -178,6 +153,9 @@ struct us_data {
/* subdriver information */
void *extra; /* Any extra data */
extra_data_destructor extra_destructor;/* extra data destructor */
+#ifdef CONFIG_PM
+ pm_hook suspend_resume_hook;
+#endif
};
/* Convert between us_data and the corresponding Scsi_Host */
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index 6c3a53f8f26c..5d02f16b7d0e 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -39,10 +39,15 @@ MODULE_DEVICE_TABLE (usb, skel_table);
/* Get a minor range for your devices from the usb maintainer */
#define USB_SKEL_MINOR_BASE 192
+/* our private defines. if this grows any larger, use your own .h file */
+#define MAX_TRANSFER ( PAGE_SIZE - 512 )
+#define WRITES_IN_FLIGHT 8
+
/* Structure to hold all of our device specific stuff */
struct usb_skel {
struct usb_device * udev; /* the usb device for this device */
struct usb_interface * interface; /* the interface for this device */
+ struct semaphore limit_sem; /* limiting the number of writes in progress */
unsigned char * bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the size of the receive buffer */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
@@ -152,6 +157,7 @@ static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
/* free up our allocated buffer */
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
+ up(&dev->limit_sem);
}
static ssize_t skel_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos)
@@ -160,6 +166,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
int retval = 0;
struct urb *urb = NULL;
char *buf = NULL;
+ size_t writesize = min(count, (size_t)MAX_TRANSFER);
dev = (struct usb_skel *)file->private_data;
@@ -167,6 +174,12 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
if (count == 0)
goto exit;
+ /* limit the number of URBs in flight to stop a user from using up all RAM */
+ if (down_interruptible(&dev->limit_sem)) {
+ retval = -ERESTARTSYS;
+ goto exit;
+ }
+
/* create a urb, and a buffer for it, and copy the data to the urb */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
@@ -174,13 +187,13 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
goto error;
}
- buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
+ buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}
- if (copy_from_user(buf, user_buffer, count)) {
+ if (copy_from_user(buf, user_buffer, writesize)) {
retval = -EFAULT;
goto error;
}
@@ -188,7 +201,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
/* initialize the urb properly */
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
- buf, count, skel_write_bulk_callback, dev);
+ buf, writesize, skel_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* send the data out the bulk port */
@@ -202,11 +215,12 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
usb_free_urb(urb);
exit:
- return count;
+ return writesize;
error:
- usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
+ usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
usb_free_urb(urb);
+ up(&dev->limit_sem);
return retval;
}
@@ -238,13 +252,13 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
int retval = -ENOMEM;
/* allocate memory for our device state and initialize it */
- dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
err("Out of memory");
goto error;
}
- memset(dev, 0x00, sizeof(*dev));
kref_init(&dev->kref);
+ sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
@@ -330,7 +344,6 @@ static void skel_disconnect(struct usb_interface *interface)
}
static struct usb_driver skel_driver = {
- .owner = THIS_MODULE,
.name = "skeleton",
.probe = skel_probe,
.disconnect = skel_disconnect,
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3e470c8b4193..cc8e3bf5001b 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -536,13 +536,13 @@ config FB_SUN3
config FB_SBUS
bool "SBUS and UPA framebuffers"
- depends on (FB = y) && (SPARC32 || SPARC64)
+ depends on (FB = y) && SPARC
help
Say Y if you want support for SBUS or UPA based frame buffer device.
config FB_BW2
bool "BWtwo support"
- depends on (FB = y) && ((SPARC32 || SPARC64) && FB_SBUS || (SUN3 || SUN3X) && FB_SUN3)
+ depends on (FB = y) && (SPARC && FB_SBUS || (SUN3 || SUN3X) && FB_SUN3)
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -551,7 +551,7 @@ config FB_BW2
config FB_CG3
bool "CGthree support"
- depends on (FB = y) && ((SPARC32 || SPARC64) && FB_SBUS || (SUN3 || SUN3X) && FB_SUN3)
+ depends on (FB = y) && (SPARC && FB_SBUS || (SUN3 || SUN3X) && FB_SUN3)
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -560,7 +560,7 @@ config FB_CG3
config FB_CG6
bool "CGsix (GX,TurboGX) support"
- depends on (FB = y) && ((SPARC32 || SPARC64) && FB_SBUS || (SUN3 || SUN3X) && FB_SUN3)
+ depends on (FB = y) && (SPARC && FB_SBUS || (SUN3 || SUN3X) && FB_SUN3)
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
help
@@ -1268,7 +1268,7 @@ config FB_LEO
config FB_PCI
bool "PCI framebuffers"
- depends on (FB = y) && PCI && (SPARC64 || SPARC32)
+ depends on (FB = y) && PCI && SPARC
config FB_IGA
bool "IGA 168x display support"
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 5f74df993406..a5d09e159cd1 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -6,7 +6,7 @@ menu "Console display driver support"
config VGA_CONSOLE
bool "VGA text console" if EMBEDDED || !X86
- depends on !ARCH_ACORN && !ARCH_EBSA110 && !4xx && !8xx && !SPARC32 && !SPARC64 && !M68K && !PARISC && !ARCH_VERSATILE
+ depends on !ARCH_ACORN && !ARCH_EBSA110 && !4xx && !8xx && !SPARC && !M68K && !PARISC && !ARCH_VERSATILE
default y
help
Saying Y here will allow you to use Linux in text mode through a
@@ -68,7 +68,7 @@ config SGI_NEWPORT_CONSOLE
config PROM_CONSOLE
bool "PROM console"
- depends on SPARC32 || SPARC64
+ depends on SPARC
help
Say Y to build a console driver for Sun machines that uses the
terminal emulation built into their console PROMS.
@@ -136,7 +136,7 @@ config FONTS
config FONT_8x8
bool "VGA 8x8 font" if FONTS
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
- default y if !SPARC32 && !SPARC64 && !FONTS
+ default y if !SPARC && !FONTS
help
This is the "high resolution" font for the VGA frame buffer (the one
provided by the text console 80x50 (and higher) modes).
@@ -150,7 +150,7 @@ config FONT_8x8
config FONT_8x16
bool "VGA 8x16 font" if FONTS
depends on FRAMEBUFFER_CONSOLE || SGI_NEWPORT_CONSOLE=y || STI_CONSOLE || USB_SISUSBVGA_CON
- default y if !SPARC32 && !SPARC64 && !FONTS
+ default y if !SPARC && !FONTS
help
This is the "high resolution" font for the VGA frame buffer (the one
provided by the VGA text console 80x25 mode.
@@ -160,7 +160,7 @@ config FONT_8x16
config FONT_6x11
bool "Mac console 6x11 font (not supported by all drivers)" if FONTS
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
- default y if !SPARC32 && !SPARC64 && !FONTS && MAC
+ default y if !SPARC && !FONTS && MAC
help
Small console font with Macintosh-style high-half glyphs. Some Mac
framebuffer drivers don't support this one at all.
@@ -176,7 +176,7 @@ config FONT_7x14
config FONT_PEARL_8x8
bool "Pearl (old m68k) console 8x8 font" if FONTS
depends on FRAMEBUFFER_CONSOLE
- default y if !SPARC32 && !SPARC64 && !FONTS && AMIGA
+ default y if !SPARC && !FONTS && AMIGA
help
Small console font with PC-style control-character and high-half
glyphs.
@@ -184,24 +184,24 @@ config FONT_PEARL_8x8
config FONT_ACORN_8x8
bool "Acorn console 8x8 font" if FONTS
depends on FRAMEBUFFER_CONSOLE
- default y if !SPARC32 && !SPARC64 && !FONTS && ARM && ARCH_ACORN
+ default y if !SPARC && !FONTS && ARM && ARCH_ACORN
help
Small console font with PC-style control characters and high-half
glyphs.
config FONT_MINI_4x6
bool "Mini 4x6 font"
- depends on !SPARC32 && !SPARC64 && FONTS
+ depends on !SPARC && FONTS
config FONT_SUN8x16
bool "Sparc console 8x16 font"
- depends on FRAMEBUFFER_CONSOLE && (!SPARC32 && !SPARC64 && FONTS || SPARC32 || SPARC64)
+ depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC)
help
This is the high resolution console font for Sun machines. Say Y.
config FONT_SUN12x22
bool "Sparc console 12x22 font (not supported by all drivers)"
- depends on FRAMEBUFFER_CONSOLE && (!SPARC32 && !SPARC64 && FONTS || SPARC32 || SPARC64)
+ depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC)
help
This is the high resolution console font for Sun machines with very
big letters (like the letters used in the SPARC PROM). If the
diff --git a/drivers/video/console/fbcon_ud.c b/drivers/video/console/fbcon_ud.c
index c4d7c89212b4..9dd059e8b645 100644
--- a/drivers/video/console/fbcon_ud.c
+++ b/drivers/video/console/fbcon_ud.c
@@ -420,13 +420,15 @@ static void ud_cursor(struct vc_data *vc, struct fb_info *info,
int ud_update_start(struct fb_info *info)
{
struct fbcon_ops *ops = info->fbcon_par;
- u32 xoffset, yoffset;
+ int xoffset, yoffset;
u32 vyres = GETVYRES(ops->p->scrollmode, info);
u32 vxres = GETVXRES(ops->p->scrollmode, info);
int err;
- xoffset = (vxres - info->var.xres) - ops->var.xoffset;
- yoffset = (vyres - info->var.yres) - ops->var.yoffset;
+ xoffset = vxres - info->var.xres - ops->var.xoffset;
+ yoffset = vyres - info->var.yres - ops->var.yoffset;
+ if (yoffset < 0)
+ yoffset += vyres;
ops->var.xoffset = xoffset;
ops->var.yoffset = yoffset;
err = fb_pan_display(info, &ops->var);
diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h
index f077ca34faba..da29d007f215 100644
--- a/drivers/video/intelfb/intelfb.h
+++ b/drivers/video/intelfb/intelfb.h
@@ -41,6 +41,10 @@
/*** hw-related values ***/
+/* Resource Allocation */
+#define INTELFB_FB_ACQUIRED 1
+#define INTELFB_MMIO_ACQUIRED 2
+
/* PCI ids for supported devices */
#define PCI_DEVICE_ID_INTEL_830M 0x3577
#define PCI_DEVICE_ID_INTEL_845G 0x2562
@@ -257,6 +261,7 @@ struct intelfb_info {
int hwcursor;
int fixed_mode;
int ring_active;
+ int flag;
/* hw cursor */
int cursor_on;
diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c
index 427689e584da..0090544842f5 100644
--- a/drivers/video/intelfb/intelfbdrv.c
+++ b/drivers/video/intelfb/intelfbdrv.c
@@ -135,9 +135,6 @@
static void __devinit get_initial_mode(struct intelfb_info *dinfo);
static void update_dinfo(struct intelfb_info *dinfo,
struct fb_var_screeninfo *var);
-static int intelfb_get_fix(struct fb_fix_screeninfo *fix,
- struct fb_info *info);
-
static int intelfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info);
static int intelfb_set_par(struct fb_info *info);
@@ -473,9 +470,9 @@ cleanup(struct intelfb_info *dinfo)
if (dinfo->aperture.virtual)
iounmap((void __iomem *)dinfo->aperture.virtual);
- if (dinfo->mmio_base_phys)
+ if (dinfo->flag & INTELFB_MMIO_ACQUIRED)
release_mem_region(dinfo->mmio_base_phys, INTEL_REG_SIZE);
- if (dinfo->aperture.physical)
+ if (dinfo->flag & INTELFB_FB_ACQUIRED)
release_mem_region(dinfo->aperture.physical,
dinfo->aperture.size);
framebuffer_release(dinfo->info);
@@ -572,6 +569,9 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
cleanup(dinfo);
return -ENODEV;
}
+
+ dinfo->flag |= INTELFB_FB_ACQUIRED;
+
if (!request_mem_region(dinfo->mmio_base_phys,
INTEL_REG_SIZE,
INTELFB_MODULE_NAME)) {
@@ -580,6 +580,8 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
return -ENODEV;
}
+ dinfo->flag |= INTELFB_MMIO_ACQUIRED;
+
/* Get the chipset info. */
dinfo->pci_chipset = pdev->device;
@@ -1091,7 +1093,17 @@ intelfb_set_fbinfo(struct intelfb_info *dinfo)
return 1;
info->pixmap.scan_align = 1;
-
+ strcpy(info->fix.id, dinfo->name);
+ info->fix.smem_start = dinfo->fb.physical;
+ info->fix.smem_len = dinfo->fb.size;
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.type_aux = 0;
+ info->fix.xpanstep = 8;
+ info->fix.ypanstep = 1;
+ info->fix.ywrapstep = 0;
+ info->fix.mmio_start = dinfo->mmio_base_phys;
+ info->fix.mmio_len = INTEL_REG_SIZE;
+ info->fix.accel = FB_ACCEL_I830;
update_dinfo(dinfo, &info->var);
return 0;
@@ -1109,7 +1121,8 @@ update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var)
dinfo->yres = var->xres;
dinfo->pixclock = var->pixclock;
- intelfb_get_fix(&dinfo->info->fix, dinfo->info);
+ dinfo->info->fix.visual = dinfo->visual;
+ dinfo->info->fix.line_length = dinfo->pitch;
switch (dinfo->bpp) {
case 8:
@@ -1139,30 +1152,6 @@ update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var)
/* fbops functions */
-static int
-intelfb_get_fix(struct fb_fix_screeninfo *fix, struct fb_info *info)
-{
- struct intelfb_info *dinfo = GET_DINFO(info);
-
- DBG_MSG("intelfb_get_fix\n");
-
- memset(fix, 0, sizeof(*fix));
- strcpy(fix->id, dinfo->name);
- fix->smem_start = dinfo->fb.physical;
- fix->smem_len = dinfo->fb.size;
- fix->type = FB_TYPE_PACKED_PIXELS;
- fix->type_aux = 0;
- fix->visual = dinfo->visual;
- fix->xpanstep = 8;
- fix->ypanstep = 1;
- fix->ywrapstep = 0;
- fix->line_length = dinfo->pitch;
- fix->mmio_start = dinfo->mmio_base_phys;
- fix->mmio_len = INTEL_REG_SIZE;
- fix->accel = FB_ACCEL_I830;
- return 0;
-}
-
/***************************************************************
* fbdev interface *
***************************************************************/
diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig
index 8cb7fb4db441..f0e6512c87ff 100644
--- a/drivers/video/logo/Kconfig
+++ b/drivers/video/logo/Kconfig
@@ -47,7 +47,7 @@ config LOGO_SGI_CLUT224
config LOGO_SUN_CLUT224
bool "224-color Sun Linux logo"
- depends on LOGO && (SPARC32 || SPARC64)
+ depends on LOGO && SPARC
default y
config LOGO_SUPERH_MONO
diff --git a/drivers/video/sbuslib.c b/drivers/video/sbuslib.c
index 646c43f921c5..3a74a63dd4f2 100644
--- a/drivers/video/sbuslib.c
+++ b/drivers/video/sbuslib.c
@@ -46,6 +46,9 @@ int sbusfb_mmap_helper(struct sbus_mmap_map *map,
unsigned long off;
int i;
+ if (!(vma->vm_flags & (VM_SHARED | VM_MAYSHARE)))
+ return -EINVAL;
+
size = vma->vm_end - vma->vm_start;
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
diff --git a/drivers/w1/dscore.c b/drivers/w1/dscore.c
index 15fb250451e5..b9146306df49 100644
--- a/drivers/w1/dscore.c
+++ b/drivers/w1/dscore.c
@@ -52,7 +52,6 @@ static int ds_send_control_cmd(struct ds_device *, u16, u16);
static struct usb_driver ds_driver = {
- .owner = THIS_MODULE,
.name = "DS9490R",
.probe = ds_probe,
.disconnect = ds_disconnect,
diff --git a/fs/9p/trans_sock.c b/fs/9p/trans_sock.c
index a93c2bf94c33..6a9a75d40f73 100644
--- a/fs/9p/trans_sock.c
+++ b/fs/9p/trans_sock.c
@@ -26,6 +26,7 @@
*/
#include <linux/config.h>
+#include <linux/in.h>
#include <linux/module.h>
#include <linux/net.h>
#include <linux/ipv6.h>
diff --git a/fs/compat.c b/fs/compat.c
index 818634120b69..55ac0324aaf1 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1170,7 +1170,7 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
}
ret = rw_verify_area(type, file, pos, tot_len);
- if (ret)
+ if (ret < 0)
goto out;
fnv = NULL;
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 4684eb7d48c6..b3ad0bd0312f 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -501,11 +501,16 @@ int hostfs_commit_write(struct file *file, struct page *page, unsigned from,
long long start;
int err = 0;
- start = (long long) (page->index << PAGE_CACHE_SHIFT) + from;
+ start = (((long long) page->index) << PAGE_CACHE_SHIFT) + from;
buffer = kmap(page);
err = write_file(FILE_HOSTFS_I(file)->fd, &start, buffer + from,
to - from);
if(err > 0) err = 0;
+
+ /* Actually, if !err, write_file has added to-from to start, so, despite
+ * the appearance, we are comparing i_size against the _last_ written
+ * location, as we should. */
+
if(!err && (start > inode->i_size))
inode->i_size = start;
@@ -910,10 +915,8 @@ static struct inode_operations hostfs_dir_iops = {
int hostfs_link_readpage(struct file *file, struct page *page)
{
char *buffer, *name;
- long long start;
int err;
- start = page->index << PAGE_CACHE_SHIFT;
buffer = kmap(page);
name = inode_name(page->mapping->host, 0);
if(name == NULL) return(-ENOMEM);
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index 006bb9e14579..3eaf6e701087 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -157,6 +157,8 @@ void nlmclnt_mark_reclaim(struct nlm_host *host)
inode = fl->fl_file->f_dentry->d_inode;
if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
continue;
+ if (fl->fl_u.nfs_fl.owner == NULL)
+ continue;
if (fl->fl_u.nfs_fl.owner->host != host)
continue;
if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED))
@@ -226,6 +228,8 @@ restart:
inode = fl->fl_file->f_dentry->d_inode;
if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
continue;
+ if (fl->fl_u.nfs_fl.owner == NULL)
+ continue;
if (fl->fl_u.nfs_fl.owner->host != host)
continue;
if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_RECLAIM))
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index f2ca782aba33..30cae3602867 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -14,6 +14,9 @@
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/nfs_fs.h>
+
+#include <net/inet_sock.h>
+
#include "nfs4_fs.h"
#include "callback.h"
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index b497c71384e8..079228817603 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -678,15 +678,9 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t
if (!count)
goto out;
- if (mapping->nrpages) {
- retval = filemap_fdatawrite(mapping);
- if (retval == 0)
- retval = nfs_wb_all(inode);
- if (retval == 0)
- retval = filemap_fdatawait(mapping);
- if (retval)
- goto out;
- }
+ retval = nfs_sync_mapping(mapping);
+ if (retval)
+ goto out;
retval = nfs_direct_read(inode, ctx, &iov, pos, 1);
if (retval > 0)
@@ -764,15 +758,9 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
if (!count)
goto out;
- if (mapping->nrpages) {
- retval = filemap_fdatawrite(mapping);
- if (retval == 0)
- retval = nfs_wb_all(inode);
- if (retval == 0)
- retval = filemap_fdatawait(mapping);
- if (retval)
- goto out;
- }
+ retval = nfs_sync_mapping(mapping);
+ if (retval)
+ goto out;
retval = nfs_direct_write(inode, ctx, &iov, pos, 1);
if (mapping->nrpages)
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 57d3e77d97ee..7a79fbe9f539 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -433,11 +433,7 @@ static int do_unlk(struct file *filp, int cmd, struct file_lock *fl)
* Flush all pending writes before doing anything
* with locks..
*/
- filemap_fdatawrite(filp->f_mapping);
- down(&inode->i_sem);
- nfs_wb_all(inode);
- up(&inode->i_sem);
- filemap_fdatawait(filp->f_mapping);
+ nfs_sync_mapping(filp->f_mapping);
/* NOTE: special case
* If we're signalled while cleaning up locks on process exit, we
@@ -465,15 +461,8 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
* Flush all pending writes before doing anything
* with locks..
*/
- status = filemap_fdatawrite(filp->f_mapping);
- if (status == 0) {
- down(&inode->i_sem);
- status = nfs_wb_all(inode);
- up(&inode->i_sem);
- if (status == 0)
- status = filemap_fdatawait(filp->f_mapping);
- }
- if (status < 0)
+ status = nfs_sync_mapping(filp->f_mapping);
+ if (status != 0)
goto out;
lock_kernel();
@@ -497,11 +486,7 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
* Make sure we clear the cache whenever we try to get the lock.
* This makes locking act as a cache coherency point.
*/
- filemap_fdatawrite(filp->f_mapping);
- down(&inode->i_sem);
- nfs_wb_all(inode); /* we may have slept */
- up(&inode->i_sem);
- filemap_fdatawait(filp->f_mapping);
+ nfs_sync_mapping(filp->f_mapping);
nfs_zap_caches(inode);
out:
rpc_clnt_sigunmask(NFS_CLIENT(inode), &oldset);
@@ -524,7 +509,8 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
return -EINVAL;
/* No mandatory locks over NFS */
- if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+ if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
+ fl->fl_type != F_UNLCK)
return -ENOLCK;
if (IS_GETLK(cmd))
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index afd75d0463fd..432f41cd75e6 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -640,6 +640,27 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
return 0;
}
+/**
+ * nfs_sync_mapping - helper to flush all mmapped dirty data to disk
+ */
+int nfs_sync_mapping(struct address_space *mapping)
+{
+ int ret;
+
+ if (mapping->nrpages == 0)
+ return 0;
+ unmap_mapping_range(mapping, 0, 0, 0);
+ ret = filemap_fdatawrite(mapping);
+ if (ret != 0)
+ goto out;
+ ret = filemap_fdatawait(mapping);
+ if (ret != 0)
+ goto out;
+ ret = nfs_wb_all(mapping->host);
+out:
+ return ret;
+}
+
/*
* Invalidate the local caches
*/
@@ -1179,11 +1200,8 @@ void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
struct nfs_inode *nfsi = NFS_I(inode);
if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
- if (S_ISREG(inode->i_mode)) {
- if (filemap_fdatawrite(mapping) == 0)
- filemap_fdatawait(mapping);
- nfs_wb_all(inode);
- }
+ if (S_ISREG(inode->i_mode))
+ nfs_sync_mapping(mapping);
invalidate_inode_pages2(mapping);
spin_lock(&inode->i_lock);
diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c
index 7cbf0682b2f0..fc95c4df6693 100644
--- a/fs/nfsd/nfs2acl.c
+++ b/fs/nfsd/nfs2acl.c
@@ -107,7 +107,7 @@ static int nfsacld_proc_setacl(struct svc_rqst * rqstp,
dprintk("nfsd: SETACL(2acl) %s\n", SVCFH_fmt(&argp->fh));
fh = fh_copy(&resp->fh, &argp->fh);
- nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
+ nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_SATTR);
if (!nfserr) {
nfserr = nfserrno( nfsd_set_posix_acl(
diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c
index 64ba40572fea..16e10c170aed 100644
--- a/fs/nfsd/nfs3acl.c
+++ b/fs/nfsd/nfs3acl.c
@@ -101,7 +101,7 @@ static int nfsd3_proc_setacl(struct svc_rqst * rqstp,
int nfserr = 0;
fh = fh_copy(&resp->fh, &argp->fh);
- nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
+ nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_SATTR);
if (!nfserr) {
nfserr = nfserrno( nfsd_set_posix_acl(
diff --git a/fs/partitions/Kconfig b/fs/partitions/Kconfig
index deb25b661f04..656bc43431b9 100644
--- a/fs/partitions/Kconfig
+++ b/fs/partitions/Kconfig
@@ -203,7 +203,7 @@ config ULTRIX_PARTITION
config SUN_PARTITION
bool "Sun partition tables support" if PARTITION_ADVANCED
- default y if (SPARC32 || SPARC64 || SUN3 || SUN3X)
+ default y if (SPARC || SUN3 || SUN3X)
---help---
Like most systems, SunOS uses its own hard disk partition table
format, incompatible with all others. Saying Y here allows you to
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index b638fb500743..72b431d0a0a4 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -54,6 +54,18 @@ proc_file_read(struct file *file, char __user *buf, size_t nbytes,
ssize_t n, count;
char *start;
struct proc_dir_entry * dp;
+ unsigned long long pos;
+
+ /*
+ * Gaah, please just use "seq_file" instead. The legacy /proc
+ * interfaces cut loff_t down to off_t for reads, and ignore
+ * the offset entirely for writes..
+ */
+ pos = *ppos;
+ if (pos > MAX_NON_LFS)
+ return 0;
+ if (nbytes > MAX_NON_LFS - pos)
+ nbytes = MAX_NON_LFS - pos;
dp = PDE(inode);
if (!(page = (char*) __get_free_page(GFP_KERNEL)))
@@ -202,30 +214,17 @@ proc_file_write(struct file *file, const char __user *buffer,
static loff_t
proc_file_lseek(struct file *file, loff_t offset, int orig)
{
- lock_kernel();
-
- switch (orig) {
- case 0:
- if (offset < 0)
- goto out;
- file->f_pos = offset;
- unlock_kernel();
- return(file->f_pos);
- case 1:
- if (offset + file->f_pos < 0)
- goto out;
- file->f_pos += offset;
- unlock_kernel();
- return(file->f_pos);
- case 2:
- goto out;
- default:
- goto out;
- }
-
-out:
- unlock_kernel();
- return -EINVAL;
+ loff_t retval = -EINVAL;
+ switch (orig) {
+ case 1:
+ offset += file->f_pos;
+ /* fallthrough */
+ case 0:
+ if (offset < 0 || offset > MAX_NON_LFS)
+ break;
+ file->f_pos = retval = offset;
+ }
+ return retval;
}
static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
diff --git a/fs/read_write.c b/fs/read_write.c
index a091ee4f430d..df3468a22fea 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -14,6 +14,7 @@
#include <linux/security.h>
#include <linux/module.h>
#include <linux/syscalls.h>
+#include <linux/pagemap.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
@@ -182,22 +183,33 @@ bad:
}
#endif
+/*
+ * rw_verify_area doesn't like huge counts. We limit
+ * them to something that fits in "int" so that others
+ * won't have to do range checks all the time.
+ */
+#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)
int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count)
{
struct inode *inode;
loff_t pos;
- if (unlikely(count > INT_MAX))
+ if (unlikely((ssize_t) count < 0))
goto Einval;
pos = *ppos;
if (unlikely((pos < 0) || (loff_t) (pos + count) < 0))
goto Einval;
inode = file->f_dentry->d_inode;
- if (inode->i_flock && MANDATORY_LOCK(inode))
- return locks_mandatory_area(read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, inode, file, pos, count);
- return 0;
+ if (inode->i_flock && MANDATORY_LOCK(inode)) {
+ int retval = locks_mandatory_area(
+ read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
+ inode, file, pos, count);
+ if (retval < 0)
+ return retval;
+ }
+ return count > MAX_RW_COUNT ? MAX_RW_COUNT : count;
Einval:
return -EINVAL;
@@ -244,7 +256,8 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
return -EFAULT;
ret = rw_verify_area(READ, file, pos, count);
- if (!ret) {
+ if (ret >= 0) {
+ count = ret;
ret = security_file_permission (file, MAY_READ);
if (!ret) {
if (file->f_op->read)
@@ -295,7 +308,8 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_
return -EFAULT;
ret = rw_verify_area(WRITE, file, pos, count);
- if (!ret) {
+ if (ret >= 0) {
+ count = ret;
ret = security_file_permission (file, MAY_WRITE);
if (!ret) {
if (file->f_op->write)
@@ -497,7 +511,7 @@ static ssize_t do_readv_writev(int type, struct file *file,
}
ret = rw_verify_area(type, file, pos, tot_len);
- if (ret)
+ if (ret < 0)
goto out;
ret = security_file_permission(file, type == READ ? MAY_READ : MAY_WRITE);
if (ret)
@@ -653,8 +667,9 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
if (!(in_file->f_mode & FMODE_PREAD))
goto fput_in;
retval = rw_verify_area(READ, in_file, ppos, count);
- if (retval)
+ if (retval < 0)
goto fput_in;
+ count = retval;
retval = security_file_permission (in_file, MAY_READ);
if (retval)
@@ -674,8 +689,9 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
goto fput_out;
out_inode = out_file->f_dentry->d_inode;
retval = rw_verify_area(WRITE, out_file, &out_file->f_pos, count);
- if (retval)
+ if (retval < 0)
goto fput_out;
+ count = retval;
retval = security_file_permission (out_file, MAY_WRITE);
if (retval)
diff --git a/fs/relayfs/relay.c b/fs/relayfs/relay.c
index 16446a15c96d..2a6f7f12b7f9 100644
--- a/fs/relayfs/relay.c
+++ b/fs/relayfs/relay.c
@@ -333,8 +333,7 @@ size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length)
return length;
toobig:
- printk(KERN_WARNING "relayfs: event too large (%Zd)\n", length);
- WARN_ON(1);
+ buf->chan->last_toobig = length;
return 0;
}
@@ -399,6 +398,11 @@ void relay_close(struct rchan *chan)
relay_close_buf(chan->buf[i]);
}
+ if (chan->last_toobig)
+ printk(KERN_WARNING "relayfs: one or more items not logged "
+ "[item size (%Zd) > sub-buffer size (%Zd)]\n",
+ chan->last_toobig, chan->subbuf_size);
+
kref_put(&chan->kref, relay_destroy_channel);
}
diff --git a/include/acpi/acglobal.h b/include/acpi/acglobal.h
index e9c2790139ec..4ab2ca18b8df 100644
--- a/include/acpi/acglobal.h
+++ b/include/acpi/acglobal.h
@@ -211,7 +211,7 @@ ACPI_EXTERN u32 acpi_gbl_original_mode;
ACPI_EXTERN u32 acpi_gbl_rsdp_original_location;
ACPI_EXTERN u32 acpi_gbl_ns_lookup_count;
ACPI_EXTERN u32 acpi_gbl_ps_find_count;
-ACPI_EXTERN u32 acpi_gbl_owner_id_mask;
+ACPI_EXTERN u64 acpi_gbl_owner_id_mask;
ACPI_EXTERN u16 acpi_gbl_pm1_enable_register_save;
ACPI_EXTERN u16 acpi_gbl_global_lock_handle;
ACPI_EXTERN u8 acpi_gbl_debugger_configuration;
diff --git a/include/asm-alpha/bitops.h b/include/asm-alpha/bitops.h
index 578ed3f1a607..302201f1a097 100644
--- a/include/asm-alpha/bitops.h
+++ b/include/asm-alpha/bitops.h
@@ -321,6 +321,7 @@ static inline int fls(int word)
#else
#define fls generic_fls
#endif
+#define fls64 generic_fls64
/* Compute powers of two for the given integer. */
static inline long floor_log2(unsigned long word)
diff --git a/include/asm-arm/arch-pxa/ohci.h b/include/asm-arm/arch-pxa/ohci.h
new file mode 100644
index 000000000000..7da89569061e
--- /dev/null
+++ b/include/asm-arm/arch-pxa/ohci.h
@@ -0,0 +1,18 @@
+#ifndef ASMARM_ARCH_OHCI_H
+#define ASMARM_ARCH_OHCI_H
+
+struct device;
+
+struct pxaohci_platform_data {
+ int (*init)(struct device *);
+ void (*exit)(struct device *);
+
+ int port_mode;
+#define PMM_NPS_MODE 1
+#define PMM_GLOBAL_MODE 2
+#define PMM_PERPORT_MODE 3
+};
+
+extern void pxa_set_ohci_info(struct pxaohci_platform_data *info);
+
+#endif
diff --git a/include/asm-arm/bitops.h b/include/asm-arm/bitops.h
index 7399d431edfe..d02de721ecc1 100644
--- a/include/asm-arm/bitops.h
+++ b/include/asm-arm/bitops.h
@@ -332,6 +332,7 @@ static inline unsigned long __ffs(unsigned long word)
*/
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
/*
* ffs: find first bit set. This is defined the same way as
@@ -351,6 +352,7 @@ static inline unsigned long __ffs(unsigned long word)
#define fls(x) \
( __builtin_constant_p(x) ? generic_fls(x) : \
({ int __r; asm("clz\t%0, %1" : "=r"(__r) : "r"(x) : "cc"); 32-__r; }) )
+#define fls64(x) generic_fls64(x)
#define ffs(x) ({ unsigned long __t = (x); fls(__t & -__t); })
#define __ffs(x) (ffs(x) - 1)
#define ffz(x) __ffs( ~(x) )
diff --git a/include/asm-arm26/bitops.h b/include/asm-arm26/bitops.h
index 7d062fb2e343..15cc6f2da792 100644
--- a/include/asm-arm26/bitops.h
+++ b/include/asm-arm26/bitops.h
@@ -259,6 +259,7 @@ static inline unsigned long __ffs(unsigned long word)
*/
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
/*
* ffs: find first bit set. This is defined the same way as
diff --git a/include/asm-cris/bitops.h b/include/asm-cris/bitops.h
index 1bddb3f3a289..d3eb0f1e4208 100644
--- a/include/asm-cris/bitops.h
+++ b/include/asm-cris/bitops.h
@@ -240,6 +240,7 @@ static inline int test_bit(int nr, const volatile unsigned long *addr)
*/
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
/*
* hweightN - returns the hamming weight of a N-bit word
diff --git a/include/asm-frv/bitops.h b/include/asm-frv/bitops.h
index b664bd5b6663..02be7b3a8a83 100644
--- a/include/asm-frv/bitops.h
+++ b/include/asm-frv/bitops.h
@@ -228,6 +228,7 @@ found_middle:
\
bit ? 33 - bit : bit; \
})
+#define fls64(x) generic_fls64(x)
/*
* Every architecture must define this function. It's the fastest
diff --git a/include/asm-generic/bitops.h b/include/asm-generic/bitops.h
index ce31b739fd80..0e6d9852008c 100644
--- a/include/asm-generic/bitops.h
+++ b/include/asm-generic/bitops.h
@@ -56,6 +56,7 @@ extern __inline__ int test_bit(int nr, const unsigned long * addr)
*/
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
#ifdef __KERNEL__
diff --git a/include/asm-h8300/bitops.h b/include/asm-h8300/bitops.h
index 5036f595f8c9..c0411ec9d651 100644
--- a/include/asm-h8300/bitops.h
+++ b/include/asm-h8300/bitops.h
@@ -406,5 +406,6 @@ found_middle:
#endif /* __KERNEL__ */
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
#endif /* _H8300_BITOPS_H */
diff --git a/include/asm-i386/bitops.h b/include/asm-i386/bitops.h
index ddf1739dc7fd..4807aa1d2e3d 100644
--- a/include/asm-i386/bitops.h
+++ b/include/asm-i386/bitops.h
@@ -372,6 +372,7 @@ static inline unsigned long ffz(unsigned long word)
*/
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
#ifdef __KERNEL__
diff --git a/include/asm-i386/param.h b/include/asm-i386/param.h
index fa02e67ea86b..095580f3a45c 100644
--- a/include/asm-i386/param.h
+++ b/include/asm-i386/param.h
@@ -1,9 +1,8 @@
-#include <linux/config.h>
-
#ifndef _ASMi386_PARAM_H
#define _ASMi386_PARAM_H
#ifdef __KERNEL__
+# include <linux/config.h>
# define HZ CONFIG_HZ /* Internal kernel timer frequency */
# define USER_HZ 100 /* .. some user interfaces are in "ticks" */
# define CLOCKS_PER_SEC (USER_HZ) /* like times() */
diff --git a/include/asm-ia64/bitops.h b/include/asm-ia64/bitops.h
index 7232528e2d0c..36d0fb95ea89 100644
--- a/include/asm-ia64/bitops.h
+++ b/include/asm-ia64/bitops.h
@@ -345,6 +345,7 @@ fls (int t)
x |= x >> 16;
return ia64_popcnt(x);
}
+#define fls64(x) generic_fls64(x)
/*
* ffs: find first bit set. This is defined the same way as the libc and compiler builtin
diff --git a/include/asm-ia64/delay.h b/include/asm-ia64/delay.h
index 57182d6f2b9a..bba702076391 100644
--- a/include/asm-ia64/delay.h
+++ b/include/asm-ia64/delay.h
@@ -84,14 +84,6 @@ __delay (unsigned long loops)
ia64_delay_loop (loops - 1);
}
-static __inline__ void
-udelay (unsigned long usecs)
-{
- unsigned long start = ia64_get_itc();
- unsigned long cycles = usecs*local_cpu_data->cyc_per_usec;
-
- while (ia64_get_itc() - start < cycles)
- cpu_relax();
-}
+extern void udelay (unsigned long usecs);
#endif /* _ASM_IA64_DELAY_H */
diff --git a/include/asm-ia64/topology.h b/include/asm-ia64/topology.h
index a9f738bf18a7..f7c330467e7e 100644
--- a/include/asm-ia64/topology.h
+++ b/include/asm-ia64/topology.h
@@ -38,7 +38,7 @@
/*
* Returns the number of the first CPU on Node 'node'.
*/
-#define node_to_first_cpu(node) (__ffs(node_to_cpumask(node)))
+#define node_to_first_cpu(node) (first_cpu(node_to_cpumask(node)))
/*
* Determines the node for a given pci bus
diff --git a/include/asm-m32r/bitops.h b/include/asm-m32r/bitops.h
index e78443981349..abea2fdd8689 100644
--- a/include/asm-m32r/bitops.h
+++ b/include/asm-m32r/bitops.h
@@ -465,6 +465,7 @@ static __inline__ unsigned long __ffs(unsigned long word)
* fls: find last bit set.
*/
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
#ifdef __KERNEL__
diff --git a/include/asm-m68k/bitops.h b/include/asm-m68k/bitops.h
index b1bcf7c66516..13f4c0048463 100644
--- a/include/asm-m68k/bitops.h
+++ b/include/asm-m68k/bitops.h
@@ -310,6 +310,7 @@ static inline int fls(int x)
return 32 - cnt;
}
+#define fls64(x) generic_fls64(x)
/*
* Every architecture must define this function. It's the fastest
diff --git a/include/asm-m68knommu/bitops.h b/include/asm-m68knommu/bitops.h
index c42f88a9b9f9..4058dd086a02 100644
--- a/include/asm-m68knommu/bitops.h
+++ b/include/asm-m68knommu/bitops.h
@@ -499,5 +499,6 @@ found_middle:
* fls: find last bit set.
*/
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
#endif /* _M68KNOMMU_BITOPS_H */
diff --git a/include/asm-mips/bitops.h b/include/asm-mips/bitops.h
index 5496f9064a6a..3b0c8aaf6e8b 100644
--- a/include/asm-mips/bitops.h
+++ b/include/asm-mips/bitops.h
@@ -695,7 +695,7 @@ static inline unsigned long fls(unsigned long word)
return flz(~word) + 1;
}
-
+#define fls64(x) generic_fls64(x)
/*
* find_next_zero_bit - find the first zero bit in a memory region
diff --git a/include/asm-parisc/bitops.h b/include/asm-parisc/bitops.h
index 55b98c67fd82..15d8c2b51584 100644
--- a/include/asm-parisc/bitops.h
+++ b/include/asm-parisc/bitops.h
@@ -263,6 +263,7 @@ static __inline__ int fls(int x)
return ret;
}
+#define fls64(x) generic_fls64(x)
/*
* hweightN: returns the hamming weight (i.e. the number
diff --git a/include/asm-powerpc/bitops.h b/include/asm-powerpc/bitops.h
index 5727229b0444..1996eaa8aeae 100644
--- a/include/asm-powerpc/bitops.h
+++ b/include/asm-powerpc/bitops.h
@@ -310,6 +310,7 @@ static __inline__ int fls(unsigned int x)
asm ("cntlzw %0,%1" : "=r" (lz) : "r" (x));
return 32 - lz;
}
+#define fls64(x) generic_fls64(x)
/*
* hweightN: returns the hamming weight (i.e. the number
diff --git a/include/asm-ppc/ppc4xx_dma.h b/include/asm-ppc/ppc4xx_dma.h
index a415001165fa..46a086fff816 100644
--- a/include/asm-ppc/ppc4xx_dma.h
+++ b/include/asm-ppc/ppc4xx_dma.h
@@ -33,9 +33,6 @@
#define MAX_PPC4xx_DMA_CHANNELS 4
-/* in arch/ppc/kernel/setup.c -- Cort */
-extern unsigned long DMA_MODE_WRITE, DMA_MODE_READ;
-
/*
* Function return status codes
* These values are used to indicate whether or not the function
diff --git a/include/asm-s390/bitops.h b/include/asm-s390/bitops.h
index b07c578b22ea..61232760cc3b 100644
--- a/include/asm-s390/bitops.h
+++ b/include/asm-s390/bitops.h
@@ -839,6 +839,7 @@ static inline int sched_find_first_bit(unsigned long *b)
* fls: find last bit set.
*/
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
/*
* hweightN: returns the hamming weight (i.e. the number
diff --git a/include/asm-sh/bitops.h b/include/asm-sh/bitops.h
index 5163d1ff2f1b..1c5260860045 100644
--- a/include/asm-sh/bitops.h
+++ b/include/asm-sh/bitops.h
@@ -470,6 +470,7 @@ found_middle:
*/
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
#endif /* __KERNEL__ */
diff --git a/include/asm-sh64/bitops.h b/include/asm-sh64/bitops.h
index e1ff63e09227..ce9c3ad45fe0 100644
--- a/include/asm-sh64/bitops.h
+++ b/include/asm-sh64/bitops.h
@@ -510,6 +510,7 @@ found_middle:
#define ffs(x) generic_ffs(x)
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
#endif /* __KERNEL__ */
diff --git a/include/asm-sparc/bitops.h b/include/asm-sparc/bitops.h
index bfbd795a0a80..41722b5e45ef 100644
--- a/include/asm-sparc/bitops.h
+++ b/include/asm-sparc/bitops.h
@@ -298,6 +298,7 @@ static inline int ffs(int x)
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
*/
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
/*
* hweightN: returns the hamming weight (i.e. the number
diff --git a/include/asm-sparc64/bitops.h b/include/asm-sparc64/bitops.h
index 6388b8376c50..6efc0162fb09 100644
--- a/include/asm-sparc64/bitops.h
+++ b/include/asm-sparc64/bitops.h
@@ -119,6 +119,7 @@ static inline unsigned long __ffs(unsigned long word)
*/
#define fls(x) generic_fls(x)
+#define fls64(x) generic_fls64(x)
#ifdef __KERNEL__
diff --git a/include/asm-v850/bitops.h b/include/asm-v850/bitops.h
index b91e799763fd..8955d2376ac8 100644
--- a/include/asm-v850/bitops.h
+++ b/include/asm-v850/bitops.h
@@ -276,6 +276,7 @@ found_middle:
#define ffs(x) generic_ffs (x)
#define fls(x) generic_fls (x)
+#define fls64(x) generic_fls64(x)
#define __ffs(x) ffs(x)
diff --git a/include/asm-x86_64/bitops.h b/include/asm-x86_64/bitops.h
index 05a0d374404b..a4d5d0909453 100644
--- a/include/asm-x86_64/bitops.h
+++ b/include/asm-x86_64/bitops.h
@@ -340,6 +340,20 @@ static __inline__ unsigned long __ffs(unsigned long word)
return word;
}
+/*
+ * __fls: find last bit set.
+ * @word: The word to search
+ *
+ * Undefined if no zero exists, so code should check against ~0UL first.
+ */
+static __inline__ unsigned long __fls(unsigned long word)
+{
+ __asm__("bsrq %1,%0"
+ :"=r" (word)
+ :"rm" (word));
+ return word;
+}
+
#ifdef __KERNEL__
static inline int sched_find_first_bit(const unsigned long *b)
@@ -370,6 +384,19 @@ static __inline__ int ffs(int x)
}
/**
+ * fls64 - find last bit set in 64 bit word
+ * @x: the word to search
+ *
+ * This is defined the same way as fls.
+ */
+static __inline__ int fls64(__u64 x)
+{
+ if (x == 0)
+ return 0;
+ return __fls(x) + 1;
+}
+
+/**
* hweightN - returns the hamming weight of a N-bit word
* @x: the word to weigh
*
diff --git a/include/asm-x86_64/param.h b/include/asm-x86_64/param.h
index 40b11937180d..5956b23b57c2 100644
--- a/include/asm-x86_64/param.h
+++ b/include/asm-x86_64/param.h
@@ -1,9 +1,8 @@
-#include <linux/config.h>
-
#ifndef _ASMx86_64_PARAM_H
#define _ASMx86_64_PARAM_H
#ifdef __KERNEL__
+# include <linux/config.h>
# define HZ CONFIG_HZ /* Internal kernel timer frequency */
# define USER_HZ 100 /* .. some user interfaces are in "ticks */
#define CLOCKS_PER_SEC (USER_HZ) /* like times() */
diff --git a/include/asm-x86_64/rwlock.h b/include/asm-x86_64/rwlock.h
index 8a78a4ace53c..9942cc393064 100644
--- a/include/asm-x86_64/rwlock.h
+++ b/include/asm-x86_64/rwlock.h
@@ -64,7 +64,7 @@
::"a" (rw) : "memory")
#define __build_write_lock_const(rw, helper) \
- asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \
+ asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",%0\n\t" \
"jnz 2f\n" \
"1:\n" \
LOCK_SECTION_START("") \
diff --git a/include/asm-x86_64/topology.h b/include/asm-x86_64/topology.h
index d39ebd5263ed..7d82bc56b9fa 100644
--- a/include/asm-x86_64/topology.h
+++ b/include/asm-x86_64/topology.h
@@ -23,7 +23,7 @@ extern int __node_distance(int, int);
#define cpu_to_node(cpu) (cpu_to_node[cpu])
#define parent_node(node) (node)
-#define node_to_first_cpu(node) (__ffs(node_to_cpumask[node]))
+#define node_to_first_cpu(node) (first_cpu(node_to_cpumask[node]))
#define node_to_cpumask(node) (node_to_cpumask[node])
#define pcibus_to_node(bus) ((long)(bus->sysdata))
#define pcibus_to_cpumask(bus) node_to_cpumask(pcibus_to_node(bus));
diff --git a/include/asm-xtensa/bitops.h b/include/asm-xtensa/bitops.h
index e76ee889e21d..0a2065f1a372 100644
--- a/include/asm-xtensa/bitops.h
+++ b/include/asm-xtensa/bitops.h
@@ -245,6 +245,7 @@ static __inline__ int fls (unsigned int x)
{
return __cntlz(x);
}
+#define fls64(x) generic_fls64(x)
static __inline__ int
find_next_bit(const unsigned long *addr, int size, int offset)
diff --git a/include/linux/bitops.h b/include/linux/bitops.h
index 38c2fb7ebe09..6a2a19f14bb2 100644
--- a/include/linux/bitops.h
+++ b/include/linux/bitops.h
@@ -76,6 +76,15 @@ static __inline__ int generic_fls(int x)
*/
#include <asm/bitops.h>
+
+static inline int generic_fls64(__u64 x)
+{
+ __u32 h = x >> 32;
+ if (h)
+ return fls(x) + 32;
+ return fls(x);
+}
+
static __inline__ int get_bitmask_order(unsigned int count)
{
int order;
diff --git a/include/linux/cache.h b/include/linux/cache.h
index f6b5a46c5f82..0b7ecf3af78a 100644
--- a/include/linux/cache.h
+++ b/include/linux/cache.h
@@ -13,7 +13,7 @@
#define SMP_CACHE_BYTES L1_CACHE_BYTES
#endif
-#if defined(CONFIG_X86) || defined(CONFIG_SPARC64)
+#if defined(CONFIG_X86) || defined(CONFIG_SPARC64) || defined(CONFIG_IA64)
#define __read_mostly __attribute__((__section__(".data.read_mostly")))
#else
#define __read_mostly
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index d068176b7ad7..c31650df9241 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -256,6 +256,16 @@ int cpufreq_update_policy(unsigned int cpu);
/* query the current CPU frequency (in kHz). If zero, cpufreq couldn't detect it */
unsigned int cpufreq_get(unsigned int cpu);
+/* query the last known CPU freq (in kHz). If zero, cpufreq couldn't detect it */
+#ifdef CONFIG_CPU_FREQ
+unsigned int cpufreq_quick_get(unsigned int cpu);
+#else
+static inline unsigned int cpufreq_quick_get(unsigned int cpu)
+{
+ return 0;
+}
+#endif
+
/*********************************************************************
* CPUFREQ DEFAULT GOVERNOR *
diff --git a/include/linux/dccp.h b/include/linux/dccp.h
index 71fab4311e92..088529f54965 100644
--- a/include/linux/dccp.h
+++ b/include/linux/dccp.h
@@ -192,10 +192,9 @@ enum {
#include <linux/workqueue.h>
#include <net/inet_connection_sock.h>
+#include <net/inet_sock.h>
#include <net/inet_timewait_sock.h>
-#include <net/sock.h>
#include <net/tcp_states.h>
-#include <net/tcp.h>
enum dccp_state {
DCCP_OPEN = TCP_ESTABLISHED,
@@ -408,8 +407,6 @@ struct dccp_ackvec;
* @dccps_gar - greatest valid ack number received on a non-Sync; initialized to %dccps_iss
* @dccps_timestamp_time - time of latest TIMESTAMP option
* @dccps_timestamp_echo - latest timestamp received on a TIMESTAMP option
- * @dccps_ext_header_len - network protocol overhead (IP/IPv6 options)
- * @dccps_pmtu_cookie - Last pmtu seen by socket
* @dccps_packet_size - Set thru setsockopt
* @dccps_role - Role of this sock, one of %dccp_role
* @dccps_ndp_count - number of Non Data Packets since last data packet
@@ -434,8 +431,6 @@ struct dccp_sock {
__u32 dccps_timestamp_echo;
__u32 dccps_packet_size;
unsigned long dccps_ndp_count;
- __u16 dccps_ext_header_len;
- __u32 dccps_pmtu_cookie;
__u32 dccps_mss_cache;
struct dccp_options dccps_options;
struct dccp_ackvec *dccps_hc_rx_ackvec;
diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h
index 5f49a30eb6f2..745c988359c0 100644
--- a/include/linux/etherdevice.h
+++ b/include/linux/etherdevice.h
@@ -63,10 +63,11 @@ static inline int is_zero_ether_addr(const u8 *addr)
* @addr: Pointer to a six-byte array containing the Ethernet address
*
* Return true if the address is a multicast address.
+ * By definition the broadcast address is also a multicast address.
*/
static inline int is_multicast_ether_addr(const u8 *addr)
{
- return ((addr[0] != 0xff) && (0x01 & addr[0]));
+ return (0x01 & addr[0]);
}
/**
diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h
index e677f73f13dd..4fab3d0a4bce 100644
--- a/include/linux/if_pppox.h
+++ b/include/linux/if_pppox.h
@@ -157,8 +157,7 @@ struct pppox_proto {
extern int register_pppox_proto(int proto_num, struct pppox_proto *pp);
extern void unregister_pppox_proto(int proto_num);
extern void pppox_unbind_sock(struct sock *sk);/* delete ppp-channel binding */
-extern int pppox_channel_ioctl(struct ppp_channel *pc, unsigned int cmd,
- unsigned long arg);
+extern int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
/* PPPoX socket states */
enum {
diff --git a/include/linux/ip.h b/include/linux/ip.h
index 33e8a19a1a0f..9e2eb9a602eb 100644
--- a/include/linux/ip.h
+++ b/include/linux/ip.h
@@ -16,6 +16,7 @@
*/
#ifndef _LINUX_IP_H
#define _LINUX_IP_H
+#include <linux/types.h>
#include <asm/byteorder.h>
#define IPTOS_TOS_MASK 0x1E
@@ -78,126 +79,6 @@
#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
#define IPOPT_TS_PRESPEC 3 /* specified modules only */
-#ifdef __KERNEL__
-#include <linux/config.h>
-#include <linux/types.h>
-#include <net/request_sock.h>
-#include <net/sock.h>
-#include <linux/igmp.h>
-#include <net/flow.h>
-
-struct ip_options {
- __u32 faddr; /* Saved first hop address */
- unsigned char optlen;
- unsigned char srr;
- unsigned char rr;
- unsigned char ts;
- unsigned char is_setbyuser:1, /* Set by setsockopt? */
- is_data:1, /* Options in __data, rather than skb */
- is_strictroute:1, /* Strict source route */
- srr_is_hit:1, /* Packet destination addr was our one */
- is_changed:1, /* IP checksum more not valid */
- rr_needaddr:1, /* Need to record addr of outgoing dev */
- ts_needtime:1, /* Need to record timestamp */
- ts_needaddr:1; /* Need to record addr of outgoing dev */
- unsigned char router_alert;
- unsigned char __pad1;
- unsigned char __pad2;
- unsigned char __data[0];
-};
-
-#define optlength(opt) (sizeof(struct ip_options) + opt->optlen)
-
-struct inet_request_sock {
- struct request_sock req;
- u32 loc_addr;
- u32 rmt_addr;
- u16 rmt_port;
- u16 snd_wscale : 4,
- rcv_wscale : 4,
- tstamp_ok : 1,
- sack_ok : 1,
- wscale_ok : 1,
- ecn_ok : 1,
- acked : 1;
- struct ip_options *opt;
-};
-
-static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk)
-{
- return (struct inet_request_sock *)sk;
-}
-
-struct ipv6_pinfo;
-
-struct inet_sock {
- /* sk and pinet6 has to be the first two members of inet_sock */
- struct sock sk;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- struct ipv6_pinfo *pinet6;
-#endif
- /* Socket demultiplex comparisons on incoming packets. */
- __u32 daddr; /* Foreign IPv4 addr */
- __u32 rcv_saddr; /* Bound local IPv4 addr */
- __u16 dport; /* Destination port */
- __u16 num; /* Local port */
- __u32 saddr; /* Sending source */
- __s16 uc_ttl; /* Unicast TTL */
- __u16 cmsg_flags;
- struct ip_options *opt;
- __u16 sport; /* Source port */
- __u16 id; /* ID counter for DF pkts */
- __u8 tos; /* TOS */
- __u8 mc_ttl; /* Multicasting TTL */
- __u8 pmtudisc;
- unsigned recverr : 1,
- freebind : 1,
- hdrincl : 1,
- mc_loop : 1;
- int mc_index; /* Multicast device index */
- __u32 mc_addr;
- struct ip_mc_socklist *mc_list; /* Group array */
- /*
- * Following members are used to retain the infomation to build
- * an ip header on each ip fragmentation while the socket is corked.
- */
- struct {
- unsigned int flags;
- unsigned int fragsize;
- struct ip_options *opt;
- struct rtable *rt;
- int length; /* Total length of all frames */
- u32 addr;
- struct flowi fl;
- } cork;
-};
-
-#define IPCORK_OPT 1 /* ip-options has been held in ipcork.opt */
-#define IPCORK_ALLFRAG 2 /* always fragment (for ipv6 for now) */
-
-static inline struct inet_sock *inet_sk(const struct sock *sk)
-{
- return (struct inet_sock *)sk;
-}
-
-static inline void __inet_sk_copy_descendant(struct sock *sk_to,
- const struct sock *sk_from,
- const int ancestor_size)
-{
- memcpy(inet_sk(sk_to) + 1, inet_sk(sk_from) + 1,
- sk_from->sk_prot->obj_size - ancestor_size);
-}
-#if !(defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE))
-static inline void inet_sk_copy_descendant(struct sock *sk_to,
- const struct sock *sk_from)
-{
- __inet_sk_copy_descendant(sk_to, sk_from, sizeof(struct inet_sock));
-}
-#endif
-#endif
-
-extern int inet_sk_rebuild_header(struct sock *sk);
-
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:4,
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index e0b922785d98..93bbed5c6cf4 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -171,12 +171,13 @@ enum {
};
#ifdef __KERNEL__
-#include <linux/in6.h> /* struct sockaddr_in6 */
#include <linux/icmpv6.h>
-#include <net/if_inet6.h> /* struct ipv6_mc_socklist */
#include <linux/tcp.h>
#include <linux/udp.h>
+#include <net/if_inet6.h> /* struct ipv6_mc_socklist */
+#include <net/inet_sock.h>
+
/*
This structure contains results of exthdrs parsing
as offsets from skb->nh.
@@ -199,18 +200,17 @@ static inline int inet6_iif(const struct sk_buff *skb)
return IP6CB(skb)->iif;
}
-struct tcp6_request_sock {
- struct tcp_request_sock req;
+struct inet6_request_sock {
struct in6_addr loc_addr;
struct in6_addr rmt_addr;
struct sk_buff *pktopts;
int iif;
};
-static inline struct tcp6_request_sock *tcp6_rsk(const struct request_sock *sk)
-{
- return (struct tcp6_request_sock *)sk;
-}
+struct tcp6_request_sock {
+ struct tcp_request_sock tcp6rsk_tcp;
+ struct inet6_request_sock tcp6rsk_inet6;
+};
/**
* struct ipv6_pinfo - ipv6 private area
@@ -298,12 +298,36 @@ struct tcp6_sock {
struct ipv6_pinfo inet6;
};
+extern int inet6_sk_rebuild_header(struct sock *sk);
+
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk)
{
return inet_sk(__sk)->pinet6;
}
+static inline struct inet6_request_sock *
+ inet6_rsk(const struct request_sock *rsk)
+{
+ return (struct inet6_request_sock *)(((u8 *)rsk) +
+ inet_rsk(rsk)->inet6_rsk_offset);
+}
+
+static inline u32 inet6_rsk_offset(struct request_sock *rsk)
+{
+ return rsk->rsk_ops->obj_size - sizeof(struct inet6_request_sock);
+}
+
+static inline struct request_sock *inet6_reqsk_alloc(struct request_sock_ops *ops)
+{
+ struct request_sock *req = reqsk_alloc(ops);
+
+ if (req != NULL)
+ inet_rsk(req)->inet6_rsk_offset = inet6_rsk_offset(req);
+
+ return req;
+}
+
static inline struct raw6_sock *raw6_sk(const struct sock *sk)
{
return (struct raw6_sock *)sk;
@@ -323,28 +347,37 @@ static inline void inet_sk_copy_descendant(struct sock *sk_to,
#define __ipv6_only_sock(sk) (inet6_sk(sk)->ipv6only)
#define ipv6_only_sock(sk) ((sk)->sk_family == PF_INET6 && __ipv6_only_sock(sk))
-#include <linux/tcp.h>
+struct inet6_timewait_sock {
+ struct in6_addr tw_v6_daddr;
+ struct in6_addr tw_v6_rcv_saddr;
+};
struct tcp6_timewait_sock {
- struct tcp_timewait_sock tw_v6_sk;
- struct in6_addr tw_v6_daddr;
- struct in6_addr tw_v6_rcv_saddr;
+ struct tcp_timewait_sock tcp6tw_tcp;
+ struct inet6_timewait_sock tcp6tw_inet6;
};
-static inline struct tcp6_timewait_sock *tcp6_twsk(const struct sock *sk)
+static inline u16 inet6_tw_offset(const struct proto *prot)
{
- return (struct tcp6_timewait_sock *)sk;
+ return prot->twsk_prot->twsk_obj_size -
+ sizeof(struct inet6_timewait_sock);
}
-static inline struct in6_addr *__tcp_v6_rcv_saddr(const struct sock *sk)
+static inline struct inet6_timewait_sock *inet6_twsk(const struct sock *sk)
+{
+ return (struct inet6_timewait_sock *)(((u8 *)sk) +
+ inet_twsk(sk)->tw_ipv6_offset);
+}
+
+static inline struct in6_addr *__inet6_rcv_saddr(const struct sock *sk)
{
return likely(sk->sk_state != TCP_TIME_WAIT) ?
- &inet6_sk(sk)->rcv_saddr : &tcp6_twsk(sk)->tw_v6_rcv_saddr;
+ &inet6_sk(sk)->rcv_saddr : &inet6_twsk(sk)->tw_v6_rcv_saddr;
}
-static inline struct in6_addr *tcp_v6_rcv_saddr(const struct sock *sk)
+static inline struct in6_addr *inet6_rcv_saddr(const struct sock *sk)
{
- return sk->sk_family == AF_INET6 ? __tcp_v6_rcv_saddr(sk) : NULL;
+ return sk->sk_family == AF_INET6 ? __inet6_rcv_saddr(sk) : NULL;
}
static inline int inet_v6_ipv6only(const struct sock *sk)
@@ -361,13 +394,19 @@ static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk)
return NULL;
}
+static inline struct inet6_request_sock *
+ inet6_rsk(const struct request_sock *rsk)
+{
+ return NULL;
+}
+
static inline struct raw6_sock *raw6_sk(const struct sock *sk)
{
return NULL;
}
-#define __tcp_v6_rcv_saddr(__sk) NULL
-#define tcp_v6_rcv_saddr(__sk) NULL
+#define __inet6_rcv_saddr(__sk) NULL
+#define inet6_rcv_saddr(__sk) NULL
#define tcp_twsk_ipv6only(__sk) 0
#define inet_v6_ipv6only(__sk) 0
#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
diff --git a/include/linux/ipv6_route.h b/include/linux/ipv6_route.h
index e2f935038013..d7c41d1d706a 100644
--- a/include/linux/ipv6_route.h
+++ b/include/linux/ipv6_route.h
@@ -18,6 +18,7 @@
fallback, no routers on link */
#define RTF_ADDRCONF 0x00040000 /* addrconf route - RA */
#define RTF_PREFIX_RT 0x00080000 /* A prefix only route - RA */
+#define RTF_ANYCAST 0x00100000 /* Anycast */
#define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */
#define RTF_EXPIRES 0x00400000
diff --git a/include/linux/irq.h b/include/linux/irq.h
index c516382fbec2..f04ba20712a2 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -10,7 +10,7 @@
*/
#include <linux/config.h>
-#include <asm/smp.h> /* cpu_online_map */
+#include <linux/smp.h>
#if !defined(CONFIG_ARCH_S390)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index e5677f456742..a06a84d347fb 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -163,6 +163,7 @@ extern unsigned int kobjsize(const void *objp);
#define VM_HUGETLB 0x00400000 /* Huge TLB Page VM */
#define VM_NONLINEAR 0x00800000 /* Is non-linear (remap_file_pages) */
#define VM_MAPPED_COPY 0x01000000 /* T if mapped copy of data (nommu mmap) */
+#define VM_INSERTPAGE 0x02000000 /* The vma has had "vm_insert_page()" done on it */
#ifndef VM_STACK_DEFAULT_FLAGS /* arch can override this */
#define VM_STACK_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index f1fd4215686a..7419b5fab133 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -17,7 +17,6 @@
#include <linux/mtd/bbm.h>
#define MAX_BUFFERRAM 2
-#define MAX_ONENAND_PAGESIZE (2048 + 64)
/* Scan and identify a OneNAND device */
extern int onenand_scan(struct mtd_info *mtd, int max_chips);
@@ -110,6 +109,7 @@ struct onenand_chip {
spinlock_t chip_lock;
wait_queue_head_t wq;
onenand_state_t state;
+ unsigned char *page_buf;
struct nand_oobinfo *autooob;
@@ -134,13 +134,12 @@ struct onenand_chip {
* Options bits
*/
#define ONENAND_CONT_LOCK (0x0001)
-
+#define ONENAND_PAGEBUF_ALLOC (0x1000)
/*
* OneNAND Flash Manufacturer ID Codes
*/
#define ONENAND_MFR_SAMSUNG 0xec
-#define ONENAND_MFR_UNKNOWN 0x00
/**
* struct nand_manufacturers - NAND Flash Manufacturer ID Structure
diff --git a/include/linux/n_r3964.h b/include/linux/n_r3964.h
index 2352bcd31a06..db4f3776978a 100644
--- a/include/linux/n_r3964.h
+++ b/include/linux/n_r3964.h
@@ -13,6 +13,10 @@
* L. Haag
*
* $Log: r3964.h,v $
+ * Revision 1.4 2005/12/21 19:54:24 Kurt Huwig <kurt huwig de>
+ * Fixed HZ usage on 2.6 kernels
+ * Removed unnecessary include
+ *
* Revision 1.3 2001/03/18 13:02:24 dwmw2
* Fix timer usage, use spinlocks properly.
*
@@ -45,9 +49,11 @@
#define __LINUX_N_R3964_H__
/* line disciplines for r3964 protocol */
-#include <asm/termios.h>
#ifdef __KERNEL__
+
+#include <linux/param.h>
+
/*
* Common ascii handshake characters:
*/
@@ -58,14 +64,14 @@
#define NAK 0x15
/*
- * Timeouts (msecs/10 msecs per timer interrupt):
+ * Timeouts (from milliseconds to jiffies)
*/
-#define R3964_TO_QVZ 550/10
-#define R3964_TO_ZVZ 220/10
-#define R3964_TO_NO_BUF 400/10
-#define R3964_NO_TX_ROOM 100/10
-#define R3964_TO_RX_PANIC 4000/10
+#define R3964_TO_QVZ ((550)*HZ/1000)
+#define R3964_TO_ZVZ ((220)*HZ/1000)
+#define R3964_TO_NO_BUF ((400)*HZ/1000)
+#define R3964_NO_TX_ROOM ((100)*HZ/1000)
+#define R3964_TO_RX_PANIC ((4000)*HZ/1000)
#define R3964_MAX_RETRIES 5
#endif
diff --git a/include/linux/net.h b/include/linux/net.h
index d6a41e6577f6..28195a2d8ff0 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -107,7 +107,7 @@ enum sock_type {
struct socket {
socket_state state;
unsigned long flags;
- struct proto_ops *ops;
+ const struct proto_ops *ops;
struct fasync_struct *fasync_list;
struct file *file;
struct sock *sk;
@@ -260,7 +260,7 @@ SOCKCALL_WRAP(name, recvmsg, (struct kiocb *iocb, struct socket *sock, struct ms
SOCKCALL_WRAP(name, mmap, (struct file *file, struct socket *sock, struct vm_area_struct *vma), \
(file, sock, vma)) \
\
-static struct proto_ops name##_ops = { \
+static const struct proto_ops name##_ops = { \
.family = fam, \
.owner = THIS_MODULE, \
.release = __lock_##name##_release, \
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 12787a9b0259..2516adeccecf 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -291,6 +291,7 @@ static inline int nfs_verify_change_attribute(struct inode *inode, unsigned long
/*
* linux/fs/nfs/inode.c
*/
+extern int nfs_sync_mapping(struct address_space *mapping);
extern void nfs_zap_caches(struct inode *);
extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
struct nfs_fattr *);
diff --git a/include/linux/pfkeyv2.h b/include/linux/pfkeyv2.h
index 724066778aff..6351c4055ace 100644
--- a/include/linux/pfkeyv2.h
+++ b/include/linux/pfkeyv2.h
@@ -216,6 +216,16 @@ struct sadb_x_nat_t_port {
} __attribute__((packed));
/* sizeof(struct sadb_x_nat_t_port) == 8 */
+/* Generic LSM security context */
+struct sadb_x_sec_ctx {
+ uint16_t sadb_x_sec_len;
+ uint16_t sadb_x_sec_exttype;
+ uint8_t sadb_x_ctx_alg; /* LSMs: e.g., selinux == 1 */
+ uint8_t sadb_x_ctx_doi;
+ uint16_t sadb_x_ctx_len;
+} __attribute__((packed));
+/* sizeof(struct sadb_sec_ctx) = 8 */
+
/* Message types */
#define SADB_RESERVED 0
#define SADB_GETSPI 1
@@ -325,7 +335,8 @@ struct sadb_x_nat_t_port {
#define SADB_X_EXT_NAT_T_SPORT 21
#define SADB_X_EXT_NAT_T_DPORT 22
#define SADB_X_EXT_NAT_T_OA 23
-#define SADB_EXT_MAX 23
+#define SADB_X_EXT_SEC_CTX 24
+#define SADB_EXT_MAX 24
/* Identity Extension values */
#define SADB_IDENTTYPE_RESERVED 0
diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index e87b233615b3..d10f35338507 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -429,6 +429,7 @@ enum
TCA_NETEM_CORR,
TCA_NETEM_DELAY_DIST,
TCA_NETEM_REORDER,
+ TCA_NETEM_CORRUPT,
__TCA_NETEM_MAX,
};
@@ -457,6 +458,12 @@ struct tc_netem_reorder
__u32 correlation;
};
+struct tc_netem_corrupt
+{
+ __u32 probability;
+ __u32 correlation;
+};
+
#define NETEM_DIST_SCALE 8192
#endif
diff --git a/include/linux/preempt.h b/include/linux/preempt.h
index d9a2f5254a51..5769d14d1e6a 100644
--- a/include/linux/preempt.h
+++ b/include/linux/preempt.h
@@ -48,6 +48,7 @@ do { \
#define preempt_enable() \
do { \
preempt_enable_no_resched(); \
+ barrier(); \
preempt_check_resched(); \
} while (0)
diff --git a/include/linux/random.h b/include/linux/random.h
index 7b2adb3322d5..5d6456bcdeba 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -52,9 +52,9 @@ extern void get_random_bytes(void *buf, int nbytes);
void generate_random_uuid(unsigned char uuid_out[16]);
extern __u32 secure_ip_id(__u32 daddr);
-extern u32 secure_tcp_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport);
-extern u32 secure_tcpv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr,
- __u16 dport);
+extern u32 secure_ipv4_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport);
+extern u32 secure_ipv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr,
+ __u16 dport);
extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
__u16 sport, __u16 dport);
extern __u32 secure_tcpv6_sequence_number(__u32 *saddr, __u32 *daddr,
diff --git a/include/linux/relayfs_fs.h b/include/linux/relayfs_fs.h
index cfafc3e76bc2..fb7e80737325 100644
--- a/include/linux/relayfs_fs.h
+++ b/include/linux/relayfs_fs.h
@@ -20,9 +20,9 @@
#include <linux/kref.h>
/*
- * Tracks changes to rchan_buf struct
+ * Tracks changes to rchan/rchan_buf structs
*/
-#define RELAYFS_CHANNEL_VERSION 5
+#define RELAYFS_CHANNEL_VERSION 6
/*
* Per-cpu relay channel buffer
@@ -60,6 +60,7 @@ struct rchan
struct rchan_callbacks *cb; /* client callbacks */
struct kref kref; /* channel refcount */
void *private_data; /* for user-defined data */
+ size_t last_toobig; /* tried to log event > subbuf size */
struct rchan_buf *buf[NR_CPUS]; /* per-cpu channel buffers */
};
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index c231e9a08f0b..d50482ba27fe 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -866,6 +866,7 @@ enum rtnetlink_groups {
#define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE
RTNLGRP_IPV4_ROUTE,
#define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE
+ RTNLGRP_NOP1,
RTNLGRP_IPV6_IFADDR,
#define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR
RTNLGRP_IPV6_MROUTE,
@@ -876,8 +877,11 @@ enum rtnetlink_groups {
#define RTNLGRP_IPV6_IFINFO RTNLGRP_IPV6_IFINFO
RTNLGRP_DECnet_IFADDR,
#define RTNLGRP_DECnet_IFADDR RTNLGRP_DECnet_IFADDR
+ RTNLGRP_NOP2,
RTNLGRP_DECnet_ROUTE,
#define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE
+ RTNLGRP_NOP3,
+ RTNLGRP_NOP4,
RTNLGRP_IPV6_PREFIX,
#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX
__RTNLGRP_MAX
diff --git a/include/linux/security.h b/include/linux/security.h
index f7e0ae018712..ef753654daa5 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -59,6 +59,12 @@ struct sk_buff;
struct sock;
struct sockaddr;
struct socket;
+struct flowi;
+struct dst_entry;
+struct xfrm_selector;
+struct xfrm_policy;
+struct xfrm_state;
+struct xfrm_user_sec_ctx;
extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb);
extern int cap_netlink_recv(struct sk_buff *skb);
@@ -788,6 +794,52 @@ struct swap_info_struct;
* which is used to copy security attributes between local stream sockets.
* @sk_free_security:
* Deallocate security structure.
+ * @sk_getsid:
+ * Retrieve the LSM-specific sid for the sock to enable caching of network
+ * authorizations.
+ *
+ * Security hooks for XFRM operations.
+ *
+ * @xfrm_policy_alloc_security:
+ * @xp contains the xfrm_policy being added to Security Policy Database
+ * used by the XFRM system.
+ * @sec_ctx contains the security context information being provided by
+ * the user-level policy update program (e.g., setkey).
+ * Allocate a security structure to the xp->selector.security field.
+ * The security field is initialized to NULL when the xfrm_policy is
+ * allocated.
+ * Return 0 if operation was successful (memory to allocate, legal context)
+ * @xfrm_policy_clone_security:
+ * @old contains an existing xfrm_policy in the SPD.
+ * @new contains a new xfrm_policy being cloned from old.
+ * Allocate a security structure to the new->selector.security field
+ * that contains the information from the old->selector.security field.
+ * Return 0 if operation was successful (memory to allocate).
+ * @xfrm_policy_free_security:
+ * @xp contains the xfrm_policy
+ * Deallocate xp->selector.security.
+ * @xfrm_state_alloc_security:
+ * @x contains the xfrm_state being added to the Security Association
+ * Database by the XFRM system.
+ * @sec_ctx contains the security context information being provided by
+ * the user-level SA generation program (e.g., setkey or racoon).
+ * Allocate a security structure to the x->sel.security field. The
+ * security field is initialized to NULL when the xfrm_state is
+ * allocated.
+ * Return 0 if operation was successful (memory to allocate, legal context).
+ * @xfrm_state_free_security:
+ * @x contains the xfrm_state.
+ * Deallocate x>sel.security.
+ * @xfrm_policy_lookup:
+ * @xp contains the xfrm_policy for which the access control is being
+ * checked.
+ * @sk_sid contains the sock security label that is used to authorize
+ * access to the policy xp.
+ * @dir contains the direction of the flow (input or output).
+ * Check permission when a sock selects a xfrm_policy for processing
+ * XFRMs on a packet. The hook is called when selecting either a
+ * per-socket policy or a generic xfrm policy.
+ * Return 0 if permission is granted.
*
* Security hooks affecting all Key Management operations
*
@@ -1237,8 +1289,18 @@ struct security_operations {
int (*socket_getpeersec) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
void (*sk_free_security) (struct sock *sk);
+ unsigned int (*sk_getsid) (struct sock *sk, struct flowi *fl, u8 dir);
#endif /* CONFIG_SECURITY_NETWORK */
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+ int (*xfrm_policy_alloc_security) (struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx);
+ int (*xfrm_policy_clone_security) (struct xfrm_policy *old, struct xfrm_policy *new);
+ void (*xfrm_policy_free_security) (struct xfrm_policy *xp);
+ int (*xfrm_state_alloc_security) (struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx);
+ void (*xfrm_state_free_security) (struct xfrm_state *x);
+ int (*xfrm_policy_lookup)(struct xfrm_policy *xp, u32 sk_sid, u8 dir);
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
+
/* key management security hooks */
#ifdef CONFIG_KEYS
int (*key_alloc)(struct key *key);
@@ -2679,6 +2741,11 @@ static inline void security_sk_free(struct sock *sk)
{
return security_ops->sk_free_security(sk);
}
+
+static inline unsigned int security_sk_sid(struct sock *sk, struct flowi *fl, u8 dir)
+{
+ return security_ops->sk_getsid(sk, fl, dir);
+}
#else /* CONFIG_SECURITY_NETWORK */
static inline int security_unix_stream_connect(struct socket * sock,
struct socket * other,
@@ -2795,8 +2862,73 @@ static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
static inline void security_sk_free(struct sock *sk)
{
}
+
+static inline unsigned int security_sk_sid(struct sock *sk, struct flowi *fl, u8 dir)
+{
+ return 0;
+}
#endif /* CONFIG_SECURITY_NETWORK */
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+static inline int security_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx)
+{
+ return security_ops->xfrm_policy_alloc_security(xp, sec_ctx);
+}
+
+static inline int security_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new)
+{
+ return security_ops->xfrm_policy_clone_security(old, new);
+}
+
+static inline void security_xfrm_policy_free(struct xfrm_policy *xp)
+{
+ security_ops->xfrm_policy_free_security(xp);
+}
+
+static inline int security_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx)
+{
+ return security_ops->xfrm_state_alloc_security(x, sec_ctx);
+}
+
+static inline void security_xfrm_state_free(struct xfrm_state *x)
+{
+ security_ops->xfrm_state_free_security(x);
+}
+
+static inline int security_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
+{
+ return security_ops->xfrm_policy_lookup(xp, sk_sid, dir);
+}
+#else /* CONFIG_SECURITY_NETWORK_XFRM */
+static inline int security_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx)
+{
+ return 0;
+}
+
+static inline int security_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new)
+{
+ return 0;
+}
+
+static inline void security_xfrm_policy_free(struct xfrm_policy *xp)
+{
+}
+
+static inline int security_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx)
+{
+ return 0;
+}
+
+static inline void security_xfrm_state_free(struct xfrm_state *x)
+{
+}
+
+static inline int security_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
+{
+ return 0;
+}
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
+
#ifdef CONFIG_KEYS
#ifdef CONFIG_SECURITY
static inline int security_key_alloc(struct key *key)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 8c5d6001a923..483cfc47ec34 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -32,7 +32,6 @@
#define HAVE_ALLOC_SKB /* For the drivers to know */
#define HAVE_ALIGNABLE_SKB /* Ditto 8) */
-#define SLAB_SKB /* Slabified skbuffs */
#define CHECKSUM_NONE 0
#define CHECKSUM_HW 1
@@ -134,7 +133,7 @@ struct skb_frag_struct {
*/
struct skb_shared_info {
atomic_t dataref;
- unsigned int nr_frags;
+ unsigned short nr_frags;
unsigned short tso_size;
unsigned short tso_segs;
unsigned short ufo_size;
@@ -1239,6 +1238,8 @@ extern int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
int hlen,
struct iovec *iov);
extern void skb_free_datagram(struct sock *sk, struct sk_buff *skb);
+extern void skb_kill_datagram(struct sock *sk, struct sk_buff *skb,
+ unsigned int flags);
extern unsigned int skb_checksum(const struct sk_buff *skb, int offset,
int len, unsigned int csum);
extern int skb_copy_bits(const struct sk_buff *skb, int offset,
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 1739c2d5b95b..9f4019156fd8 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -27,7 +27,6 @@ struct __kernel_sockaddr_storage {
#include <linux/compiler.h> /* __user */
extern int sysctl_somaxconn;
-extern void sock_init(void);
#ifdef CONFIG_PROC_FS
struct seq_file;
extern void socket_seq_show(struct seq_file *seq);
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 4be34ef8c2f7..93fa765e47d3 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -390,6 +390,7 @@ enum
NET_IPV4_ICMP_ERRORS_USE_INBOUND_IFADDR=109,
NET_TCP_CONG_CONTROL=110,
NET_TCP_ABC=111,
+ NET_IPV4_IPFRAG_MAX_DIST=112,
};
enum {
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 0e1da6602e05..f2bb2396853f 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -55,22 +55,6 @@ struct tcphdr {
__u16 urg_ptr;
};
-#define TCP_ACTION_FIN (1 << 7)
-
-enum {
- TCPF_ESTABLISHED = (1 << 1),
- TCPF_SYN_SENT = (1 << 2),
- TCPF_SYN_RECV = (1 << 3),
- TCPF_FIN_WAIT1 = (1 << 4),
- TCPF_FIN_WAIT2 = (1 << 5),
- TCPF_TIME_WAIT = (1 << 6),
- TCPF_CLOSE = (1 << 7),
- TCPF_CLOSE_WAIT = (1 << 8),
- TCPF_LAST_ACK = (1 << 9),
- TCPF_LISTEN = (1 << 10),
- TCPF_CLOSING = (1 << 11)
-};
-
/*
* The union cast uses a gcc extension to avoid aliasing problems
* (union is compatible to any of its members)
@@ -254,10 +238,9 @@ struct tcp_sock {
__u32 snd_wl1; /* Sequence for window update */
__u32 snd_wnd; /* The window we expect to receive */
__u32 max_window; /* Maximal window ever seen from peer */
- __u32 pmtu_cookie; /* Last pmtu seen by socket */
__u32 mss_cache; /* Cached effective mss, not including SACKS */
__u16 xmit_size_goal; /* Goal for segmenting output packets */
- __u16 ext_header_len; /* Network protocol overhead (IP/IPv6 options) */
+ /* XXX Two bytes hole, try to pack */
__u32 window_clamp; /* Maximal window to advertise */
__u32 rcv_ssthresh; /* Current window clamp */
@@ -295,8 +278,6 @@ struct tcp_sock {
struct sk_buff_head out_of_order_queue; /* Out of order segments go here */
- struct tcp_func *af_specific; /* Operations which are AF_INET{4,6} specific */
-
__u32 rcv_wnd; /* Current receiver window */
__u32 rcv_wup; /* rcv_nxt on last window update sent */
__u32 write_seq; /* Tail(+1) of data held in tcp send buffer */
diff --git a/include/linux/udp.h b/include/linux/udp.h
index b60e0b4a25c4..85a55658831c 100644
--- a/include/linux/udp.h
+++ b/include/linux/udp.h
@@ -35,10 +35,10 @@ struct udphdr {
#define UDP_ENCAP_ESPINUDP 2 /* draft-ietf-ipsec-udp-encaps-06 */
#ifdef __KERNEL__
-
#include <linux/config.h>
-#include <net/sock.h>
-#include <linux/ip.h>
+#include <linux/types.h>
+
+#include <net/inet_sock.h>
struct udp_sock {
/* inet_sock has to be the first member */
diff --git a/include/linux/usb.h b/include/linux/usb.h
index d81b050e5955..e59d1bd52d4f 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -329,8 +329,6 @@ struct usb_device {
struct usb_tt *tt; /* low/full speed dev, highspeed hub */
int ttport; /* device port on that tt hub */
- struct semaphore serialize;
-
unsigned int toggle[2]; /* one bit for each endpoint
* ([0] = IN, [1] = OUT) */
@@ -349,6 +347,9 @@ struct usb_device {
char **rawdescriptors; /* Raw descriptors for each config */
+ unsigned short bus_mA; /* Current available from the bus */
+ u8 portnum; /* Parent port number (origin 1) */
+
int have_langid; /* whether string_langid is valid */
int string_langid; /* language ID for strings */
@@ -377,11 +378,12 @@ struct usb_device {
extern struct usb_device *usb_get_dev(struct usb_device *dev);
extern void usb_put_dev(struct usb_device *dev);
-extern void usb_lock_device(struct usb_device *udev);
-extern int usb_trylock_device(struct usb_device *udev);
+/* USB device locking */
+#define usb_lock_device(udev) down(&(udev)->dev.sem)
+#define usb_unlock_device(udev) up(&(udev)->dev.sem)
+#define usb_trylock_device(udev) down_trylock(&(udev)->dev.sem)
extern int usb_lock_device_for_reset(struct usb_device *udev,
struct usb_interface *iface);
-extern void usb_unlock_device(struct usb_device *udev);
/* USB port reset for device reinitialization */
extern int usb_reset_device(struct usb_device *dev);
@@ -529,10 +531,13 @@ static inline int usb_make_path (struct usb_device *dev, char *buf,
/* ----------------------------------------------------------------------- */
+struct usb_dynids {
+ spinlock_t lock;
+ struct list_head list;
+};
+
/**
* struct usb_driver - identifies USB driver to usbcore
- * @owner: Pointer to the module owner of this driver; initialize
- * it using THIS_MODULE.
* @name: The driver name should be unique among USB drivers,
* and should normally be the same as the module name.
* @probe: Called to see if the driver is willing to manage a particular
@@ -553,7 +558,11 @@ static inline int usb_make_path (struct usb_device *dev, char *buf,
* @id_table: USB drivers use ID table to support hotplugging.
* Export this with MODULE_DEVICE_TABLE(usb,...). This must be set
* or your driver's probe function will never get called.
+ * @dynids: used internally to hold the list of dynamically added device
+ * ids for this driver.
* @driver: the driver model core driver structure.
+ * @no_dynamic_id: if set to 1, the USB core will not allow dynamic ids to be
+ * added to this driver by preventing the sysfs file from being created.
*
* USB drivers must provide a name, probe() and disconnect() methods,
* and an id_table. Other driver fields are optional.
@@ -571,8 +580,6 @@ static inline int usb_make_path (struct usb_device *dev, char *buf,
* them as necessary, and blocking until the unlinks complete).
*/
struct usb_driver {
- struct module *owner;
-
const char *name;
int (*probe) (struct usb_interface *intf,
@@ -588,7 +595,9 @@ struct usb_driver {
const struct usb_device_id *id_table;
+ struct usb_dynids dynids;
struct device_driver driver;
+ unsigned int no_dynamic_id:1;
};
#define to_usb_driver(d) container_of(d, struct usb_driver, driver)
@@ -614,7 +623,11 @@ struct usb_class_driver {
* use these in module_init()/module_exit()
* and don't forget MODULE_DEVICE_TABLE(usb, ...)
*/
-extern int usb_register(struct usb_driver *);
+int usb_register_driver(struct usb_driver *, struct module *);
+static inline int usb_register(struct usb_driver *driver)
+{
+ return usb_register_driver(driver, THIS_MODULE);
+}
extern void usb_deregister(struct usb_driver *);
extern int usb_register_dev(struct usb_interface *intf,
diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
new file mode 100644
index 000000000000..b2d08984a9f7
--- /dev/null
+++ b/include/linux/usb_usual.h
@@ -0,0 +1,126 @@
+/*
+ * Interface to the libusual.
+ *
+ * Copyright (c) 2005 Pete Zaitcev <zaitcev@redhat.com>
+ * Copyright (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ * Copyright (c) 1999 Michael Gee (michael@linuxspecific.com)
+ */
+
+#ifndef __LINUX_USB_USUAL_H
+#define __LINUX_USB_USUAL_H
+
+#include <linux/config.h>
+
+/* We should do this for cleanliness... But other usb_foo.h do not do this. */
+/* #include <linux/usb.h> */
+
+/*
+ * The flags field, which we store in usb_device_id.driver_info.
+ * It is compatible with the old usb-storage flags in lower 24 bits.
+ */
+
+/*
+ * Static flag definitions. We use this roundabout technique so that the
+ * proc_info() routine can automatically display a message for each flag.
+ */
+#define US_DO_ALL_FLAGS \
+ US_FLAG(SINGLE_LUN, 0x00000001) \
+ /* allow access to only LUN 0 */ \
+ US_FLAG(NEED_OVERRIDE, 0x00000002) \
+ /* unusual_devs entry is necessary */ \
+ US_FLAG(SCM_MULT_TARG, 0x00000004) \
+ /* supports multiple targets */ \
+ US_FLAG(FIX_INQUIRY, 0x00000008) \
+ /* INQUIRY response needs faking */ \
+ US_FLAG(FIX_CAPACITY, 0x00000010) \
+ /* READ CAPACITY response too big */ \
+ US_FLAG(IGNORE_RESIDUE, 0x00000020) \
+ /* reported residue is wrong */ \
+ US_FLAG(BULK32, 0x00000040) \
+ /* Uses 32-byte CBW length */ \
+ US_FLAG(NOT_LOCKABLE, 0x00000080) \
+ /* PREVENT/ALLOW not supported */ \
+ US_FLAG(GO_SLOW, 0x00000100) \
+ /* Need delay after Command phase */ \
+ US_FLAG(NO_WP_DETECT, 0x00000200) \
+ /* Don't check for write-protect */ \
+
+#define US_FLAG(name, value) US_FL_##name = value ,
+enum { US_DO_ALL_FLAGS };
+#undef US_FLAG
+
+/*
+ * The bias field for libusual and friends.
+ */
+#define USB_US_TYPE_NONE 0
+#define USB_US_TYPE_STOR 1 /* usb-storage */
+#define USB_US_TYPE_UB 2 /* ub */
+
+#define USB_US_TYPE(flags) (((flags) >> 24) & 0xFF)
+#define USB_US_ORIG_FLAGS(flags) ((flags) & 0x00FFFFFF)
+
+/*
+ * This is probably not the best place to keep these constants, conceptually.
+ * But it's the only header included into all places which need them.
+ */
+
+/* Sub Classes */
+
+#define US_SC_RBC 0x01 /* Typically, flash devices */
+#define US_SC_8020 0x02 /* CD-ROM */
+#define US_SC_QIC 0x03 /* QIC-157 Tapes */
+#define US_SC_UFI 0x04 /* Floppy */
+#define US_SC_8070 0x05 /* Removable media */
+#define US_SC_SCSI 0x06 /* Transparent */
+#define US_SC_ISD200 0x07 /* ISD200 ATA */
+#define US_SC_MIN US_SC_RBC
+#define US_SC_MAX US_SC_ISD200
+
+#define US_SC_DEVICE 0xff /* Use device's value */
+
+/* Protocols */
+
+#define US_PR_CBI 0x00 /* Control/Bulk/Interrupt */
+#define US_PR_CB 0x01 /* Control/Bulk w/o interrupt */
+#define US_PR_BULK 0x50 /* bulk only */
+#ifdef CONFIG_USB_STORAGE_USBAT
+#define US_PR_USBAT 0x80 /* SCM-ATAPI bridge */
+#endif
+#ifdef CONFIG_USB_STORAGE_SDDR09
+#define US_PR_EUSB_SDDR09 0x81 /* SCM-SCSI bridge for SDDR-09 */
+#endif
+#ifdef CONFIG_USB_STORAGE_SDDR55
+#define US_PR_SDDR55 0x82 /* SDDR-55 (made up) */
+#endif
+#define US_PR_DPCM_USB 0xf0 /* Combination CB/SDDR09 */
+#ifdef CONFIG_USB_STORAGE_FREECOM
+#define US_PR_FREECOM 0xf1 /* Freecom */
+#endif
+#ifdef CONFIG_USB_STORAGE_DATAFAB
+#define US_PR_DATAFAB 0xf2 /* Datafab chipsets */
+#endif
+#ifdef CONFIG_USB_STORAGE_JUMPSHOT
+#define US_PR_JUMPSHOT 0xf3 /* Lexar Jumpshot */
+#endif
+#ifdef CONFIG_USB_STORAGE_ALAUDA
+#define US_PR_ALAUDA 0xf4 /* Alauda chipsets */
+#endif
+
+#define US_PR_DEVICE 0xff /* Use device's value */
+
+/*
+ */
+#ifdef CONFIG_USB_LIBUSUAL
+
+extern struct usb_device_id storage_usb_ids[];
+extern void usb_usual_set_present(int type);
+extern void usb_usual_clear_present(int type);
+extern int usb_usual_check_type(const struct usb_device_id *, int type);
+#else
+
+#define usb_usual_set_present(t) do { } while(0)
+#define usb_usual_clear_present(t) do { } while(0)
+#define usb_usual_check_type(id, t) (0)
+#endif /* CONFIG_USB_LIBUSUAL */
+
+#endif /* __LINUX_USB_USUAL_H */
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index 0fb077d68441..82fbb758e28f 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -27,6 +27,22 @@ struct xfrm_id
__u8 proto;
};
+struct xfrm_sec_ctx {
+ __u8 ctx_doi;
+ __u8 ctx_alg;
+ __u16 ctx_len;
+ __u32 ctx_sid;
+ char ctx_str[0];
+};
+
+/* Security Context Domains of Interpretation */
+#define XFRM_SC_DOI_RESERVED 0
+#define XFRM_SC_DOI_LSM 1
+
+/* Security Context Algorithms */
+#define XFRM_SC_ALG_RESERVED 0
+#define XFRM_SC_ALG_SELINUX 1
+
/* Selector, used as selector both on policy rules (SPD) and SAs. */
struct xfrm_selector
@@ -146,6 +162,18 @@ enum {
#define XFRM_NR_MSGTYPES (XFRM_MSG_MAX + 1 - XFRM_MSG_BASE)
+/*
+ * Generic LSM security context for comunicating to user space
+ * NOTE: Same format as sadb_x_sec_ctx
+ */
+struct xfrm_user_sec_ctx {
+ __u16 len;
+ __u16 exttype;
+ __u8 ctx_alg; /* LSMs: e.g., selinux == 1 */
+ __u8 ctx_doi;
+ __u16 ctx_len;
+};
+
struct xfrm_user_tmpl {
struct xfrm_id id;
__u16 family;
@@ -176,6 +204,7 @@ enum xfrm_attr_type_t {
XFRMA_TMPL, /* 1 or more struct xfrm_user_tmpl */
XFRMA_SA,
XFRMA_POLICY,
+ XFRMA_SEC_CTX, /* struct xfrm_sec_ctx */
__XFRMA_MAX
#define XFRMA_MAX (__XFRMA_MAX - 1)
diff --git a/include/net/af_unix.h b/include/net/af_unix.h
index b5d785ab4a0e..bfc1779fc753 100644
--- a/include/net/af_unix.h
+++ b/include/net/af_unix.h
@@ -13,7 +13,7 @@ extern void unix_gc(void);
#define UNIX_HASH_SIZE 256
extern struct hlist_head unix_socket_table[UNIX_HASH_SIZE + 1];
-extern rwlock_t unix_table_lock;
+extern spinlock_t unix_table_lock;
extern atomic_t unix_tot_inflight;
@@ -58,10 +58,10 @@ struct unix_skb_parms {
#define UNIXCB(skb) (*(struct unix_skb_parms*)&((skb)->cb))
#define UNIXCREDS(skb) (&UNIXCB((skb)).creds)
-#define unix_state_rlock(s) read_lock(&unix_sk(s)->lock)
-#define unix_state_runlock(s) read_unlock(&unix_sk(s)->lock)
-#define unix_state_wlock(s) write_lock(&unix_sk(s)->lock)
-#define unix_state_wunlock(s) write_unlock(&unix_sk(s)->lock)
+#define unix_state_rlock(s) spin_lock(&unix_sk(s)->lock)
+#define unix_state_runlock(s) spin_unlock(&unix_sk(s)->lock)
+#define unix_state_wlock(s) spin_lock(&unix_sk(s)->lock)
+#define unix_state_wunlock(s) spin_unlock(&unix_sk(s)->lock)
#ifdef __KERNEL__
/* The AF_UNIX socket */
@@ -76,7 +76,7 @@ struct unix_sock {
struct sock *other;
struct sock *gc_tree;
atomic_t inflight;
- rwlock_t lock;
+ spinlock_t lock;
wait_queue_head_t peer_wait;
};
#define unix_sk(__sk) ((struct unix_sock *)__sk)
diff --git a/include/net/atmclip.h b/include/net/atmclip.h
index 47048b1d179a..90fcc98e676f 100644
--- a/include/net/atmclip.h
+++ b/include/net/atmclip.h
@@ -7,7 +7,6 @@
#define _ATMCLIP_H
#include <linux/netdevice.h>
-#include <linux/skbuff.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/atmarp.h>
@@ -18,6 +17,7 @@
#define CLIP_VCC(vcc) ((struct clip_vcc *) ((vcc)->user_back))
#define NEIGH2ENTRY(neigh) ((struct atmarp_entry *) (neigh)->primary_key)
+struct sk_buff;
struct clip_vcc {
struct atm_vcc *vcc; /* VCC descriptor */
diff --git a/include/net/dst.h b/include/net/dst.h
index 6c196a5baf24..bee8b84d329d 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -9,6 +9,7 @@
#define _NET_DST_H
#include <linux/config.h>
+#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/rcupdate.h>
#include <linux/jiffies.h>
diff --git a/include/net/flow.h b/include/net/flow.h
index 9a5c94b1a0ec..ec7eb86eb203 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -84,11 +84,12 @@ struct flowi {
#define FLOW_DIR_OUT 1
#define FLOW_DIR_FWD 2
-typedef void (*flow_resolve_t)(struct flowi *key, u16 family, u8 dir,
+struct sock;
+typedef void (*flow_resolve_t)(struct flowi *key, u32 sk_sid, u16 family, u8 dir,
void **objp, atomic_t **obj_refp);
-extern void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
- flow_resolve_t resolver);
+extern void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir,
+ flow_resolve_t resolver);
extern void flow_cache_flush(void);
extern atomic_t flow_cache_genid;
diff --git a/include/net/genetlink.h b/include/net/genetlink.h
index 52d8b1a73d52..c5b96b2b8155 100644
--- a/include/net/genetlink.h
+++ b/include/net/genetlink.h
@@ -60,7 +60,7 @@ struct genl_info
*/
struct genl_ops
{
- unsigned int cmd;
+ u8 cmd;
unsigned int flags;
struct nla_policy *policy;
int (*doit)(struct sk_buff *skb,
diff --git a/include/net/icmp.h b/include/net/icmp.h
index 6cdebeee5f96..e7c3f20fbafc 100644
--- a/include/net/icmp.h
+++ b/include/net/icmp.h
@@ -20,12 +20,9 @@
#include <linux/config.h>
#include <linux/icmp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/protocol.h>
+#include <net/inet_sock.h>
#include <net/snmp.h>
-#include <linux/ip.h>
struct icmp_err {
int errno;
@@ -38,6 +35,10 @@ DECLARE_SNMP_STAT(struct icmp_mib, icmp_statistics);
#define ICMP_INC_STATS_BH(field) SNMP_INC_STATS_BH(icmp_statistics, field)
#define ICMP_INC_STATS_USER(field) SNMP_INC_STATS_USER(icmp_statistics, field)
+struct dst_entry;
+struct net_proto_family;
+struct sk_buff;
+
extern void icmp_send(struct sk_buff *skb_in, int type, int code, u32 info);
extern int icmp_rcv(struct sk_buff *skb);
extern int icmp_ioctl(struct sock *sk, int cmd, unsigned long arg);
diff --git a/include/net/ieee80211_crypt.h b/include/net/ieee80211_crypt.h
index 225fc751d464..03b766afdc39 100644
--- a/include/net/ieee80211_crypt.h
+++ b/include/net/ieee80211_crypt.h
@@ -23,12 +23,17 @@
#ifndef IEEE80211_CRYPT_H
#define IEEE80211_CRYPT_H
-#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <asm/atomic.h>
enum {
IEEE80211_CRYPTO_TKIP_COUNTERMEASURES = (1 << 0),
};
+struct sk_buff;
+struct module;
+
struct ieee80211_crypto_ops {
const char *name;
struct list_head list;
@@ -87,6 +92,8 @@ struct ieee80211_crypt_data {
atomic_t refcnt;
};
+struct ieee80211_device;
+
int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops);
int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops);
struct ieee80211_crypto_ops *ieee80211_get_crypto_ops(const char *name);
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index e97a9accb71d..eb8afe3499a9 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -24,6 +24,7 @@
#define IF_RA_MANAGED 0x40
#define IF_RA_RCVD 0x20
#define IF_RS_SENT 0x10
+#define IF_READY 0x80000000
/* prefix flags */
#define IF_PREFIX_ONLINK 0x01
@@ -82,6 +83,7 @@ struct ipv6_mc_socklist
struct in6_addr addr;
int ifindex;
struct ipv6_mc_socklist *next;
+ rwlock_t sflock;
unsigned int sfmode; /* MCAST_{INCLUDE,EXCLUDE} */
struct ip6_sf_socklist *sflist;
};
diff --git a/include/net/inet6_connection_sock.h b/include/net/inet6_connection_sock.h
new file mode 100644
index 000000000000..b33b438bffcc
--- /dev/null
+++ b/include/net/inet6_connection_sock.h
@@ -0,0 +1,42 @@
+/*
+ * NET Generic infrastructure for INET6 connection oriented protocols.
+ *
+ * Authors: Many people, see the TCPv6 sources
+ *
+ * From code originally in TCPv6
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _INET6_CONNECTION_SOCK_H
+#define _INET6_CONNECTION_SOCK_H
+
+#include <linux/types.h>
+
+struct in6_addr;
+struct inet_bind_bucket;
+struct request_sock;
+struct sk_buff;
+struct sock;
+struct sockaddr;
+
+extern int inet6_csk_bind_conflict(const struct sock *sk,
+ const struct inet_bind_bucket *tb);
+
+extern struct request_sock *inet6_csk_search_req(const struct sock *sk,
+ struct request_sock ***prevp,
+ const __u16 rport,
+ const struct in6_addr *raddr,
+ const struct in6_addr *laddr,
+ const int iif);
+
+extern void inet6_csk_reqsk_queue_hash_add(struct sock *sk,
+ struct request_sock *req,
+ const unsigned long timeout);
+
+extern void inet6_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr);
+
+extern int inet6_csk_xmit(struct sk_buff *skb, int ipfragok);
+#endif /* _INET6_CONNECTION_SOCK_H */
diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h
index 5a2beed5a770..25f708ff020e 100644
--- a/include/net/inet6_hashtables.h
+++ b/include/net/inet6_hashtables.h
@@ -48,6 +48,32 @@ static inline int inet6_sk_ehashfn(const struct sock *sk)
return inet6_ehashfn(laddr, lport, faddr, fport);
}
+static inline void __inet6_hash(struct inet_hashinfo *hashinfo,
+ struct sock *sk)
+{
+ struct hlist_head *list;
+ rwlock_t *lock;
+
+ BUG_TRAP(sk_unhashed(sk));
+
+ if (sk->sk_state == TCP_LISTEN) {
+ list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
+ lock = &hashinfo->lhash_lock;
+ inet_listen_wlock(hashinfo);
+ } else {
+ unsigned int hash;
+ sk->sk_hash = hash = inet6_sk_ehashfn(sk);
+ hash &= (hashinfo->ehash_size - 1);
+ list = &hashinfo->ehash[hash].chain;
+ lock = &hashinfo->ehash[hash].lock;
+ write_lock(lock);
+ }
+
+ __sk_add_node(sk, list);
+ sock_prot_inc_use(sk->sk_prot);
+ write_unlock(lock);
+}
+
/*
* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
* we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
@@ -84,10 +110,10 @@ static inline struct sock *
if(*((__u32 *)&(tw->tw_dport)) == ports &&
sk->sk_family == PF_INET6) {
- const struct tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk);
+ const struct inet6_timewait_sock *tw6 = inet6_twsk(sk);
- if (ipv6_addr_equal(&tcp6tw->tw_v6_daddr, saddr) &&
- ipv6_addr_equal(&tcp6tw->tw_v6_rcv_saddr, daddr) &&
+ if (ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
+ ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
(!sk->sk_bound_dev_if || sk->sk_bound_dev_if == dif))
goto hit;
}
diff --git a/include/net/inet_common.h b/include/net/inet_common.h
index f943306ce5ff..227adcbdfec8 100644
--- a/include/net/inet_common.h
+++ b/include/net/inet_common.h
@@ -1,8 +1,8 @@
#ifndef _INET_COMMON_H
#define _INET_COMMON_H
-extern struct proto_ops inet_stream_ops;
-extern struct proto_ops inet_dgram_ops;
+extern const struct proto_ops inet_stream_ops;
+extern const struct proto_ops inet_dgram_ops;
/*
* INET4 prototypes used by INET6
diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
index b0c99060b78d..50234fa56a68 100644
--- a/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -15,9 +15,11 @@
#ifndef _INET_CONNECTION_SOCK_H
#define _INET_CONNECTION_SOCK_H
-#include <linux/ip.h>
+#include <linux/compiler.h>
#include <linux/string.h>
#include <linux/timer.h>
+
+#include <net/inet_sock.h>
#include <net/request_sock.h>
#define INET_CSK_DEBUG 1
@@ -29,6 +31,29 @@ struct inet_bind_bucket;
struct inet_hashinfo;
struct tcp_congestion_ops;
+/*
+ * Pointers to address related TCP functions
+ * (i.e. things that depend on the address family)
+ */
+struct inet_connection_sock_af_ops {
+ int (*queue_xmit)(struct sk_buff *skb, int ipfragok);
+ void (*send_check)(struct sock *sk, int len,
+ struct sk_buff *skb);
+ int (*rebuild_header)(struct sock *sk);
+ int (*conn_request)(struct sock *sk, struct sk_buff *skb);
+ struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req,
+ struct dst_entry *dst);
+ int (*remember_stamp)(struct sock *sk);
+ __u16 net_header_len;
+ int (*setsockopt)(struct sock *sk, int level, int optname,
+ char __user *optval, int optlen);
+ int (*getsockopt)(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen);
+ void (*addr2sockaddr)(struct sock *sk, struct sockaddr *);
+ int sockaddr_len;
+};
+
/** inet_connection_sock - INET connection oriented sock
*
* @icsk_accept_queue: FIFO of established children
@@ -36,13 +61,16 @@ struct tcp_congestion_ops;
* @icsk_timeout: Timeout
* @icsk_retransmit_timer: Resend (no ack)
* @icsk_rto: Retransmit timeout
+ * @icsk_pmtu_cookie Last pmtu seen by socket
* @icsk_ca_ops Pluggable congestion control hook
+ * @icsk_af_ops Operations which are AF_INET{4,6} specific
* @icsk_ca_state: Congestion control state
* @icsk_retransmits: Number of unrecovered [RTO] timeouts
* @icsk_pending: Scheduled timer event
* @icsk_backoff: Backoff
* @icsk_syn_retries: Number of allowed SYN (or equivalent) retries
* @icsk_probes_out: unanswered 0 window probes
+ * @icsk_ext_hdr_len: Network protocol overhead (IP/IPv6 options)
* @icsk_ack: Delayed ACK control data
*/
struct inet_connection_sock {
@@ -54,14 +82,17 @@ struct inet_connection_sock {
struct timer_list icsk_retransmit_timer;
struct timer_list icsk_delack_timer;
__u32 icsk_rto;
+ __u32 icsk_pmtu_cookie;
struct tcp_congestion_ops *icsk_ca_ops;
+ struct inet_connection_sock_af_ops *icsk_af_ops;
+ unsigned int (*icsk_sync_mss)(struct sock *sk, u32 pmtu);
__u8 icsk_ca_state;
__u8 icsk_retransmits;
__u8 icsk_pending;
__u8 icsk_backoff;
__u8 icsk_syn_retries;
__u8 icsk_probes_out;
- /* 2 BYTES HOLE, TRY TO PACK! */
+ __u16 icsk_ext_hdr_len;
struct {
__u8 pending; /* ACK is pending */
__u8 quick; /* Scheduled number of quick acks */
@@ -192,8 +223,12 @@ extern struct request_sock *inet_csk_search_req(const struct sock *sk,
const __u16 rport,
const __u32 raddr,
const __u32 laddr);
+extern int inet_csk_bind_conflict(const struct sock *sk,
+ const struct inet_bind_bucket *tb);
extern int inet_csk_get_port(struct inet_hashinfo *hashinfo,
- struct sock *sk, unsigned short snum);
+ struct sock *sk, unsigned short snum,
+ int (*bind_conflict)(const struct sock *sk,
+ const struct inet_bind_bucket *tb));
extern struct dst_entry* inet_csk_route_req(struct sock *sk,
const struct request_sock *req);
@@ -207,7 +242,7 @@ static inline void inet_csk_reqsk_queue_add(struct sock *sk,
extern void inet_csk_reqsk_queue_hash_add(struct sock *sk,
struct request_sock *req,
- const unsigned timeout);
+ unsigned long timeout);
static inline void inet_csk_reqsk_queue_removed(struct sock *sk,
struct request_sock *req)
@@ -273,4 +308,6 @@ static inline unsigned int inet_csk_listen_poll(const struct sock *sk)
extern int inet_csk_listen_start(struct sock *sk, const int nr_table_entries);
extern void inet_csk_listen_stop(struct sock *sk);
+extern void inet_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr);
+
#endif /* _INET_CONNECTION_SOCK_H */
diff --git a/include/net/inet_ecn.h b/include/net/inet_ecn.h
index b0c47e2eccf1..d599c6bfbb86 100644
--- a/include/net/inet_ecn.h
+++ b/include/net/inet_ecn.h
@@ -3,6 +3,8 @@
#include <linux/ip.h>
#include <linux/skbuff.h>
+
+#include <net/inet_sock.h>
#include <net/dsfield.h>
enum {
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 07840baa9341..135d80fd658e 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -26,6 +26,7 @@
#include <linux/wait.h>
#include <net/inet_connection_sock.h>
+#include <net/inet_sock.h>
#include <net/route.h>
#include <net/sock.h>
#include <net/tcp_states.h>
@@ -128,26 +129,6 @@ struct inet_hashinfo {
kmem_cache_t *bind_bucket_cachep;
};
-static inline unsigned int inet_ehashfn(const __u32 laddr, const __u16 lport,
- const __u32 faddr, const __u16 fport)
-{
- unsigned int h = (laddr ^ lport) ^ (faddr ^ fport);
- h ^= h >> 16;
- h ^= h >> 8;
- return h;
-}
-
-static inline int inet_sk_ehashfn(const struct sock *sk)
-{
- const struct inet_sock *inet = inet_sk(sk);
- const __u32 laddr = inet->rcv_saddr;
- const __u16 lport = inet->num;
- const __u32 faddr = inet->daddr;
- const __u16 fport = inet->dport;
-
- return inet_ehashfn(laddr, lport, faddr, fport);
-}
-
static inline struct inet_ehash_bucket *inet_ehash_bucket(
struct inet_hashinfo *hashinfo,
unsigned int hash)
@@ -434,4 +415,7 @@ static inline struct sock *inet_lookup(struct inet_hashinfo *hashinfo,
return sk;
}
+
+extern int inet_hash_connect(struct inet_timewait_death_row *death_row,
+ struct sock *sk);
#endif /* _INET_HASHTABLES_H */
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
new file mode 100644
index 000000000000..883eb529ef8e
--- /dev/null
+++ b/include/net/inet_sock.h
@@ -0,0 +1,193 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for inet_sock
+ *
+ * Authors: Many, reorganised here by
+ * Arnaldo Carvalho de Melo <acme@mandriva.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _INET_SOCK_H
+#define _INET_SOCK_H
+
+#include <linux/config.h>
+
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <net/flow.h>
+#include <net/sock.h>
+#include <net/request_sock.h>
+
+/** struct ip_options - IP Options
+ *
+ * @faddr - Saved first hop address
+ * @is_setbyuser - Set by setsockopt?
+ * @is_data - Options in __data, rather than skb
+ * @is_strictroute - Strict source route
+ * @srr_is_hit - Packet destination addr was our one
+ * @is_changed - IP checksum more not valid
+ * @rr_needaddr - Need to record addr of outgoing dev
+ * @ts_needtime - Need to record timestamp
+ * @ts_needaddr - Need to record addr of outgoing dev
+ */
+struct ip_options {
+ __u32 faddr;
+ unsigned char optlen;
+ unsigned char srr;
+ unsigned char rr;
+ unsigned char ts;
+ unsigned char is_setbyuser:1,
+ is_data:1,
+ is_strictroute:1,
+ srr_is_hit:1,
+ is_changed:1,
+ rr_needaddr:1,
+ ts_needtime:1,
+ ts_needaddr:1;
+ unsigned char router_alert;
+ unsigned char __pad1;
+ unsigned char __pad2;
+ unsigned char __data[0];
+};
+
+#define optlength(opt) (sizeof(struct ip_options) + opt->optlen)
+
+struct inet_request_sock {
+ struct request_sock req;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ u16 inet6_rsk_offset;
+ /* 2 bytes hole, try to pack */
+#endif
+ u32 loc_addr;
+ u32 rmt_addr;
+ u16 rmt_port;
+ u16 snd_wscale : 4,
+ rcv_wscale : 4,
+ tstamp_ok : 1,
+ sack_ok : 1,
+ wscale_ok : 1,
+ ecn_ok : 1,
+ acked : 1;
+ struct ip_options *opt;
+};
+
+static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk)
+{
+ return (struct inet_request_sock *)sk;
+}
+
+struct ip_mc_socklist;
+struct ipv6_pinfo;
+struct rtable;
+
+/** struct inet_sock - representation of INET sockets
+ *
+ * @sk - ancestor class
+ * @pinet6 - pointer to IPv6 control block
+ * @daddr - Foreign IPv4 addr
+ * @rcv_saddr - Bound local IPv4 addr
+ * @dport - Destination port
+ * @num - Local port
+ * @saddr - Sending source
+ * @uc_ttl - Unicast TTL
+ * @sport - Source port
+ * @id - ID counter for DF pkts
+ * @tos - TOS
+ * @mc_ttl - Multicasting TTL
+ * @is_icsk - is this an inet_connection_sock?
+ * @mc_index - Multicast device index
+ * @mc_list - Group array
+ * @cork - info to build ip hdr on each ip frag while socket is corked
+ */
+struct inet_sock {
+ /* sk and pinet6 has to be the first two members of inet_sock */
+ struct sock sk;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct ipv6_pinfo *pinet6;
+#endif
+ /* Socket demultiplex comparisons on incoming packets. */
+ __u32 daddr;
+ __u32 rcv_saddr;
+ __u16 dport;
+ __u16 num;
+ __u32 saddr;
+ __s16 uc_ttl;
+ __u16 cmsg_flags;
+ struct ip_options *opt;
+ __u16 sport;
+ __u16 id;
+ __u8 tos;
+ __u8 mc_ttl;
+ __u8 pmtudisc;
+ __u8 recverr:1,
+ is_icsk:1,
+ freebind:1,
+ hdrincl:1,
+ mc_loop:1;
+ int mc_index;
+ __u32 mc_addr;
+ struct ip_mc_socklist *mc_list;
+ struct {
+ unsigned int flags;
+ unsigned int fragsize;
+ struct ip_options *opt;
+ struct rtable *rt;
+ int length; /* Total length of all frames */
+ u32 addr;
+ struct flowi fl;
+ } cork;
+};
+
+#define IPCORK_OPT 1 /* ip-options has been held in ipcork.opt */
+#define IPCORK_ALLFRAG 2 /* always fragment (for ipv6 for now) */
+
+static inline struct inet_sock *inet_sk(const struct sock *sk)
+{
+ return (struct inet_sock *)sk;
+}
+
+static inline void __inet_sk_copy_descendant(struct sock *sk_to,
+ const struct sock *sk_from,
+ const int ancestor_size)
+{
+ memcpy(inet_sk(sk_to) + 1, inet_sk(sk_from) + 1,
+ sk_from->sk_prot->obj_size - ancestor_size);
+}
+#if !(defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE))
+static inline void inet_sk_copy_descendant(struct sock *sk_to,
+ const struct sock *sk_from)
+{
+ __inet_sk_copy_descendant(sk_to, sk_from, sizeof(struct inet_sock));
+}
+#endif
+
+extern int inet_sk_rebuild_header(struct sock *sk);
+
+static inline unsigned int inet_ehashfn(const __u32 laddr, const __u16 lport,
+ const __u32 faddr, const __u16 fport)
+{
+ unsigned int h = (laddr ^ lport) ^ (faddr ^ fport);
+ h ^= h >> 16;
+ h ^= h >> 8;
+ return h;
+}
+
+static inline int inet_sk_ehashfn(const struct sock *sk)
+{
+ const struct inet_sock *inet = inet_sk(sk);
+ const __u32 laddr = inet->rcv_saddr;
+ const __u16 lport = inet->num;
+ const __u32 faddr = inet->daddr;
+ const __u16 fport = inet->dport;
+
+ return inet_ehashfn(laddr, lport, faddr, fport);
+}
+
+#endif /* _INET_SOCK_H */
diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h
index 28f7b2103505..1da294c47522 100644
--- a/include/net/inet_timewait_sock.h
+++ b/include/net/inet_timewait_sock.h
@@ -17,15 +17,16 @@
#include <linux/config.h>
-#include <linux/ip.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/workqueue.h>
+#include <net/inet_sock.h>
#include <net/sock.h>
#include <net/tcp_states.h>
+#include <net/timewait_sock.h>
#include <asm/atomic.h>
@@ -127,7 +128,8 @@ struct inet_timewait_sock {
__u16 tw_num;
/* And these are ours. */
__u8 tw_ipv6only:1;
- /* 31 bits hole, try to pack */
+ /* 15 bits hole, try to pack */
+ __u16 tw_ipv6_offset;
int tw_timeout;
unsigned long tw_ttd;
struct inet_bind_bucket *tw_tb;
@@ -199,7 +201,7 @@ static inline void inet_twsk_put(struct inet_timewait_sock *tw)
printk(KERN_DEBUG "%s timewait_sock %p released\n",
tw->tw_prot->name, tw);
#endif
- kmem_cache_free(tw->tw_prot->twsk_slab, tw);
+ kmem_cache_free(tw->tw_prot->twsk_prot->twsk_slab, tw);
module_put(owner);
}
}
diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h
index 7fda471002b6..0965515f40cf 100644
--- a/include/net/inetpeer.h
+++ b/include/net/inetpeer.h
@@ -25,6 +25,7 @@ struct inet_peer
__u32 v4daddr; /* peer's address */
__u16 avl_height;
__u16 ip_id_count; /* IP ID for the next packet */
+ atomic_t rid; /* Frag reception counter */
__u32 tcp_ts;
unsigned long tcp_ts_stamp;
};
diff --git a/include/net/ip.h b/include/net/ip.h
index e4563bbee6ea..f7e7fd728b67 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -24,14 +24,10 @@
#include <linux/config.h>
#include <linux/types.h>
-#include <linux/socket.h>
#include <linux/ip.h>
#include <linux/in.h>
-#include <linux/netdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/in_route.h>
-#include <net/route.h>
-#include <net/arp.h>
+
+#include <net/inet_sock.h>
#include <net/snmp.h>
struct sock;
@@ -45,6 +41,7 @@ struct inet_skb_parm
#define IPSKB_TRANSLATED 2
#define IPSKB_FORWARDED 4
#define IPSKB_XFRM_TUNNEL_SIZE 8
+#define IPSKB_FRAG_COMPLETE 16
};
struct ipcm_cookie
@@ -74,6 +71,13 @@ extern rwlock_t ip_ra_lock;
#define IP_FRAG_TIME (30 * HZ) /* fragment lifetime */
+struct msghdr;
+struct net_device;
+struct packet_type;
+struct rtable;
+struct sk_buff;
+struct sockaddr;
+
extern void ip_mc_dropsocket(struct sock *);
extern void ip_mc_dropdevice(struct net_device *dev);
extern int igmp_mc_proc_init(void);
@@ -168,6 +172,7 @@ extern int sysctl_ipfrag_high_thresh;
extern int sysctl_ipfrag_low_thresh;
extern int sysctl_ipfrag_time;
extern int sysctl_ipfrag_secret_interval;
+extern int sysctl_ipfrag_max_dist;
/* From inetpeer.c */
extern int inet_peer_threshold;
@@ -182,6 +187,8 @@ extern int sysctl_ip_dynaddr;
extern void ipfrag_init(void);
#ifdef CONFIG_INET
+#include <net/dst.h>
+
/* The function in 2.2 was invalid, producing wrong result for
* check=0xFEFF. It was noticed by Arthur Skawina _year_ ago. --ANK(000625) */
static inline
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 14de4ebd1211..e000fa2cd5f6 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -238,6 +238,8 @@ extern int fib_validate_source(u32 src, u32 dst, u8 tos, int oif,
struct net_device *dev, u32 *spec_dst, u32 *itag);
extern void fib_select_multipath(const struct flowi *flp, struct fib_result *res);
+struct rtentry;
+
/* Exported by fib_semantics.c */
extern int ip_fib_check_default(u32 gw, struct net_device *dev);
extern int fib_sync_down(u32 local, struct net_device *dev, int force);
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index 3b5559a023a4..7d2674fde19a 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -251,16 +251,15 @@ struct ip_vs_daemon_user {
#include <linux/config.h>
#include <linux/list.h> /* for struct list_head */
#include <linux/spinlock.h> /* for struct rwlock_t */
-#include <linux/skbuff.h> /* for struct sk_buff */
-#include <linux/ip.h> /* for struct iphdr */
#include <asm/atomic.h> /* for struct atomic_t */
-#include <linux/netdevice.h> /* for struct neighbour */
-#include <net/dst.h> /* for struct dst_entry */
-#include <net/udp.h>
#include <linux/compiler.h>
+#include <linux/timer.h>
+#include <net/checksum.h>
#ifdef CONFIG_IP_VS_DEBUG
+#include <linux/net.h>
+
extern int ip_vs_get_debug_level(void);
#define IP_VS_DBG(level, msg...) \
do { \
@@ -429,8 +428,11 @@ struct ip_vs_stats
spinlock_t lock; /* spin lock */
};
+struct dst_entry;
+struct iphdr;
struct ip_vs_conn;
struct ip_vs_app;
+struct sk_buff;
struct ip_vs_protocol {
struct ip_vs_protocol *next;
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 0a2ad51cff82..860bbac4c4ee 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -240,6 +240,8 @@ extern struct ipv6_txoptions * ipv6_renew_options(struct sock *sk, struct ipv6_t
struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
struct ipv6_txoptions *opt);
+extern int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb);
+
extern int ip6_frag_nqueues;
extern atomic_t ip6_frag_mem;
@@ -525,6 +527,9 @@ extern int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
extern int inet6_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg);
+extern int inet6_hash_connect(struct inet_timewait_death_row *death_row,
+ struct sock *sk);
+
/*
* reassembly.c
*/
@@ -533,8 +538,11 @@ extern int sysctl_ip6frag_low_thresh;
extern int sysctl_ip6frag_time;
extern int sysctl_ip6frag_secret_interval;
-extern struct proto_ops inet6_stream_ops;
-extern struct proto_ops inet6_dgram_ops;
+extern const struct proto_ops inet6_stream_ops;
+extern const struct proto_ops inet6_dgram_ops;
+
+struct group_source_req;
+struct group_filter;
extern int ip6_mc_source(int add, int omode, struct sock *sk,
struct group_source_req *pgsr);
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index f85d6e4b7442..bbac87eeb422 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -35,11 +35,20 @@ enum {
#ifdef __KERNEL__
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
+#include <linux/config.h>
+#include <linux/compiler.h>
#include <linux/icmpv6.h>
+#include <linux/in6.h>
+#include <linux/types.h>
+
#include <net/neighbour.h>
-#include <asm/atomic.h>
+
+struct ctl_table;
+struct file;
+struct inet6_dev;
+struct net_device;
+struct net_proto_family;
+struct sk_buff;
extern struct neigh_table nd_tbl;
@@ -108,7 +117,7 @@ extern int igmp6_event_report(struct sk_buff *skb);
extern void igmp6_cleanup(void);
#ifdef CONFIG_SYSCTL
-extern int ndisc_ifinfo_sysctl_change(ctl_table *ctl,
+extern int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl,
int write,
struct file * filp,
void __user *buffer,
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 34c07731933d..6fa9ae190741 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -49,8 +49,8 @@
#ifdef __KERNEL__
#include <asm/atomic.h>
-#include <linux/skbuff.h>
#include <linux/netdevice.h>
+#include <linux/skbuff.h>
#include <linux/rcupdate.h>
#include <linux/seq_file.h>
diff --git a/include/net/pkt_act.h b/include/net/pkt_act.h
index bd08964b72c0..b225d8472b7e 100644
--- a/include/net/pkt_act.h
+++ b/include/net/pkt_act.h
@@ -15,7 +15,6 @@
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
-#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
#include <linux/module.h>
diff --git a/include/net/protocol.h b/include/net/protocol.h
index 357691f6a45f..63f7db99c2a6 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -65,7 +65,7 @@ struct inet_protosw {
int protocol; /* This is the L4 protocol number. */
struct proto *prot;
- struct proto_ops *ops;
+ const struct proto_ops *ops;
int capability; /* Which (if any) capability do
* we need to use this socket
@@ -76,6 +76,7 @@ struct inet_protosw {
};
#define INET_PROTOSW_REUSE 0x01 /* Are ports automatically reusable? */
#define INET_PROTOSW_PERMANENT 0x02 /* Permanent protocols are unremovable. */
+#define INET_PROTOSW_ICSK 0x04 /* Is this an inet_connection_sock? */
extern struct net_protocol *inet_protocol_base;
extern struct net_protocol *inet_protos[MAX_INET_PROTOS];
diff --git a/include/net/raw.h b/include/net/raw.h
index f47917469b12..e67b28a0248c 100644
--- a/include/net/raw.h
+++ b/include/net/raw.h
@@ -19,6 +19,8 @@
#include <linux/config.h>
+#include <net/protocol.h>
+
extern struct proto raw_prot;
extern void raw_err(struct sock *, struct sk_buff *, u32 info);
diff --git a/include/net/request_sock.h b/include/net/request_sock.h
index b52cc52ffe39..11641c9384f7 100644
--- a/include/net/request_sock.h
+++ b/include/net/request_sock.h
@@ -244,7 +244,7 @@ static inline int reqsk_queue_is_full(const struct request_sock_queue *queue)
static inline void reqsk_queue_hash_req(struct request_sock_queue *queue,
u32 hash, struct request_sock *req,
- unsigned timeout)
+ unsigned long timeout)
{
struct listen_sock *lopt = queue->listen_opt;
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 8e7794ee27ff..f5c22d77feab 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -277,6 +277,24 @@ struct sctp_sock {
__u32 default_context;
__u32 default_timetolive;
+ /* Heartbeat interval: The endpoint sends out a Heartbeat chunk to
+ * the destination address every heartbeat interval. This value
+ * will be inherited by all new associations.
+ */
+ __u32 hbinterval;
+
+ /* This is the max_retrans value for new associations. */
+ __u16 pathmaxrxt;
+
+ /* The initial Path MTU to use for new associations. */
+ __u32 pathmtu;
+
+ /* The default SACK delay timeout for new associations. */
+ __u32 sackdelay;
+
+ /* Flags controling Heartbeat, SACK delay, and Path MTU Discovery. */
+ __u32 param_flags;
+
struct sctp_initmsg initmsg;
struct sctp_rtoinfo rtoinfo;
struct sctp_paddrparams paddrparam;
@@ -845,9 +863,6 @@ struct sctp_transport {
/* Data that has been sent, but not acknowledged. */
__u32 flight_size;
- /* PMTU : The current known path MTU. */
- __u32 pmtu;
-
/* Destination */
struct dst_entry *dst;
/* Source address. */
@@ -862,7 +877,22 @@ struct sctp_transport {
/* Heartbeat interval: The endpoint sends out a Heartbeat chunk to
* the destination address every heartbeat interval.
*/
- int hb_interval;
+ __u32 hbinterval;
+
+ /* This is the max_retrans value for the transport and will
+ * be initialized from the assocs value. This can be changed
+ * using SCTP_SET_PEER_ADDR_PARAMS socket option.
+ */
+ __u16 pathmaxrxt;
+
+ /* PMTU : The current known path MTU. */
+ __u32 pathmtu;
+
+ /* SACK delay timeout */
+ __u32 sackdelay;
+
+ /* Flags controling Heartbeat, SACK delay, and Path MTU Discovery. */
+ __u32 param_flags;
/* When was the last time (in jiffies) that we heard from this
* transport? We use this to pick new active and retran paths.
@@ -882,22 +912,11 @@ struct sctp_transport {
*/
int state;
- /* hb_allowed : The current heartbeat state of this destination,
- * : i.e. ALLOW-HB, NO-HEARTBEAT, etc.
- */
- int hb_allowed;
-
/* These are the error stats for this destination. */
/* Error count : The current error count for this destination. */
unsigned short error_count;
- /* This is the max_retrans value for the transport and will
- * be initialized to proto.max_retrans.path. This can be changed
- * using SCTP_SET_PEER_ADDR_PARAMS socket option.
- */
- int max_retrans;
-
/* Per : A timer used by each destination.
* Destination :
* Timer :
@@ -1502,6 +1521,28 @@ struct sctp_association {
/* The largest timeout or RTO value to use in attempting an INIT */
__u16 max_init_timeo;
+ /* Heartbeat interval: The endpoint sends out a Heartbeat chunk to
+ * the destination address every heartbeat interval. This value
+ * will be inherited by all new transports.
+ */
+ __u32 hbinterval;
+
+ /* This is the max_retrans value for new transports in the
+ * association.
+ */
+ __u16 pathmaxrxt;
+
+ /* Association : The smallest PMTU discovered for all of the
+ * PMTU : peer's transport addresses.
+ */
+ __u32 pathmtu;
+
+ /* SACK delay timeout */
+ __u32 sackdelay;
+
+ /* Flags controling Heartbeat, SACK delay, and Path MTU Discovery. */
+ __u32 param_flags;
+
int timeouts[SCTP_NUM_TIMEOUT_TYPES];
struct timer_list timers[SCTP_NUM_TIMEOUT_TYPES];
@@ -1571,11 +1612,6 @@ struct sctp_association {
*/
wait_queue_head_t wait;
- /* Association : The smallest PMTU discovered for all of the
- * PMTU : peer's transport addresses.
- */
- __u32 pmtu;
-
/* The message size at which SCTP fragmentation will occur. */
__u32 frag_point;
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h
index f1c3bc54526a..8a6bef6f91eb 100644
--- a/include/net/sctp/user.h
+++ b/include/net/sctp/user.h
@@ -93,6 +93,8 @@ enum sctp_optname {
#define SCTP_STATUS SCTP_STATUS
SCTP_GET_PEER_ADDR_INFO,
#define SCTP_GET_PEER_ADDR_INFO SCTP_GET_PEER_ADDR_INFO
+ SCTP_DELAYED_ACK_TIME,
+#define SCTP_DELAYED_ACK_TIME SCTP_DELAYED_ACK_TIME
/* Internal Socket Options. Some of the sctp library functions are
* implemented using these socket options.
@@ -503,13 +505,41 @@ struct sctp_setadaption {
* unreachable. The following structure is used to access and modify an
* address's parameters:
*/
+enum sctp_spp_flags {
+ SPP_HB_ENABLE = 1, /*Enable heartbeats*/
+ SPP_HB_DISABLE = 2, /*Disable heartbeats*/
+ SPP_HB = SPP_HB_ENABLE | SPP_HB_DISABLE,
+ SPP_HB_DEMAND = 4, /*Send heartbeat immediately*/
+ SPP_PMTUD_ENABLE = 8, /*Enable PMTU discovery*/
+ SPP_PMTUD_DISABLE = 16, /*Disable PMTU discovery*/
+ SPP_PMTUD = SPP_PMTUD_ENABLE | SPP_PMTUD_DISABLE,
+ SPP_SACKDELAY_ENABLE = 32, /*Enable SACK*/
+ SPP_SACKDELAY_DISABLE = 64, /*Disable SACK*/
+ SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE,
+};
+
struct sctp_paddrparams {
sctp_assoc_t spp_assoc_id;
struct sockaddr_storage spp_address;
__u32 spp_hbinterval;
__u16 spp_pathmaxrxt;
+ __u32 spp_pathmtu;
+ __u32 spp_sackdelay;
+ __u32 spp_flags;
} __attribute__((packed, aligned(4)));
+/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
+ *
+ * This options will get or set the delayed ack timer. The time is set
+ * in milliseconds. If the assoc_id is 0, then this sets or gets the
+ * endpoints default delayed ack timer value. If the assoc_id field is
+ * non-zero, then the set or get effects the specified association.
+ */
+struct sctp_assoc_value {
+ sctp_assoc_t assoc_id;
+ uint32_t assoc_value;
+};
+
/*
* 7.2.2 Peer Address Information
*
diff --git a/include/net/sock.h b/include/net/sock.h
index 982b4ecd187b..6961700ff3a0 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -493,6 +493,7 @@ extern void sk_stream_kill_queues(struct sock *sk);
extern int sk_wait_data(struct sock *sk, long *timeo);
struct request_sock_ops;
+struct timewait_sock_ops;
/* Networking protocol blocks we attach to sockets.
* socket layer -> transport layer interface
@@ -557,11 +558,10 @@ struct proto {
kmem_cache_t *slab;
unsigned int obj_size;
- kmem_cache_t *twsk_slab;
- unsigned int twsk_obj_size;
atomic_t *orphan_count;
struct request_sock_ops *rsk_prot;
+ struct timewait_sock_ops *twsk_prot;
struct module *owner;
@@ -926,6 +926,29 @@ static inline void sock_put(struct sock *sk)
sk_free(sk);
}
+static inline int sk_receive_skb(struct sock *sk, struct sk_buff *skb)
+{
+ int rc = NET_RX_SUCCESS;
+
+ if (sk_filter(sk, skb, 0))
+ goto discard_and_relse;
+
+ skb->dev = NULL;
+
+ bh_lock_sock(sk);
+ if (!sock_owned_by_user(sk))
+ rc = sk->sk_backlog_rcv(sk, skb);
+ else
+ sk_add_backlog(sk, skb);
+ bh_unlock_sock(sk);
+out:
+ sock_put(sk);
+ return rc;
+discard_and_relse:
+ kfree_skb(skb);
+ goto out;
+}
+
/* Detach socket from process context.
* Announce socket dead, detach it from wait queue and inode.
* Note that parent inode held reference count on this struct sock,
@@ -1166,7 +1189,10 @@ static inline int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb)
static inline int sock_error(struct sock *sk)
{
- int err = xchg(&sk->sk_err, 0);
+ int err;
+ if (likely(!sk->sk_err))
+ return 0;
+ err = xchg(&sk->sk_err, 0);
return -err;
}
diff --git a/include/net/tcp.h b/include/net/tcp.h
index d78025f9fbea..77f21c65bbca 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -225,53 +225,6 @@ extern atomic_t tcp_sockets_allocated;
extern int tcp_memory_pressure;
/*
- * Pointers to address related TCP functions
- * (i.e. things that depend on the address family)
- */
-
-struct tcp_func {
- int (*queue_xmit) (struct sk_buff *skb,
- int ipfragok);
-
- void (*send_check) (struct sock *sk,
- struct tcphdr *th,
- int len,
- struct sk_buff *skb);
-
- int (*rebuild_header) (struct sock *sk);
-
- int (*conn_request) (struct sock *sk,
- struct sk_buff *skb);
-
- struct sock * (*syn_recv_sock) (struct sock *sk,
- struct sk_buff *skb,
- struct request_sock *req,
- struct dst_entry *dst);
-
- int (*remember_stamp) (struct sock *sk);
-
- __u16 net_header_len;
-
- int (*setsockopt) (struct sock *sk,
- int level,
- int optname,
- char __user *optval,
- int optlen);
-
- int (*getsockopt) (struct sock *sk,
- int level,
- int optname,
- char __user *optval,
- int __user *optlen);
-
-
- void (*addr2sockaddr) (struct sock *sk,
- struct sockaddr *);
-
- int sockaddr_len;
-};
-
-/*
* The next routines deal with comparing 32 bit unsigned ints
* and worry about wraparound (automatic with unsigned arithmetic).
*/
@@ -334,6 +287,9 @@ extern int tcp_rcv_established(struct sock *sk,
extern void tcp_rcv_space_adjust(struct sock *sk);
+extern int tcp_twsk_unique(struct sock *sk,
+ struct sock *sktw, void *twp);
+
static inline void tcp_dec_quickack_mode(struct sock *sk,
const unsigned int pkts)
{
@@ -405,8 +361,7 @@ extern void tcp_parse_options(struct sk_buff *skb,
* TCP v4 functions exported for the inet6 API
*/
-extern void tcp_v4_send_check(struct sock *sk,
- struct tcphdr *th, int len,
+extern void tcp_v4_send_check(struct sock *sk, int len,
struct sk_buff *skb);
extern int tcp_v4_conn_request(struct sock *sk,
@@ -490,34 +445,16 @@ typedef int (*sk_read_actor_t)(read_descriptor_t *, struct sk_buff *,
extern int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t recv_actor);
-/* Initialize RCV_MSS value.
- * RCV_MSS is an our guess about MSS used by the peer.
- * We haven't any direct information about the MSS.
- * It's better to underestimate the RCV_MSS rather than overestimate.
- * Overestimations make us ACKing less frequently than needed.
- * Underestimations are more easy to detect and fix by tcp_measure_rcv_mss().
- */
+extern void tcp_initialize_rcv_mss(struct sock *sk);
-static inline void tcp_initialize_rcv_mss(struct sock *sk)
-{
- struct tcp_sock *tp = tcp_sk(sk);
- unsigned int hint = min_t(unsigned int, tp->advmss, tp->mss_cache);
-
- hint = min(hint, tp->rcv_wnd/2);
- hint = min(hint, TCP_MIN_RCVMSS);
- hint = max(hint, TCP_MIN_MSS);
-
- inet_csk(sk)->icsk_ack.rcv_mss = hint;
-}
-
-static __inline__ void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)
+static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)
{
tp->pred_flags = htonl((tp->tcp_header_len << 26) |
ntohl(TCP_FLAG_ACK) |
snd_wnd);
}
-static __inline__ void tcp_fast_path_on(struct tcp_sock *tp)
+static inline void tcp_fast_path_on(struct tcp_sock *tp)
{
__tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale);
}
@@ -535,7 +472,7 @@ static inline void tcp_fast_path_check(struct sock *sk, struct tcp_sock *tp)
* Rcv_nxt can be after the window if our peer push more data
* than the offered window.
*/
-static __inline__ u32 tcp_receive_window(const struct tcp_sock *tp)
+static inline u32 tcp_receive_window(const struct tcp_sock *tp)
{
s32 win = tp->rcv_wup + tp->rcv_wnd - tp->rcv_nxt;
@@ -707,6 +644,7 @@ extern void tcp_cleanup_congestion_control(struct sock *sk);
extern int tcp_set_default_congestion_control(const char *name);
extern void tcp_get_default_congestion_control(char *name);
extern int tcp_set_congestion_control(struct sock *sk, const char *name);
+extern void tcp_slow_start(struct tcp_sock *tp);
extern struct tcp_congestion_ops tcp_init_congestion_ops;
extern u32 tcp_reno_ssthresh(struct sock *sk);
@@ -746,7 +684,7 @@ static inline void tcp_ca_event(struct sock *sk, const enum tcp_ca_event event)
* "Packets left network, but not honestly ACKed yet" PLUS
* "Packets fast retransmitted"
*/
-static __inline__ unsigned int tcp_packets_in_flight(const struct tcp_sock *tp)
+static inline unsigned int tcp_packets_in_flight(const struct tcp_sock *tp)
{
return (tp->packets_out - tp->left_out + tp->retrans_out);
}
@@ -766,33 +704,6 @@ static inline __u32 tcp_current_ssthresh(const struct sock *sk)
(tp->snd_cwnd >> 2)));
}
-/*
- * Linear increase during slow start
- */
-static inline void tcp_slow_start(struct tcp_sock *tp)
-{
- if (sysctl_tcp_abc) {
- /* RFC3465: Slow Start
- * TCP sender SHOULD increase cwnd by the number of
- * previously unacknowledged bytes ACKed by each incoming
- * acknowledgment, provided the increase is not more than L
- */
- if (tp->bytes_acked < tp->mss_cache)
- return;
-
- /* We MAY increase by 2 if discovered delayed ack */
- if (sysctl_tcp_abc > 1 && tp->bytes_acked > 2*tp->mss_cache) {
- if (tp->snd_cwnd < tp->snd_cwnd_clamp)
- tp->snd_cwnd++;
- }
- }
- tp->bytes_acked = 0;
-
- if (tp->snd_cwnd < tp->snd_cwnd_clamp)
- tp->snd_cwnd++;
-}
-
-
static inline void tcp_sync_left_out(struct tcp_sock *tp)
{
if (tp->rx_opt.sack_ok &&
@@ -801,34 +712,7 @@ static inline void tcp_sync_left_out(struct tcp_sock *tp)
tp->left_out = tp->sacked_out + tp->lost_out;
}
-/* Set slow start threshold and cwnd not falling to slow start */
-static inline void __tcp_enter_cwr(struct sock *sk)
-{
- const struct inet_connection_sock *icsk = inet_csk(sk);
- struct tcp_sock *tp = tcp_sk(sk);
-
- tp->undo_marker = 0;
- tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);
- tp->snd_cwnd = min(tp->snd_cwnd,
- tcp_packets_in_flight(tp) + 1U);
- tp->snd_cwnd_cnt = 0;
- tp->high_seq = tp->snd_nxt;
- tp->snd_cwnd_stamp = tcp_time_stamp;
- TCP_ECN_queue_cwr(tp);
-}
-
-static inline void tcp_enter_cwr(struct sock *sk)
-{
- struct tcp_sock *tp = tcp_sk(sk);
-
- tp->prior_ssthresh = 0;
- tp->bytes_acked = 0;
- if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) {
- __tcp_enter_cwr(sk);
- tcp_set_ca_state(sk, TCP_CA_CWR);
- }
-}
-
+extern void tcp_enter_cwr(struct sock *sk);
extern __u32 tcp_init_cwnd(struct tcp_sock *tp, struct dst_entry *dst);
/* Slow start with delack produces 3 packets of burst, so that
@@ -860,14 +744,14 @@ static inline int tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight)
return left <= tcp_max_burst(tp);
}
-static __inline__ void tcp_minshall_update(struct tcp_sock *tp, int mss,
- const struct sk_buff *skb)
+static inline void tcp_minshall_update(struct tcp_sock *tp, int mss,
+ const struct sk_buff *skb)
{
if (skb->len < mss)
tp->snd_sml = TCP_SKB_CB(skb)->end_seq;
}
-static __inline__ void tcp_check_probe_timer(struct sock *sk, struct tcp_sock *tp)
+static inline void tcp_check_probe_timer(struct sock *sk, struct tcp_sock *tp)
{
const struct inet_connection_sock *icsk = inet_csk(sk);
if (!tp->packets_out && !icsk->icsk_pending)
@@ -875,18 +759,18 @@ static __inline__ void tcp_check_probe_timer(struct sock *sk, struct tcp_sock *t
icsk->icsk_rto, TCP_RTO_MAX);
}
-static __inline__ void tcp_push_pending_frames(struct sock *sk,
- struct tcp_sock *tp)
+static inline void tcp_push_pending_frames(struct sock *sk,
+ struct tcp_sock *tp)
{
__tcp_push_pending_frames(sk, tp, tcp_current_mss(sk, 1), tp->nonagle);
}
-static __inline__ void tcp_init_wl(struct tcp_sock *tp, u32 ack, u32 seq)
+static inline void tcp_init_wl(struct tcp_sock *tp, u32 ack, u32 seq)
{
tp->snd_wl1 = seq;
}
-static __inline__ void tcp_update_wl(struct tcp_sock *tp, u32 ack, u32 seq)
+static inline void tcp_update_wl(struct tcp_sock *tp, u32 ack, u32 seq)
{
tp->snd_wl1 = seq;
}
@@ -894,19 +778,19 @@ static __inline__ void tcp_update_wl(struct tcp_sock *tp, u32 ack, u32 seq)
/*
* Calculate(/check) TCP checksum
*/
-static __inline__ u16 tcp_v4_check(struct tcphdr *th, int len,
- unsigned long saddr, unsigned long daddr,
- unsigned long base)
+static inline u16 tcp_v4_check(struct tcphdr *th, int len,
+ unsigned long saddr, unsigned long daddr,
+ unsigned long base)
{
return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base);
}
-static __inline__ int __tcp_checksum_complete(struct sk_buff *skb)
+static inline int __tcp_checksum_complete(struct sk_buff *skb)
{
return __skb_checksum_complete(skb);
}
-static __inline__ int tcp_checksum_complete(struct sk_buff *skb)
+static inline int tcp_checksum_complete(struct sk_buff *skb)
{
return skb->ip_summed != CHECKSUM_UNNECESSARY &&
__tcp_checksum_complete(skb);
@@ -914,7 +798,7 @@ static __inline__ int tcp_checksum_complete(struct sk_buff *skb)
/* Prequeue for VJ style copy to user, combined with checksumming. */
-static __inline__ void tcp_prequeue_init(struct tcp_sock *tp)
+static inline void tcp_prequeue_init(struct tcp_sock *tp)
{
tp->ucopy.task = NULL;
tp->ucopy.len = 0;
@@ -930,7 +814,7 @@ static __inline__ void tcp_prequeue_init(struct tcp_sock *tp)
*
* NOTE: is this not too big to inline?
*/
-static __inline__ int tcp_prequeue(struct sock *sk, struct sk_buff *skb)
+static inline int tcp_prequeue(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
@@ -971,7 +855,7 @@ static const char *statename[]={
};
#endif
-static __inline__ void tcp_set_state(struct sock *sk, int state)
+static inline void tcp_set_state(struct sock *sk, int state)
{
int oldstate = sk->sk_state;
@@ -1005,7 +889,7 @@ static __inline__ void tcp_set_state(struct sock *sk, int state)
#endif
}
-static __inline__ void tcp_done(struct sock *sk)
+static inline void tcp_done(struct sock *sk)
{
tcp_set_state(sk, TCP_CLOSE);
tcp_clear_xmit_timers(sk);
@@ -1018,81 +902,13 @@ static __inline__ void tcp_done(struct sock *sk)
inet_csk_destroy_sock(sk);
}
-static __inline__ void tcp_sack_reset(struct tcp_options_received *rx_opt)
+static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
{
rx_opt->dsack = 0;
rx_opt->eff_sacks = 0;
rx_opt->num_sacks = 0;
}
-static __inline__ void tcp_build_and_update_options(__u32 *ptr, struct tcp_sock *tp, __u32 tstamp)
-{
- if (tp->rx_opt.tstamp_ok) {
- *ptr++ = __constant_htonl((TCPOPT_NOP << 24) |
- (TCPOPT_NOP << 16) |
- (TCPOPT_TIMESTAMP << 8) |
- TCPOLEN_TIMESTAMP);
- *ptr++ = htonl(tstamp);
- *ptr++ = htonl(tp->rx_opt.ts_recent);
- }
- if (tp->rx_opt.eff_sacks) {
- struct tcp_sack_block *sp = tp->rx_opt.dsack ? tp->duplicate_sack : tp->selective_acks;
- int this_sack;
-
- *ptr++ = __constant_htonl((TCPOPT_NOP << 24) |
- (TCPOPT_NOP << 16) |
- (TCPOPT_SACK << 8) |
- (TCPOLEN_SACK_BASE +
- (tp->rx_opt.eff_sacks * TCPOLEN_SACK_PERBLOCK)));
- for(this_sack = 0; this_sack < tp->rx_opt.eff_sacks; this_sack++) {
- *ptr++ = htonl(sp[this_sack].start_seq);
- *ptr++ = htonl(sp[this_sack].end_seq);
- }
- if (tp->rx_opt.dsack) {
- tp->rx_opt.dsack = 0;
- tp->rx_opt.eff_sacks--;
- }
- }
-}
-
-/* Construct a tcp options header for a SYN or SYN_ACK packet.
- * If this is every changed make sure to change the definition of
- * MAX_SYN_SIZE to match the new maximum number of options that you
- * can generate.
- */
-static inline void tcp_syn_build_options(__u32 *ptr, int mss, int ts, int sack,
- int offer_wscale, int wscale, __u32 tstamp, __u32 ts_recent)
-{
- /* We always get an MSS option.
- * The option bytes which will be seen in normal data
- * packets should timestamps be used, must be in the MSS
- * advertised. But we subtract them from tp->mss_cache so
- * that calculations in tcp_sendmsg are simpler etc.
- * So account for this fact here if necessary. If we
- * don't do this correctly, as a receiver we won't
- * recognize data packets as being full sized when we
- * should, and thus we won't abide by the delayed ACK
- * rules correctly.
- * SACKs don't matter, we never delay an ACK when we
- * have any of those going out.
- */
- *ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | mss);
- if (ts) {
- if(sack)
- *ptr++ = __constant_htonl((TCPOPT_SACK_PERM << 24) | (TCPOLEN_SACK_PERM << 16) |
- (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
- else
- *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
- (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
- *ptr++ = htonl(tstamp); /* TSVAL */
- *ptr++ = htonl(ts_recent); /* TSECR */
- } else if(sack)
- *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
- (TCPOPT_SACK_PERM << 8) | TCPOLEN_SACK_PERM);
- if (offer_wscale)
- *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_WINDOW << 16) | (TCPOLEN_WINDOW << 8) | (wscale));
-}
-
/* Determine a window scaling and initial window to offer. */
extern void tcp_select_initial_window(int __space, __u32 mss,
__u32 *rcv_wnd, __u32 *window_clamp,
@@ -1117,9 +933,9 @@ static inline int tcp_full_space(const struct sock *sk)
return tcp_win_from_space(sk->sk_rcvbuf);
}
-static __inline__ void tcp_openreq_init(struct request_sock *req,
- struct tcp_options_received *rx_opt,
- struct sk_buff *skb)
+static inline void tcp_openreq_init(struct request_sock *req,
+ struct tcp_options_received *rx_opt,
+ struct sk_buff *skb)
{
struct inet_request_sock *ireq = inet_rsk(req);
diff --git a/include/net/tcp_states.h b/include/net/tcp_states.h
index b9d4176b2d15..b0b645988bd8 100644
--- a/include/net/tcp_states.h
+++ b/include/net/tcp_states.h
@@ -31,4 +31,20 @@ enum {
#define TCP_STATE_MASK 0xF
+#define TCP_ACTION_FIN (1 << 7)
+
+enum {
+ TCPF_ESTABLISHED = (1 << 1),
+ TCPF_SYN_SENT = (1 << 2),
+ TCPF_SYN_RECV = (1 << 3),
+ TCPF_FIN_WAIT1 = (1 << 4),
+ TCPF_FIN_WAIT2 = (1 << 5),
+ TCPF_TIME_WAIT = (1 << 6),
+ TCPF_CLOSE = (1 << 7),
+ TCPF_CLOSE_WAIT = (1 << 8),
+ TCPF_LAST_ACK = (1 << 9),
+ TCPF_LISTEN = (1 << 10),
+ TCPF_CLOSING = (1 << 11)
+};
+
#endif /* _LINUX_TCP_STATES_H */
diff --git a/include/net/timewait_sock.h b/include/net/timewait_sock.h
new file mode 100644
index 000000000000..2544281e1d5e
--- /dev/null
+++ b/include/net/timewait_sock.h
@@ -0,0 +1,31 @@
+/*
+ * NET Generic infrastructure for Network protocols.
+ *
+ * Authors: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _TIMEWAIT_SOCK_H
+#define _TIMEWAIT_SOCK_H
+
+#include <linux/slab.h>
+#include <net/sock.h>
+
+struct timewait_sock_ops {
+ kmem_cache_t *twsk_slab;
+ unsigned int twsk_obj_size;
+ int (*twsk_unique)(struct sock *sk,
+ struct sock *sktw, void *twp);
+};
+
+static inline int twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
+{
+ if (sk->sk_prot->twsk_prot->twsk_unique != NULL)
+ return sk->sk_prot->twsk_prot->twsk_unique(sk, sktw, twp);
+ return 0;
+}
+
+#endif /* _TIMEWAIT_SOCK_H */
diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h
index 4e86f2de6638..61f724c1036f 100644
--- a/include/net/transp_v6.h
+++ b/include/net/transp_v6.h
@@ -44,7 +44,7 @@ extern int datagram_send_ctl(struct msghdr *msg,
/*
* address family specific functions
*/
-extern struct tcp_func ipv4_specific;
+extern struct inet_connection_sock_af_ops ipv4_specific;
extern int inet6_destroy_sock(struct sock *sk);
diff --git a/include/net/udp.h b/include/net/udp.h
index 107b9d791a1f..766fba1369ce 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -22,9 +22,8 @@
#ifndef _UDP_H
#define _UDP_H
-#include <linux/udp.h>
-#include <linux/ip.h>
#include <linux/list.h>
+#include <net/inet_sock.h>
#include <net/sock.h>
#include <net/snmp.h>
#include <linux/seq_file.h>
@@ -62,6 +61,7 @@ static inline int udp_lport_inuse(u16 num)
extern struct proto udp_prot;
+struct sk_buff;
extern void udp_err(struct sk_buff *, u32);
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 5beae1ccd574..07d7b50cdd76 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -2,11 +2,12 @@
#define _NET_XFRM_H
#include <linux/compiler.h>
+#include <linux/in.h>
#include <linux/xfrm.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/skbuff.h>
-#include <linux/netdevice.h>
+#include <linux/socket.h>
#include <linux/crypto.h>
#include <linux/pfkeyv2.h>
#include <linux/in6.h>
@@ -144,6 +145,9 @@ struct xfrm_state
* transformer. */
struct xfrm_type *type;
+ /* Security context */
+ struct xfrm_sec_ctx *security;
+
/* Private data of this transformer, format is opaque,
* interpreted by xfrm_type methods. */
void *data;
@@ -298,6 +302,7 @@ struct xfrm_policy
__u8 flags;
__u8 dead;
__u8 xfrm_nr;
+ struct xfrm_sec_ctx *security;
struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH];
};
@@ -510,6 +515,25 @@ xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
return 0;
}
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+/* If neither has a context --> match
+ * Otherwise, both must have a context and the sids, doi, alg must match
+ */
+static inline int xfrm_sec_ctx_match(struct xfrm_sec_ctx *s1, struct xfrm_sec_ctx *s2)
+{
+ return ((!s1 && !s2) ||
+ (s1 && s2 &&
+ (s1->ctx_sid == s2->ctx_sid) &&
+ (s1->ctx_doi == s2->ctx_doi) &&
+ (s1->ctx_alg == s2->ctx_alg)));
+}
+#else
+static inline int xfrm_sec_ctx_match(struct xfrm_sec_ctx *s1, struct xfrm_sec_ctx *s2)
+{
+ return 1;
+}
+#endif
+
/* A struct encoding bundle of transformations to apply to some set of flow.
*
* dst->child points to the next element of bundle.
@@ -878,8 +902,8 @@ static inline int xfrm_dst_lookup(struct xfrm_dst **dst, struct flowi *fl, unsig
struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp);
extern int xfrm_policy_walk(int (*func)(struct xfrm_policy *, int, int, void*), void *);
int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
-struct xfrm_policy *xfrm_policy_bysel(int dir, struct xfrm_selector *sel,
- int delete);
+struct xfrm_policy *xfrm_policy_bysel_ctx(int dir, struct xfrm_selector *sel,
+ struct xfrm_sec_ctx *ctx, int delete);
struct xfrm_policy *xfrm_policy_byid(int dir, u32 id, int delete);
void xfrm_policy_flush(void);
u32 xfrm_get_acqseq(void);
@@ -890,6 +914,7 @@ struct xfrm_state * xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
extern void xfrm_policy_flush(void);
extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol);
extern int xfrm_flush_bundles(void);
+extern void xfrm_flush_all_bundles(void);
extern int xfrm_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl, int family);
extern void xfrm_init_pmtu(struct dst_entry *dst);
diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h
index fac547d32a98..394f14a5b7cb 100644
--- a/include/scsi/scsi_transport_fc.h
+++ b/include/scsi/scsi_transport_fc.h
@@ -79,6 +79,7 @@ enum fc_port_state {
FC_PORTSTATE_LINKDOWN,
FC_PORTSTATE_ERROR,
FC_PORTSTATE_LOOPBACK,
+ FC_PORTSTATE_DELETED,
};
@@ -325,8 +326,14 @@ struct fc_host_attrs {
struct list_head rport_bindings;
u32 next_rport_number;
u32 next_target_id;
+ u8 flags;
+ struct work_struct rport_del_work;
};
+/* values for struct fc_host_attrs "flags" field: */
+#define FC_SHOST_RPORT_DEL_SCHEDULED 0x01
+
+
#define fc_host_node_name(x) \
(((struct fc_host_attrs *)(x)->shost_data)->node_name)
#define fc_host_port_name(x) \
@@ -365,6 +372,10 @@ struct fc_host_attrs {
(((struct fc_host_attrs *)(x)->shost_data)->next_rport_number)
#define fc_host_next_target_id(x) \
(((struct fc_host_attrs *)(x)->shost_data)->next_target_id)
+#define fc_host_flags(x) \
+ (((struct fc_host_attrs *)(x)->shost_data)->flags)
+#define fc_host_rport_del_work(x) \
+ (((struct fc_host_attrs *)(x)->shost_data)->rport_del_work)
/* The functions by which the transport class and the driver communicate */
diff --git a/init/Kconfig b/init/Kconfig
index 6c5dbedc6e96..9fc0759fa942 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -260,7 +260,6 @@ config CC_OPTIMIZE_FOR_SIZE
bool "Optimize for size (Look out for broken compilers!)"
default y
depends on ARM || H8300 || EXPERIMENTAL
- depends on !SPARC64
help
Enabling this option will pass "-Os" instead of "-O2" to gcc
resulting in a smaller kernel.
diff --git a/init/main.c b/init/main.c
index 27f97f9b4636..54aaf561cf66 100644
--- a/init/main.c
+++ b/init/main.c
@@ -47,7 +47,6 @@
#include <linux/rmap.h>
#include <linux/mempolicy.h>
#include <linux/key.h>
-#include <net/sock.h>
#include <asm/io.h>
#include <asm/bugs.h>
@@ -614,9 +613,6 @@ static void __init do_basic_setup(void)
sysctl_init();
#endif
- /* Networking initialization needs a process context */
- sock_init();
-
do_initcalls();
}
diff --git a/ipc/sem.c b/ipc/sem.c
index 19af028a3e38..cb5bb2a5df96 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -381,6 +381,7 @@ static void update_queue (struct sem_array * sma)
/* hands-off: q will disappear immediately after
* writing q->status.
*/
+ smp_wmb();
q->status = error;
q = n;
} else {
@@ -461,6 +462,7 @@ static void freeary (struct sem_array *sma, int id)
n = q->next;
q->status = IN_WAKEUP;
wake_up_process(q->sleeper); /* doesn't sleep */
+ smp_wmb();
q->status = -EIDRM; /* hands-off q */
q = n;
}
diff --git a/kernel/futex.c b/kernel/futex.c
index 5872e3507f35..5e71a6bf6f6b 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -270,7 +270,13 @@ static void wake_futex(struct futex_q *q)
/*
* The waiting task can free the futex_q as soon as this is written,
* without taking any locks. This must come last.
+ *
+ * A memory barrier is required here to prevent the following store
+ * to lock_ptr from getting ahead of the wakeup. Clearing the lock
+ * at the end of wake_up_all() does not prevent this store from
+ * moving.
*/
+ wmb();
q->lock_ptr = NULL;
}
diff --git a/kernel/params.c b/kernel/params.c
index 47ba69547945..c76ad25e6a21 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -619,7 +619,7 @@ static void __init param_sysfs_builtin(void)
/* module-related sysfs stuff */
-#ifdef CONFIG_MODULES
+#ifdef CONFIG_SYSFS
#define to_module_attr(n) container_of(n, struct module_attribute, attr);
#define to_module_kobject(n) container_of(n, struct module_kobject, kobj);
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 9990e10192e8..b53115b882e1 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2192,29 +2192,32 @@ int sysctl_string(ctl_table *table, int __user *name, int nlen,
void __user *oldval, size_t __user *oldlenp,
void __user *newval, size_t newlen, void **context)
{
- size_t l, len;
-
if (!table->data || !table->maxlen)
return -ENOTDIR;
if (oldval && oldlenp) {
- if (get_user(len, oldlenp))
+ size_t bufsize;
+ if (get_user(bufsize, oldlenp))
return -EFAULT;
- if (len) {
- l = strlen(table->data);
- if (len > l) len = l;
- if (len >= table->maxlen)
+ if (bufsize) {
+ size_t len = strlen(table->data), copied;
+
+ /* This shouldn't trigger for a well-formed sysctl */
+ if (len > table->maxlen)
len = table->maxlen;
- if(copy_to_user(oldval, table->data, len))
- return -EFAULT;
- if(put_user(0, ((char __user *) oldval) + len))
+
+ /* Copy up to a max of bufsize-1 bytes of the string */
+ copied = (len >= bufsize) ? bufsize - 1 : len;
+
+ if (copy_to_user(oldval, table->data, copied) ||
+ put_user(0, (char __user *)(oldval + copied)))
return -EFAULT;
- if(put_user(len, oldlenp))
+ if (put_user(len, oldlenp))
return -EFAULT;
}
}
if (newval && newlen) {
- len = newlen;
+ size_t len = newlen;
if (len > table->maxlen)
len = table->maxlen;
if(copy_from_user(table->data, newval, len))
@@ -2223,7 +2226,7 @@ int sysctl_string(ctl_table *table, int __user *name, int nlen,
len--;
((char *) table->data)[len] = 0;
}
- return 0;
+ return 1;
}
/*
diff --git a/lib/spinlock_debug.c b/lib/spinlock_debug.c
index 906ad101eab3..dcd4be9bd4e5 100644
--- a/lib/spinlock_debug.c
+++ b/lib/spinlock_debug.c
@@ -20,7 +20,8 @@ static void spin_bug(spinlock_t *lock, const char *msg)
if (lock->owner && lock->owner != SPINLOCK_OWNER_INIT)
owner = lock->owner;
printk("BUG: spinlock %s on CPU#%d, %s/%d\n",
- msg, smp_processor_id(), current->comm, current->pid);
+ msg, raw_smp_processor_id(),
+ current->comm, current->pid);
printk(" lock: %p, .magic: %08x, .owner: %s/%d, .owner_cpu: %d\n",
lock, lock->magic,
owner ? owner->comm : "<none>",
@@ -78,8 +79,8 @@ static void __spin_lock_debug(spinlock_t *lock)
if (print_once) {
print_once = 0;
printk("BUG: spinlock lockup on CPU#%d, %s/%d, %p\n",
- smp_processor_id(), current->comm, current->pid,
- lock);
+ raw_smp_processor_id(), current->comm,
+ current->pid, lock);
dump_stack();
}
}
@@ -120,7 +121,8 @@ static void rwlock_bug(rwlock_t *lock, const char *msg)
if (xchg(&print_once, 0)) {
printk("BUG: rwlock %s on CPU#%d, %s/%d, %p\n", msg,
- smp_processor_id(), current->comm, current->pid, lock);
+ raw_smp_processor_id(), current->comm,
+ current->pid, lock);
dump_stack();
#ifdef CONFIG_SMP
/*
@@ -148,8 +150,8 @@ static void __read_lock_debug(rwlock_t *lock)
if (print_once) {
print_once = 0;
printk("BUG: read-lock lockup on CPU#%d, %s/%d, %p\n",
- smp_processor_id(), current->comm, current->pid,
- lock);
+ raw_smp_processor_id(), current->comm,
+ current->pid, lock);
dump_stack();
}
}
@@ -220,8 +222,8 @@ static void __write_lock_debug(rwlock_t *lock)
if (print_once) {
print_once = 0;
printk("BUG: write-lock lockup on CPU#%d, %s/%d, %p\n",
- smp_processor_id(), current->comm, current->pid,
- lock);
+ raw_smp_processor_id(), current->comm,
+ current->pid, lock);
dump_stack();
}
}
diff --git a/lib/swiotlb.c b/lib/swiotlb.c
index 57216f3544ca..1ff8dcebf7c6 100644
--- a/lib/swiotlb.c
+++ b/lib/swiotlb.c
@@ -704,8 +704,9 @@ swiotlb_map_sg(struct device *hwdev, struct scatterlist *sg, int nelems,
addr = SG_ENT_VIRT_ADDRESS(sg);
dev_addr = virt_to_phys(addr);
if (swiotlb_force || address_needs_mapping(hwdev, dev_addr)) {
- sg->dma_address = (dma_addr_t) virt_to_phys(map_single(hwdev, addr, sg->length, dir));
- if (!sg->dma_address) {
+ void *map = map_single(hwdev, addr, sg->length, dir);
+ sg->dma_address = virt_to_bus(map);
+ if (!map) {
/* Don't panic here, we expect map_sg users
to do proper error handling. */
swiotlb_full(hwdev, sg->length, dir, 0);
diff --git a/mm/memory.c b/mm/memory.c
index d22f78c8a381..d8dde07a3656 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -574,7 +574,7 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
* readonly mappings. The tradeoff is that copy_page_range is more
* efficient than faulting.
*/
- if (!(vma->vm_flags & (VM_HUGETLB|VM_NONLINEAR|VM_PFNMAP))) {
+ if (!(vma->vm_flags & (VM_HUGETLB|VM_NONLINEAR|VM_PFNMAP|VM_INSERTPAGE))) {
if (!vma->anon_vma)
return 0;
}
@@ -1228,6 +1228,7 @@ int vm_insert_page(struct vm_area_struct *vma, unsigned long addr, struct page *
return -EFAULT;
if (!page_count(page))
return -EINVAL;
+ vma->vm_flags |= VM_INSERTPAGE;
return insert_page(vma->vm_mm, addr, page, vma->vm_page_prot);
}
EXPORT_SYMBOL(vm_insert_page);
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index bec88c81244e..72f402cc9c9a 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -161,6 +161,10 @@ static struct mempolicy *mpol_new(int mode, nodemask_t *nodes)
switch (mode) {
case MPOL_INTERLEAVE:
policy->v.nodes = *nodes;
+ if (nodes_weight(*nodes) == 0) {
+ kmem_cache_free(policy_cache, policy);
+ return ERR_PTR(-EINVAL);
+ }
break;
case MPOL_PREFERRED:
policy->v.preferred_node = first_node(*nodes);
diff --git a/mm/mmap.c b/mm/mmap.c
index 11ca5927d5ff..64ba4dbcb7de 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -611,7 +611,7 @@ again: remove_next = 1 + (end > next->vm_end);
* If the vma has a ->close operation then the driver probably needs to release
* per-vma resources, so we don't attempt to merge those.
*/
-#define VM_SPECIAL (VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_RESERVED)
+#define VM_SPECIAL (VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_RESERVED | VM_PFNMAP)
static inline int is_mergeable_vma(struct vm_area_struct *vma,
struct file *file, unsigned long vm_flags)
diff --git a/mm/mremap.c b/mm/mremap.c
index b535438c363c..ddaeee9a0b69 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -323,7 +323,7 @@ unsigned long do_mremap(unsigned long addr,
/* We can't remap across vm area boundaries */
if (old_len > vma->vm_end - addr)
goto out;
- if (vma->vm_flags & VM_DONTEXPAND) {
+ if (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP)) {
if (new_len > old_len)
goto out;
}
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 91e412b0ab00..67465b65abe4 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -753,6 +753,8 @@ static int vlan_ioctl_handler(void __user *arg)
break;
case GET_VLAN_REALDEV_NAME_CMD:
err = vlan_dev_get_realdev_name(args.device1, args.u.device2);
+ if (err)
+ goto out;
if (copy_to_user(arg, &args,
sizeof(struct vlan_ioctl_args))) {
err = -EFAULT;
@@ -761,6 +763,8 @@ static int vlan_ioctl_handler(void __user *arg)
case GET_VLAN_VID_CMD:
err = vlan_dev_get_vid(args.device1, &vid);
+ if (err)
+ goto out;
args.u.VID = vid;
if (copy_to_user(arg, &args,
sizeof(struct vlan_ioctl_args))) {
@@ -774,7 +778,7 @@ static int vlan_ioctl_handler(void __user *arg)
__FUNCTION__, args.cmd);
return -EINVAL;
};
-
+out:
return err;
}
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index 7982656b9c83..a5144e43aae1 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -63,7 +63,7 @@
#include <linux/atalk.h>
struct datalink_proto *ddp_dl, *aarp_dl;
-static struct proto_ops atalk_dgram_ops;
+static const struct proto_ops atalk_dgram_ops;
/**************************************************************************\
* *
@@ -1763,7 +1763,7 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
*/
static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
- int rc = -EINVAL;
+ int rc = -ENOIOCTLCMD;
struct sock *sk = sock->sk;
void __user *argp = (void __user *)arg;
@@ -1813,23 +1813,6 @@ static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
rc = atif_ioctl(cmd, argp);
rtnl_unlock();
break;
- /* Physical layer ioctl calls */
- case SIOCSIFLINK:
- case SIOCGIFHWADDR:
- case SIOCSIFHWADDR:
- case SIOCGIFFLAGS:
- case SIOCSIFFLAGS:
- case SIOCGIFTXQLEN:
- case SIOCSIFTXQLEN:
- case SIOCGIFMTU:
- case SIOCGIFCONF:
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- case SIOCGIFCOUNT:
- case SIOCGIFINDEX:
- case SIOCGIFNAME:
- rc = dev_ioctl(cmd, argp);
- break;
}
return rc;
@@ -1841,7 +1824,7 @@ static struct net_proto_family atalk_family_ops = {
.owner = THIS_MODULE,
};
-static struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = {
+static const struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = {
.family = PF_APPLETALK,
.owner = THIS_MODULE,
.release = atalk_release,
diff --git a/net/atm/pvc.c b/net/atm/pvc.c
index 2684a92da22b..f2c541774dcd 100644
--- a/net/atm/pvc.c
+++ b/net/atm/pvc.c
@@ -102,7 +102,7 @@ static int pvc_getname(struct socket *sock,struct sockaddr *sockaddr,
}
-static struct proto_ops pvc_proto_ops = {
+static const struct proto_ops pvc_proto_ops = {
.family = PF_ATMPVC,
.owner = THIS_MODULE,
diff --git a/net/atm/svc.c b/net/atm/svc.c
index d7b266136bf6..3a180cfd7b48 100644
--- a/net/atm/svc.c
+++ b/net/atm/svc.c
@@ -613,7 +613,7 @@ static int svc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return error;
}
-static struct proto_ops svc_proto_ops = {
+static const struct proto_ops svc_proto_ops = {
.family = PF_ATMSVC,
.owner = THIS_MODULE,
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 1b683f302657..e8753c7fcad1 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -54,7 +54,7 @@
HLIST_HEAD(ax25_list);
DEFINE_SPINLOCK(ax25_list_lock);
-static struct proto_ops ax25_proto_ops;
+static const struct proto_ops ax25_proto_ops;
static void ax25_free_sock(struct sock *sk)
{
@@ -1827,7 +1827,7 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
break;
default:
- res = dev_ioctl(cmd, argp);
+ res = -ENOIOCTLCMD;
break;
}
release_sock(sk);
@@ -1944,7 +1944,7 @@ static struct net_proto_family ax25_family_ops = {
.owner = THIS_MODULE,
};
-static struct proto_ops ax25_proto_ops = {
+static const struct proto_ops ax25_proto_ops = {
.family = PF_AX25,
.owner = THIS_MODULE,
.release = ax25_release,
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index ea616e3fc98e..fb031fe9be9e 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -287,10 +287,9 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
timeo = schedule_timeout(timeo);
lock_sock(sk);
- if (sk->sk_err) {
- err = sock_error(sk);
+ err = sock_error(sk);
+ if (err)
break;
- }
}
set_current_state(TASK_RUNNING);
remove_wait_queue(sk->sk_sleep, &wait);
diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c
index 9778c6acd53b..ccbaf69afc5b 100644
--- a/net/bluetooth/bnep/sock.c
+++ b/net/bluetooth/bnep/sock.c
@@ -146,7 +146,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
return 0;
}
-static struct proto_ops bnep_sock_ops = {
+static const struct proto_ops bnep_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = bnep_sock_release,
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
index beb045bf5714..5e22343b6090 100644
--- a/net/bluetooth/cmtp/sock.c
+++ b/net/bluetooth/cmtp/sock.c
@@ -137,7 +137,7 @@ static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
return -EINVAL;
}
-static struct proto_ops cmtp_sock_ops = {
+static const struct proto_ops cmtp_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = cmtp_sock_release,
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 1d6d0a15c099..84e6c93a044a 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -575,7 +575,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char
return 0;
}
-static struct proto_ops hci_sock_ops = {
+static const struct proto_ops hci_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = hci_sock_release,
diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c
index f8986f881431..8f8dd931b294 100644
--- a/net/bluetooth/hidp/sock.c
+++ b/net/bluetooth/hidp/sock.c
@@ -143,7 +143,7 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
return -EINVAL;
}
-static struct proto_ops hidp_sock_ops = {
+static const struct proto_ops hidp_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = hidp_sock_release,
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index e3bb11ca4235..7f0781e4326f 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -57,7 +57,7 @@
#define VERSION "2.8"
-static struct proto_ops l2cap_sock_ops;
+static const struct proto_ops l2cap_sock_ops;
static struct bt_sock_list l2cap_sk_list = {
.lock = RW_LOCK_UNLOCKED
@@ -767,8 +767,9 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
BT_DBG("sock %p, sk %p", sock, sk);
- if (sk->sk_err)
- return sock_error(sk);
+ err = sock_error(sk);
+ if (err)
+ return err;
if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
@@ -2160,7 +2161,7 @@ static ssize_t l2cap_sysfs_show(struct class *dev, char *buf)
static CLASS_ATTR(l2cap, S_IRUGO, l2cap_sysfs_show, NULL);
-static struct proto_ops l2cap_sock_ops = {
+static const struct proto_ops l2cap_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = l2cap_sock_release,
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 6c34261b232e..757d2dd3b02f 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -58,7 +58,7 @@
#define BT_DBG(D...)
#endif
-static struct proto_ops rfcomm_sock_ops;
+static const struct proto_ops rfcomm_sock_ops;
static struct bt_sock_list rfcomm_sk_list = {
.lock = RW_LOCK_UNLOCKED
@@ -907,7 +907,7 @@ static ssize_t rfcomm_sock_sysfs_show(struct class *dev, char *buf)
static CLASS_ATTR(rfcomm, S_IRUGO, rfcomm_sock_sysfs_show, NULL);
-static struct proto_ops rfcomm_sock_ops = {
+static const struct proto_ops rfcomm_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = rfcomm_sock_release,
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 9cb00dc6c08c..6b61323ce23c 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -56,7 +56,7 @@
#define VERSION "0.5"
-static struct proto_ops sco_sock_ops;
+static const struct proto_ops sco_sock_ops;
static struct bt_sock_list sco_sk_list = {
.lock = RW_LOCK_UNLOCKED
@@ -637,8 +637,9 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
BT_DBG("sock %p, sk %p", sock, sk);
- if (sk->sk_err)
- return sock_error(sk);
+ err = sock_error(sk);
+ if (err)
+ return err;
if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
@@ -913,7 +914,7 @@ static ssize_t sco_sysfs_show(struct class *dev, char *buf)
static CLASS_ATTR(sco, S_IRUGO, sco_sysfs_show, NULL);
-static struct proto_ops sco_sock_ops = {
+static const struct proto_ops sco_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = sco_sock_release,
diff --git a/net/bridge/br.c b/net/bridge/br.c
index f8f184942aaf..188cc1ac49eb 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -67,3 +67,4 @@ EXPORT_SYMBOL(br_should_route_hook);
module_init(br_init)
module_exit(br_deinit)
MODULE_LICENSE("GPL");
+MODULE_VERSION(BR_VERSION);
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index f564ee99782d..0b33a7b3a00c 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -15,7 +15,9 @@
#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+
#include <asm/uaccess.h>
#include "br_private.h"
@@ -82,6 +84,87 @@ static int br_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+/* Allow setting mac address of pseudo-bridge to be same as
+ * any of the bound interfaces
+ */
+static int br_set_mac_address(struct net_device *dev, void *p)
+{
+ struct net_bridge *br = netdev_priv(dev);
+ struct sockaddr *addr = p;
+ struct net_bridge_port *port;
+ int err = -EADDRNOTAVAIL;
+
+ spin_lock_bh(&br->lock);
+ list_for_each_entry(port, &br->port_list, list) {
+ if (!compare_ether_addr(port->dev->dev_addr, addr->sa_data)) {
+ br_stp_change_bridge_id(br, addr->sa_data);
+ err = 0;
+ break;
+ }
+ }
+ spin_unlock_bh(&br->lock);
+
+ return err;
+}
+
+static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, "bridge");
+ strcpy(info->version, BR_VERSION);
+ strcpy(info->fw_version, "N/A");
+ strcpy(info->bus_info, "N/A");
+}
+
+static int br_set_sg(struct net_device *dev, u32 data)
+{
+ struct net_bridge *br = netdev_priv(dev);
+
+ if (data)
+ br->feature_mask |= NETIF_F_SG;
+ else
+ br->feature_mask &= ~NETIF_F_SG;
+
+ br_features_recompute(br);
+ return 0;
+}
+
+static int br_set_tso(struct net_device *dev, u32 data)
+{
+ struct net_bridge *br = netdev_priv(dev);
+
+ if (data)
+ br->feature_mask |= NETIF_F_TSO;
+ else
+ br->feature_mask &= ~NETIF_F_TSO;
+
+ br_features_recompute(br);
+ return 0;
+}
+
+static int br_set_tx_csum(struct net_device *dev, u32 data)
+{
+ struct net_bridge *br = netdev_priv(dev);
+
+ if (data)
+ br->feature_mask |= NETIF_F_IP_CSUM;
+ else
+ br->feature_mask &= ~NETIF_F_IP_CSUM;
+
+ br_features_recompute(br);
+ return 0;
+}
+
+static struct ethtool_ops br_ethtool_ops = {
+ .get_drvinfo = br_getinfo,
+ .get_link = ethtool_op_get_link,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = br_set_sg,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = br_set_tx_csum,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = br_set_tso,
+};
+
void br_dev_setup(struct net_device *dev)
{
memset(dev->dev_addr, 0, ETH_ALEN);
@@ -96,8 +179,12 @@ void br_dev_setup(struct net_device *dev)
dev->change_mtu = br_change_mtu;
dev->destructor = free_netdev;
SET_MODULE_OWNER(dev);
+ SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
dev->stop = br_dev_stop;
dev->tx_queue_len = 0;
- dev->set_mac_address = NULL;
+ dev->set_mac_address = br_set_mac_address;
dev->priv_flags = IFF_EBRIDGE;
+
+ dev->features = NETIF_F_SG | NETIF_F_FRAGLIST
+ | NETIF_F_HIGHDMA | NETIF_F_TSO | NETIF_F_IP_CSUM;
}
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 975abe254b7a..11321197338e 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -32,9 +32,8 @@
* ethtool, use ethtool_ops. Also, since driver might sleep need to
* not be holding any locks.
*/
-static int br_initial_port_cost(struct net_device *dev)
+static int port_cost(struct net_device *dev)
{
-
struct ethtool_cmd ecmd = { ETHTOOL_GSET };
struct ifreq ifr;
mm_segment_t old_fs;
@@ -58,10 +57,6 @@ static int br_initial_port_cost(struct net_device *dev)
return 2;
case SPEED_10:
return 100;
- default:
- pr_info("bridge: can't decode speed from %s: %d\n",
- dev->name, ecmd.speed);
- return 100;
}
}
@@ -75,6 +70,35 @@ static int br_initial_port_cost(struct net_device *dev)
return 100; /* assume old 10Mbps */
}
+
+/*
+ * Check for port carrier transistions.
+ * Called from work queue to allow for calling functions that
+ * might sleep (such as speed check), and to debounce.
+ */
+static void port_carrier_check(void *arg)
+{
+ struct net_bridge_port *p = arg;
+
+ rtnl_lock();
+ if (netif_carrier_ok(p->dev)) {
+ u32 cost = port_cost(p->dev);
+
+ spin_lock_bh(&p->br->lock);
+ if (p->state == BR_STATE_DISABLED) {
+ p->path_cost = cost;
+ br_stp_enable_port(p);
+ }
+ spin_unlock_bh(&p->br->lock);
+ } else {
+ spin_lock_bh(&p->br->lock);
+ if (p->state != BR_STATE_DISABLED)
+ br_stp_disable_port(p);
+ spin_unlock_bh(&p->br->lock);
+ }
+ rtnl_unlock();
+}
+
static void destroy_nbp(struct net_bridge_port *p)
{
struct net_device *dev = p->dev;
@@ -102,6 +126,9 @@ static void del_nbp(struct net_bridge_port *p)
dev->br_port = NULL;
dev_set_promiscuity(dev, -1);
+ cancel_delayed_work(&p->carrier_check);
+ flush_scheduled_work();
+
spin_lock_bh(&br->lock);
br_stp_disable_port(p);
spin_unlock_bh(&br->lock);
@@ -155,6 +182,7 @@ static struct net_device *new_bridge_dev(const char *name)
br->bridge_id.prio[1] = 0x00;
memset(br->bridge_id.addr, 0, ETH_ALEN);
+ br->feature_mask = dev->features;
br->stp_enabled = 0;
br->designated_root = br->bridge_id;
br->root_path_cost = 0;
@@ -195,10 +223,9 @@ static int find_portno(struct net_bridge *br)
return (index >= BR_MAX_PORTS) ? -EXFULL : index;
}
-/* called with RTNL */
+/* called with RTNL but without bridge lock */
static struct net_bridge_port *new_nbp(struct net_bridge *br,
- struct net_device *dev,
- unsigned long cost)
+ struct net_device *dev)
{
int index;
struct net_bridge_port *p;
@@ -215,12 +242,13 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
p->br = br;
dev_hold(dev);
p->dev = dev;
- p->path_cost = cost;
+ p->path_cost = port_cost(dev);
p->priority = 0x8000 >> BR_PORT_BITS;
dev->br_port = p;
p->port_no = index;
br_init_port(p);
p->state = BR_STATE_DISABLED;
+ INIT_WORK(&p->carrier_check, port_carrier_check, p);
kobject_init(&p->kobj);
return p;
@@ -322,9 +350,8 @@ void br_features_recompute(struct net_bridge *br)
struct net_bridge_port *p;
unsigned long features, checksum;
- features = NETIF_F_SG | NETIF_F_FRAGLIST
- | NETIF_F_HIGHDMA | NETIF_F_TSO;
- checksum = NETIF_F_IP_CSUM; /* least commmon subset */
+ features = br->feature_mask &~ NETIF_F_IP_CSUM;
+ checksum = br->feature_mask & NETIF_F_IP_CSUM;
list_for_each_entry(p, &br->port_list, list) {
if (!(p->dev->features
@@ -351,7 +378,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if (dev->br_port != NULL)
return -EBUSY;
- if (IS_ERR(p = new_nbp(br, dev, br_initial_port_cost(dev))))
+ if (IS_ERR(p = new_nbp(br, dev)))
return PTR_ERR(p);
if ((err = br_fdb_insert(br, p, dev->dev_addr)))
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index b88220a64cd8..c387852f753a 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -53,6 +53,11 @@ int br_handle_frame_finish(struct sk_buff *skb)
/* insert into forwarding database after filtering to avoid spoofing */
br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
+ if (p->state == BR_STATE_LEARNING) {
+ kfree_skb(skb);
+ goto out;
+ }
+
if (br->dev->flags & IFF_PROMISC) {
struct sk_buff *skb2;
@@ -107,9 +112,6 @@ int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
goto err;
- if (p->state == BR_STATE_LEARNING)
- br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
-
if (p->br->stp_enabled &&
!memcmp(dest, bridge_ula, 5) &&
!(dest[5] & 0xF0)) {
@@ -118,9 +120,10 @@ int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
NULL, br_stp_handle_bpdu);
return 1;
}
+ goto err;
}
- else if (p->state == BR_STATE_FORWARDING) {
+ if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) {
if (br_should_route_hook) {
if (br_should_route_hook(pskb))
return 0;
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index d8e36b775125..223f8270daee 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -26,6 +26,7 @@
#include <linux/ip.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
+#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/netfilter_bridge.h>
@@ -33,8 +34,11 @@
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_arp.h>
#include <linux/in_route.h>
+
#include <net/ip.h>
#include <net/ipv6.h>
+#include <net/route.h>
+
#include <asm/uaccess.h>
#include <asm/checksum.h>
#include "br_private.h"
@@ -295,7 +299,7 @@ static int check_hbh_len(struct sk_buff *skb)
len -= 2;
while (len > 0) {
- int optlen = raw[off+1]+2;
+ int optlen = skb->nh.raw[off+1]+2;
switch (skb->nh.raw[off]) {
case IPV6_TLV_PAD0:
@@ -308,18 +312,15 @@ static int check_hbh_len(struct sk_buff *skb)
case IPV6_TLV_JUMBO:
if (skb->nh.raw[off+1] != 4 || (off&3) != 2)
goto bad;
-
pkt_len = ntohl(*(u32*)(skb->nh.raw+off+2));
-
+ if (pkt_len <= IPV6_MAXPLEN ||
+ skb->nh.ipv6h->payload_len)
+ goto bad;
if (pkt_len > skb->len - sizeof(struct ipv6hdr))
goto bad;
- if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
- if (__pskb_trim(skb,
- pkt_len + sizeof(struct ipv6hdr)))
- goto bad;
- if (skb->ip_summed == CHECKSUM_HW)
- skb->ip_summed = CHECKSUM_NONE;
- }
+ if (pskb_trim_rcsum(skb,
+ pkt_len+sizeof(struct ipv6hdr)))
+ goto bad;
break;
default:
if (optlen > len)
@@ -372,6 +373,7 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
if (hdr->nexthdr == NEXTHDR_HOP && check_hbh_len(skb))
goto inhdr_error;
+ nf_bridge_put(skb->nf_bridge);
if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
return NF_DROP;
setup_pre_routing(skb);
@@ -455,6 +457,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
skb->ip_summed = CHECKSUM_NONE;
}
+ nf_bridge_put(skb->nf_bridge);
if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
return NF_DROP;
setup_pre_routing(skb);
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c
index 917311c6828b..a43a9c1d50d7 100644
--- a/net/bridge/br_notify.c
+++ b/net/bridge/br_notify.c
@@ -52,17 +52,9 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
br_stp_recalculate_bridge_id(br);
break;
- case NETDEV_CHANGE: /* device is up but carrier changed */
- if (!(br->dev->flags & IFF_UP))
- break;
-
- if (netif_carrier_ok(dev)) {
- if (p->state == BR_STATE_DISABLED)
- br_stp_enable_port(p);
- } else {
- if (p->state != BR_STATE_DISABLED)
- br_stp_disable_port(p);
- }
+ case NETDEV_CHANGE:
+ if (br->dev->flags & IFF_UP)
+ schedule_delayed_work(&p->carrier_check, BR_PORT_DEBOUNCE);
break;
case NETDEV_FEAT_CHANGE:
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index bdf95a74d8cd..c5bd631ffcd5 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -27,6 +27,10 @@
#define BR_PORT_BITS 10
#define BR_MAX_PORTS (1<<BR_PORT_BITS)
+#define BR_PORT_DEBOUNCE (HZ/10)
+
+#define BR_VERSION "2.1"
+
typedef struct bridge_id bridge_id;
typedef struct mac_addr mac_addr;
typedef __u16 port_id;
@@ -78,6 +82,7 @@ struct net_bridge_port
struct timer_list hold_timer;
struct timer_list message_age_timer;
struct kobject kobj;
+ struct work_struct carrier_check;
struct rcu_head rcu;
};
@@ -90,6 +95,7 @@ struct net_bridge
spinlock_t hash_lock;
struct hlist_head hash[BR_HASH_SIZE];
struct list_head age_list;
+ unsigned long feature_mask;
/* STP */
bridge_id designated_root;
@@ -201,6 +207,7 @@ extern void br_stp_disable_bridge(struct net_bridge *br);
extern void br_stp_enable_port(struct net_bridge_port *p);
extern void br_stp_disable_port(struct net_bridge_port *p);
extern void br_stp_recalculate_bridge_id(struct net_bridge *br);
+extern void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *a);
extern void br_stp_set_bridge_priority(struct net_bridge *br,
u16 newprio);
extern void br_stp_set_port_priority(struct net_bridge_port *p,
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index ac09b6a23523..cc047f7fb6ef 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -120,8 +120,7 @@ void br_stp_disable_port(struct net_bridge_port *p)
}
/* called under bridge lock */
-static void br_stp_change_bridge_id(struct net_bridge *br,
- const unsigned char *addr)
+void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
{
unsigned char oldaddr[6];
struct net_bridge_port *p;
@@ -158,7 +157,7 @@ void br_stp_recalculate_bridge_id(struct net_bridge *br)
list_for_each_entry(p, &br->port_list, list) {
if (addr == br_mac_zero ||
- compare_ether_addr(p->dev->dev_addr, addr) < 0)
+ memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
addr = p->dev->dev_addr;
}
diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig
index c70b3be23026..b84fc6075fe1 100644
--- a/net/bridge/netfilter/Kconfig
+++ b/net/bridge/netfilter/Kconfig
@@ -196,9 +196,13 @@ config BRIDGE_EBT_LOG
To compile it as a module, choose M here. If unsure, say N.
config BRIDGE_EBT_ULOG
- tristate "ebt: ulog support"
+ tristate "ebt: ulog support (OBSOLETE)"
depends on BRIDGE_NF_EBTABLES
help
+ This option enables the old bridge-specific "ebt_ulog" implementation
+ which has been obsoleted by the new "nfnetlink_log" code (see
+ CONFIG_NETFILTER_NETLINK_LOG).
+
This option adds the ulog watcher, that you can use in any rule
in any ebtables table. The packet is passed to a userspace
logging daemon using netlink multicast sockets. This differs
diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c
index 662975be3d1d..9f6e0193ae10 100644
--- a/net/bridge/netfilter/ebt_log.c
+++ b/net/bridge/netfilter/ebt_log.c
@@ -3,13 +3,16 @@
*
* Authors:
* Bart De Schuymer <bdschuym@pandora.be>
+ * Harald Welte <laforge@netfilter.org>
*
* April, 2002
*
*/
+#include <linux/in.h>
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_bridge/ebt_log.h>
+#include <linux/netfilter.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/if_arp.h>
@@ -55,27 +58,30 @@ static void print_MAC(unsigned char *p)
}
#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
-static void ebt_log(const struct sk_buff *skb, unsigned int hooknr,
- const struct net_device *in, const struct net_device *out,
- const void *data, unsigned int datalen)
+static void
+ebt_log_packet(unsigned int pf, unsigned int hooknum,
+ const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, const struct nf_loginfo *loginfo,
+ const char *prefix)
{
- struct ebt_log_info *info = (struct ebt_log_info *)data;
- char level_string[4] = "< >";
+ unsigned int bitmask;
- level_string[1] = '0' + info->loglevel;
spin_lock_bh(&ebt_log_lock);
- printk(level_string);
- printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
- out ? out->name : "");
+ printk("<%c>%s IN=%s OUT=%s MAC source = ", '0' + loginfo->u.log.level,
+ prefix, in ? in->name : "", out ? out->name : "");
- printk("MAC source = ");
print_MAC(eth_hdr(skb)->h_source);
printk("MAC dest = ");
print_MAC(eth_hdr(skb)->h_dest);
printk("proto = 0x%04x", ntohs(eth_hdr(skb)->h_proto));
- if ((info->bitmask & EBT_LOG_IP) && eth_hdr(skb)->h_proto ==
+ if (loginfo->type == NF_LOG_TYPE_LOG)
+ bitmask = loginfo->u.log.logflags;
+ else
+ bitmask = NF_LOG_MASK;
+
+ if ((bitmask & EBT_LOG_IP) && eth_hdr(skb)->h_proto ==
htons(ETH_P_IP)){
struct iphdr _iph, *ih;
@@ -84,10 +90,9 @@ static void ebt_log(const struct sk_buff *skb, unsigned int hooknr,
printk(" INCOMPLETE IP header");
goto out;
}
- printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
- NIPQUAD(ih->saddr), NIPQUAD(ih->daddr));
- printk(" IP tos=0x%02X, IP proto=%d", ih->tos,
- ih->protocol);
+ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u, IP "
+ "tos=0x%02X, IP proto=%d", NIPQUAD(ih->saddr),
+ NIPQUAD(ih->daddr), ih->tos, ih->protocol);
if (ih->protocol == IPPROTO_TCP ||
ih->protocol == IPPROTO_UDP) {
struct tcpudphdr _ports, *pptr;
@@ -104,7 +109,7 @@ static void ebt_log(const struct sk_buff *skb, unsigned int hooknr,
goto out;
}
- if ((info->bitmask & EBT_LOG_ARP) &&
+ if ((bitmask & EBT_LOG_ARP) &&
((eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) ||
(eth_hdr(skb)->h_proto == htons(ETH_P_RARP)))) {
struct arphdr _arph, *ah;
@@ -144,6 +149,21 @@ static void ebt_log(const struct sk_buff *skb, unsigned int hooknr,
out:
printk("\n");
spin_unlock_bh(&ebt_log_lock);
+
+}
+
+static void ebt_log(const struct sk_buff *skb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_log_info *info = (struct ebt_log_info *)data;
+ struct nf_loginfo li;
+
+ li.type = NF_LOG_TYPE_LOG;
+ li.u.log.level = info->loglevel;
+ li.u.log.logflags = info->bitmask;
+
+ nf_log_packet(PF_BRIDGE, hooknr, skb, in, out, &li, info->prefix);
}
static struct ebt_watcher log =
@@ -154,13 +174,32 @@ static struct ebt_watcher log =
.me = THIS_MODULE,
};
+static struct nf_logger ebt_log_logger = {
+ .name = "ebt_log",
+ .logfn = &ebt_log_packet,
+ .me = THIS_MODULE,
+};
+
static int __init init(void)
{
- return ebt_register_watcher(&log);
+ int ret;
+
+ ret = ebt_register_watcher(&log);
+ if (ret < 0)
+ return ret;
+ if (nf_log_register(PF_BRIDGE, &ebt_log_logger) < 0) {
+ printk(KERN_WARNING "ebt_log: not logging via system console "
+ "since somebody else already registered for PF_INET\n");
+ /* we cannot make module load fail here, since otherwise
+ * ebtables userspace would abort */
+ }
+
+ return 0;
}
static void __exit fini(void)
{
+ nf_log_unregister_logger(&ebt_log_logger);
ebt_unregister_watcher(&log);
}
diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c
index aae26ae2e61f..ce617b3dbbb8 100644
--- a/net/bridge/netfilter/ebt_ulog.c
+++ b/net/bridge/netfilter/ebt_ulog.c
@@ -3,6 +3,7 @@
*
* Authors:
* Bart De Schuymer <bdschuym@pandora.be>
+ * Harald Welte <laforge@netfilter.org>
*
* November, 2004
*
@@ -115,14 +116,13 @@ static struct sk_buff *ulog_alloc_skb(unsigned int size)
return skb;
}
-static void ebt_ulog(const struct sk_buff *skb, unsigned int hooknr,
+static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
- const void *data, unsigned int datalen)
+ const struct ebt_ulog_info *uloginfo, const char *prefix)
{
ebt_ulog_packet_msg_t *pm;
size_t size, copy_len;
struct nlmsghdr *nlh;
- struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)data;
unsigned int group = uloginfo->nlgroup;
ebt_ulog_buff_t *ub = &ulog_buffers[group];
spinlock_t *lock = &ub->lock;
@@ -216,6 +216,39 @@ alloc_failure:
goto unlock;
}
+/* this function is registered with the netfilter core */
+static void ebt_log_packet(unsigned int pf, unsigned int hooknum,
+ const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, const struct nf_loginfo *li,
+ const char *prefix)
+{
+ struct ebt_ulog_info loginfo;
+
+ if (!li || li->type != NF_LOG_TYPE_ULOG) {
+ loginfo.nlgroup = EBT_ULOG_DEFAULT_NLGROUP;
+ loginfo.cprange = 0;
+ loginfo.qthreshold = EBT_ULOG_DEFAULT_QTHRESHOLD;
+ loginfo.prefix[0] = '\0';
+ } else {
+ loginfo.nlgroup = li->u.ulog.group;
+ loginfo.cprange = li->u.ulog.copy_len;
+ loginfo.qthreshold = li->u.ulog.qthreshold;
+ strlcpy(loginfo.prefix, prefix, sizeof(loginfo.prefix));
+ }
+
+ ebt_ulog_packet(hooknum, skb, in, out, &loginfo, prefix);
+}
+
+static void ebt_ulog(const struct sk_buff *skb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)data;
+
+ ebt_ulog_packet(hooknr, skb, in, out, uloginfo, NULL);
+}
+
+
static int ebt_ulog_check(const char *tablename, unsigned int hookmask,
const struct ebt_entry *e, void *data, unsigned int datalen)
{
@@ -240,6 +273,12 @@ static struct ebt_watcher ulog = {
.me = THIS_MODULE,
};
+static struct nf_logger ebt_ulog_logger = {
+ .name = EBT_ULOG_WATCHER,
+ .logfn = &ebt_log_packet,
+ .me = THIS_MODULE,
+};
+
static int __init init(void)
{
int i, ret = 0;
@@ -265,6 +304,13 @@ static int __init init(void)
else if ((ret = ebt_register_watcher(&ulog)))
sock_release(ebtulognl->sk_socket);
+ if (nf_log_register(PF_BRIDGE, &ebt_ulog_logger) < 0) {
+ printk(KERN_WARNING "ebt_ulog: not logging via ulog "
+ "since somebody else already registered for PF_BRIDGE\n");
+ /* we cannot make module load fail here, since otherwise
+ * ebtables userspace would abort */
+ }
+
return ret;
}
@@ -273,6 +319,7 @@ static void __exit fini(void)
ebt_ulog_buff_t *ub;
int i;
+ nf_log_unregister_logger(&ebt_ulog_logger);
ebt_unregister_watcher(&ulog);
for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
ub = &ulog_buffers[i];
diff --git a/net/core/datagram.c b/net/core/datagram.c
index 1bcfef51ac58..f8d322e1ea92 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -47,6 +47,7 @@
#include <linux/rtnetlink.h>
#include <linux/poll.h>
#include <linux/highmem.h>
+#include <linux/spinlock.h>
#include <net/protocol.h>
#include <linux/skbuff.h>
@@ -200,6 +201,41 @@ void skb_free_datagram(struct sock *sk, struct sk_buff *skb)
}
/**
+ * skb_kill_datagram - Free a datagram skbuff forcibly
+ * @sk: socket
+ * @skb: datagram skbuff
+ * @flags: MSG_ flags
+ *
+ * This function frees a datagram skbuff that was received by
+ * skb_recv_datagram. The flags argument must match the one
+ * used for skb_recv_datagram.
+ *
+ * If the MSG_PEEK flag is set, and the packet is still on the
+ * receive queue of the socket, it will be taken off the queue
+ * before it is freed.
+ *
+ * This function currently only disables BH when acquiring the
+ * sk_receive_queue lock. Therefore it must not be used in a
+ * context where that lock is acquired in an IRQ context.
+ */
+
+void skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags)
+{
+ if (flags & MSG_PEEK) {
+ spin_lock_bh(&sk->sk_receive_queue.lock);
+ if (skb == skb_peek(&sk->sk_receive_queue)) {
+ __skb_unlink(skb, &sk->sk_receive_queue);
+ atomic_dec(&skb->users);
+ }
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+ }
+
+ kfree_skb(skb);
+}
+
+EXPORT_SYMBOL(skb_kill_datagram);
+
+/**
* skb_copy_datagram_iovec - Copy a datagram to an iovec.
* @skb: buffer to copy
* @offset: offset in the buffer to start copying from
diff --git a/net/core/dev.c b/net/core/dev.c
index a5efc9ae010b..29ba109d3e54 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3276,7 +3276,6 @@ EXPORT_SYMBOL(dev_close);
EXPORT_SYMBOL(dev_get_by_flags);
EXPORT_SYMBOL(dev_get_by_index);
EXPORT_SYMBOL(dev_get_by_name);
-EXPORT_SYMBOL(dev_ioctl);
EXPORT_SYMBOL(dev_open);
EXPORT_SYMBOL(dev_queue_xmit);
EXPORT_SYMBOL(dev_remove_pack);
diff --git a/net/core/filter.c b/net/core/filter.c
index 2841bfce29d6..8964d3445588 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -13,6 +13,7 @@
* 2 of the License, or (at your option) any later version.
*
* Andi Kleen - Fix a few bad bugs and races.
+ * Kris Katterjohn - Added many additional checks in sk_chk_filter()
*/
#include <linux/module.h>
@@ -250,7 +251,7 @@ load_b:
mem[fentry->k] = X;
continue;
default:
- /* Invalid instruction counts as RET */
+ WARN_ON(1);
return 0;
}
@@ -283,8 +284,8 @@ load_b:
*
* Check the user's filter code. If we let some ugly
* filter code slip through kaboom! The filter must contain
- * no references or jumps that are out of range, no illegal instructions
- * and no backward jumps. It must end with a RET instruction
+ * no references or jumps that are out of range, no illegal
+ * instructions, and must end with a RET instruction.
*
* Returns 0 if the rule set is legal or a negative errno code if not.
*/
@@ -293,45 +294,92 @@ int sk_chk_filter(struct sock_filter *filter, int flen)
struct sock_filter *ftest;
int pc;
- if (((unsigned int)flen >= (~0U / sizeof(struct sock_filter))) || flen == 0)
+ if (flen == 0 || flen > BPF_MAXINSNS)
return -EINVAL;
/* check the filter code now */
for (pc = 0; pc < flen; pc++) {
/* all jumps are forward as they are not signed */
ftest = &filter[pc];
- if (BPF_CLASS(ftest->code) == BPF_JMP) {
- /* but they mustn't jump off the end */
- if (BPF_OP(ftest->code) == BPF_JA) {
- /*
- * Note, the large ftest->k might cause loops.
- * Compare this with conditional jumps below,
- * where offsets are limited. --ANK (981016)
- */
- if (ftest->k >= (unsigned)(flen-pc-1))
- return -EINVAL;
- } else {
- /* for conditionals both must be safe */
- if (pc + ftest->jt +1 >= flen ||
- pc + ftest->jf +1 >= flen)
- return -EINVAL;
- }
- }
- /* check for division by zero -Kris Katterjohn 2005-10-30 */
- if (ftest->code == (BPF_ALU|BPF_DIV|BPF_K) && ftest->k == 0)
- return -EINVAL;
+ /* Only allow valid instructions */
+ switch (ftest->code) {
+ case BPF_ALU|BPF_ADD|BPF_K:
+ case BPF_ALU|BPF_ADD|BPF_X:
+ case BPF_ALU|BPF_SUB|BPF_K:
+ case BPF_ALU|BPF_SUB|BPF_X:
+ case BPF_ALU|BPF_MUL|BPF_K:
+ case BPF_ALU|BPF_MUL|BPF_X:
+ case BPF_ALU|BPF_DIV|BPF_X:
+ case BPF_ALU|BPF_AND|BPF_K:
+ case BPF_ALU|BPF_AND|BPF_X:
+ case BPF_ALU|BPF_OR|BPF_K:
+ case BPF_ALU|BPF_OR|BPF_X:
+ case BPF_ALU|BPF_LSH|BPF_K:
+ case BPF_ALU|BPF_LSH|BPF_X:
+ case BPF_ALU|BPF_RSH|BPF_K:
+ case BPF_ALU|BPF_RSH|BPF_X:
+ case BPF_ALU|BPF_NEG:
+ case BPF_LD|BPF_W|BPF_ABS:
+ case BPF_LD|BPF_H|BPF_ABS:
+ case BPF_LD|BPF_B|BPF_ABS:
+ case BPF_LD|BPF_W|BPF_LEN:
+ case BPF_LD|BPF_W|BPF_IND:
+ case BPF_LD|BPF_H|BPF_IND:
+ case BPF_LD|BPF_B|BPF_IND:
+ case BPF_LD|BPF_IMM:
+ case BPF_LDX|BPF_W|BPF_LEN:
+ case BPF_LDX|BPF_B|BPF_MSH:
+ case BPF_LDX|BPF_IMM:
+ case BPF_MISC|BPF_TAX:
+ case BPF_MISC|BPF_TXA:
+ case BPF_RET|BPF_K:
+ case BPF_RET|BPF_A:
+ break;
+
+ /* Some instructions need special checks */
- /* check that memory operations use valid addresses. */
- if (ftest->k >= BPF_MEMWORDS) {
- /* but it might not be a memory operation... */
- switch (ftest->code) {
- case BPF_ST:
- case BPF_STX:
- case BPF_LD|BPF_MEM:
- case BPF_LDX|BPF_MEM:
+ case BPF_ALU|BPF_DIV|BPF_K:
+ /* check for division by zero */
+ if (ftest->k == 0)
return -EINVAL;
- }
+ break;
+
+ case BPF_LD|BPF_MEM:
+ case BPF_LDX|BPF_MEM:
+ case BPF_ST:
+ case BPF_STX:
+ /* check for invalid memory addresses */
+ if (ftest->k >= BPF_MEMWORDS)
+ return -EINVAL;
+ break;
+
+ case BPF_JMP|BPF_JA:
+ /*
+ * Note, the large ftest->k might cause loops.
+ * Compare this with conditional jumps below,
+ * where offsets are limited. --ANK (981016)
+ */
+ if (ftest->k >= (unsigned)(flen-pc-1))
+ return -EINVAL;
+ break;
+
+ case BPF_JMP|BPF_JEQ|BPF_K:
+ case BPF_JMP|BPF_JEQ|BPF_X:
+ case BPF_JMP|BPF_JGE|BPF_K:
+ case BPF_JMP|BPF_JGE|BPF_X:
+ case BPF_JMP|BPF_JGT|BPF_K:
+ case BPF_JMP|BPF_JGT|BPF_X:
+ case BPF_JMP|BPF_JSET|BPF_K:
+ case BPF_JMP|BPF_JSET|BPF_X:
+ /* for conditionals both must be safe */
+ if (pc + ftest->jt + 1 >= flen ||
+ pc + ftest->jf + 1 >= flen)
+ return -EINVAL;
+ break;
+
+ default:
+ return -EINVAL;
}
}
@@ -360,7 +408,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
int err;
/* Make sure new filter is there and in the right amounts. */
- if (fprog->filter == NULL || fprog->len > BPF_MAXINSNS)
+ if (fprog->filter == NULL)
return -EINVAL;
fp = sock_kmalloc(sk, fsize+sizeof(*fp), GFP_KERNEL);
diff --git a/net/core/flow.c b/net/core/flow.c
index 7e95b39de9fd..c4f25385029f 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -23,6 +23,7 @@
#include <net/flow.h>
#include <asm/atomic.h>
#include <asm/semaphore.h>
+#include <linux/security.h>
struct flow_cache_entry {
struct flow_cache_entry *next;
@@ -30,6 +31,7 @@ struct flow_cache_entry {
u8 dir;
struct flowi key;
u32 genid;
+ u32 sk_sid;
void *object;
atomic_t *object_ref;
};
@@ -162,7 +164,7 @@ static int flow_key_compare(struct flowi *key1, struct flowi *key2)
return 0;
}
-void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
+void *flow_cache_lookup(struct flowi *key, u32 sk_sid, u16 family, u8 dir,
flow_resolve_t resolver)
{
struct flow_cache_entry *fle, **head;
@@ -186,6 +188,7 @@ void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
for (fle = *head; fle; fle = fle->next) {
if (fle->family == family &&
fle->dir == dir &&
+ fle->sk_sid == sk_sid &&
flow_key_compare(key, &fle->key) == 0) {
if (fle->genid == atomic_read(&flow_cache_genid)) {
void *ret = fle->object;
@@ -210,6 +213,7 @@ void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
*head = fle;
fle->family = family;
fle->dir = dir;
+ fle->sk_sid = sk_sid;
memcpy(&fle->key, key, sizeof(*key));
fle->object = NULL;
flow_count(cpu)++;
@@ -221,7 +225,7 @@ nocache:
void *obj;
atomic_t *obj_ref;
- resolver(key, family, dir, &obj, &obj_ref);
+ resolver(key, sk_sid, family, dir, &obj, &obj_ref);
if (fle) {
fle->genid = atomic_read(&flow_cache_genid);
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 49424a42a2c0..281a632fa6a6 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -13,6 +13,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/string.h>
+#include <linux/if_arp.h>
#include <linux/inetdevice.h>
#include <linux/inet.h>
#include <linux/interrupt.h>
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 7fc3e9e28c34..06cad2d63e8a 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -487,9 +487,9 @@ static unsigned int fmt_ip6(char *s,const char ip[16]);
/* Module parameters, defaults. */
static int pg_count_d = 1000; /* 1000 pkts by default */
-static int pg_delay_d = 0;
-static int pg_clone_skb_d = 0;
-static int debug = 0;
+static int pg_delay_d;
+static int pg_clone_skb_d;
+static int debug;
static DECLARE_MUTEX(pktgen_sem);
static struct pktgen_thread *pktgen_threads = NULL;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 83fee37de38e..070f91cfde59 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -135,17 +135,13 @@ void skb_under_panic(struct sk_buff *skb, int sz, void *here)
struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
int fclone)
{
+ struct skb_shared_info *shinfo;
struct sk_buff *skb;
u8 *data;
/* Get the HEAD */
- if (fclone)
- skb = kmem_cache_alloc(skbuff_fclone_cache,
- gfp_mask & ~__GFP_DMA);
- else
- skb = kmem_cache_alloc(skbuff_head_cache,
- gfp_mask & ~__GFP_DMA);
-
+ skb = kmem_cache_alloc(fclone ? skbuff_fclone_cache : skbuff_head_cache,
+ gfp_mask & ~__GFP_DMA);
if (!skb)
goto out;
@@ -162,6 +158,16 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
skb->data = data;
skb->tail = data;
skb->end = data + size;
+ /* make sure we initialize shinfo sequentially */
+ shinfo = skb_shinfo(skb);
+ atomic_set(&shinfo->dataref, 1);
+ shinfo->nr_frags = 0;
+ shinfo->tso_size = 0;
+ shinfo->tso_segs = 0;
+ shinfo->ufo_size = 0;
+ shinfo->ip6_frag_id = 0;
+ shinfo->frag_list = NULL;
+
if (fclone) {
struct sk_buff *child = skb + 1;
atomic_t *fclone_ref = (atomic_t *) (child + 1);
@@ -171,13 +177,6 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
child->fclone = SKB_FCLONE_UNAVAILABLE;
}
- atomic_set(&(skb_shinfo(skb)->dataref), 1);
- skb_shinfo(skb)->nr_frags = 0;
- skb_shinfo(skb)->tso_size = 0;
- skb_shinfo(skb)->tso_segs = 0;
- skb_shinfo(skb)->frag_list = NULL;
- skb_shinfo(skb)->ufo_size = 0;
- skb_shinfo(skb)->ip6_frag_id = 0;
out:
return skb;
nodata:
diff --git a/net/core/sock.c b/net/core/sock.c
index 13cc3be4f056..6465b0e4c8cb 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1488,7 +1488,7 @@ int proto_register(struct proto *prot, int alloc_slab)
}
}
- if (prot->twsk_obj_size) {
+ if (prot->twsk_prot != NULL) {
static const char mask[] = "tw_sock_%s";
timewait_sock_slab_name = kmalloc(strlen(prot->name) + sizeof(mask) - 1, GFP_KERNEL);
@@ -1497,11 +1497,12 @@ int proto_register(struct proto *prot, int alloc_slab)
goto out_free_request_sock_slab;
sprintf(timewait_sock_slab_name, mask, prot->name);
- prot->twsk_slab = kmem_cache_create(timewait_sock_slab_name,
- prot->twsk_obj_size,
- 0, SLAB_HWCACHE_ALIGN,
- NULL, NULL);
- if (prot->twsk_slab == NULL)
+ prot->twsk_prot->twsk_slab =
+ kmem_cache_create(timewait_sock_slab_name,
+ prot->twsk_prot->twsk_obj_size,
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (prot->twsk_prot->twsk_slab == NULL)
goto out_free_timewait_sock_slab_name;
}
}
@@ -1548,12 +1549,12 @@ void proto_unregister(struct proto *prot)
prot->rsk_prot->slab = NULL;
}
- if (prot->twsk_slab != NULL) {
- const char *name = kmem_cache_name(prot->twsk_slab);
+ if (prot->twsk_prot != NULL && prot->twsk_prot->twsk_slab != NULL) {
+ const char *name = kmem_cache_name(prot->twsk_prot->twsk_slab);
- kmem_cache_destroy(prot->twsk_slab);
+ kmem_cache_destroy(prot->twsk_prot->twsk_slab);
kfree(name);
- prot->twsk_slab = NULL;
+ prot->twsk_prot->twsk_slab = NULL;
}
}
diff --git a/net/core/stream.c b/net/core/stream.c
index 15bfd03e8024..35e25259fd95 100644
--- a/net/core/stream.c
+++ b/net/core/stream.c
@@ -55,8 +55,9 @@ int sk_stream_wait_connect(struct sock *sk, long *timeo_p)
int done;
do {
- if (sk->sk_err)
- return sock_error(sk);
+ int err = sock_error(sk);
+ if (err)
+ return err;
if ((1 << sk->sk_state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV))
return -EPIPE;
if (!*timeo_p)
@@ -67,6 +68,7 @@ int sk_stream_wait_connect(struct sock *sk, long *timeo_p)
prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
sk->sk_write_pending++;
done = sk_wait_event(sk, timeo_p,
+ !sk->sk_err &&
!((1 << sk->sk_state) &
~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)));
finish_wait(sk->sk_sleep, &wait);
@@ -137,7 +139,9 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
sk->sk_write_pending++;
- sk_wait_event(sk, &current_timeo, sk_stream_memory_free(sk) &&
+ sk_wait_event(sk, &current_timeo, !sk->sk_err &&
+ !(sk->sk_shutdown & SEND_SHUTDOWN) &&
+ sk_stream_memory_free(sk) &&
vm_wait);
sk->sk_write_pending--;
diff --git a/net/dccp/Makefile b/net/dccp/Makefile
index 344a8da153fc..87b27fff6e3b 100644
--- a/net/dccp/Makefile
+++ b/net/dccp/Makefile
@@ -1,3 +1,7 @@
+obj-$(CONFIG_IPV6) += dccp_ipv6.o
+
+dccp_ipv6-y := ipv6.o
+
obj-$(CONFIG_IP_DCCP) += dccp.o
dccp-y := ccid.o input.o ipv4.o minisocks.o options.o output.o proto.o \
diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c
index c9a62cca22fc..ce9cb77c5c29 100644
--- a/net/dccp/ackvec.c
+++ b/net/dccp/ackvec.c
@@ -55,8 +55,8 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)
from = av->dccpav_buf + av->dccpav_buf_head;
/* Check if buf_head wraps */
- if (av->dccpav_buf_head + len > av->dccpav_vec_len) {
- const u32 tailsize = (av->dccpav_vec_len - av->dccpav_buf_head);
+ if ((int)av->dccpav_buf_head + len > av->dccpav_vec_len) {
+ const u32 tailsize = av->dccpav_vec_len - av->dccpav_buf_head;
memcpy(to, from, tailsize);
to += tailsize;
@@ -93,8 +93,14 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)
struct dccp_ackvec *dccp_ackvec_alloc(const unsigned int len,
const gfp_t priority)
{
- struct dccp_ackvec *av = kmalloc(sizeof(*av) + len, priority);
+ struct dccp_ackvec *av;
+ BUG_ON(len == 0);
+
+ if (len > DCCP_MAX_ACKVEC_LEN)
+ return NULL;
+
+ av = kmalloc(sizeof(*av) + len, priority);
if (av != NULL) {
av->dccpav_buf_len = len;
av->dccpav_buf_head =
@@ -117,13 +123,13 @@ void dccp_ackvec_free(struct dccp_ackvec *av)
}
static inline u8 dccp_ackvec_state(const struct dccp_ackvec *av,
- const unsigned int index)
+ const u8 index)
{
return av->dccpav_buf[index] & DCCP_ACKVEC_STATE_MASK;
}
static inline u8 dccp_ackvec_len(const struct dccp_ackvec *av,
- const unsigned int index)
+ const u8 index)
{
return av->dccpav_buf[index] & DCCP_ACKVEC_LEN_MASK;
}
@@ -135,7 +141,7 @@ static inline u8 dccp_ackvec_len(const struct dccp_ackvec *av,
*/
static inline int dccp_ackvec_set_buf_head_state(struct dccp_ackvec *av,
const unsigned int packets,
- const unsigned char state)
+ const unsigned char state)
{
unsigned int gap;
signed long new_head;
@@ -223,7 +229,7 @@ int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk,
* could reduce the complexity of this scan.)
*/
u64 delta = dccp_delta_seqno(ackno, av->dccpav_buf_ackno);
- unsigned int index = av->dccpav_buf_head;
+ u8 index = av->dccpav_buf_head;
while (1) {
const u8 len = dccp_ackvec_len(av, index);
@@ -291,7 +297,7 @@ void dccp_ackvec_print(const struct dccp_ackvec *av)
}
#endif
-static void dccp_ackvec_trow_away_ack_record(struct dccp_ackvec *av)
+static void dccp_ackvec_throw_away_ack_record(struct dccp_ackvec *av)
{
/*
* As we're keeping track of the ack vector size (dccpav_vec_len) and
@@ -301,9 +307,10 @@ static void dccp_ackvec_trow_away_ack_record(struct dccp_ackvec *av)
* draft-ietf-dccp-spec-11.txt Appendix A. -acme
*/
#if 0
- av->dccpav_buf_tail = av->dccpav_ack_ptr + 1;
- if (av->dccpav_buf_tail >= av->dccpav_vec_len)
- av->dccpav_buf_tail -= av->dccpav_vec_len;
+ u32 new_buf_tail = av->dccpav_ack_ptr + 1;
+ if (new_buf_tail >= av->dccpav_vec_len)
+ new_buf_tail -= av->dccpav_vec_len;
+ av->dccpav_buf_tail = new_buf_tail;
#endif
av->dccpav_vec_len -= av->dccpav_sent_len;
}
@@ -326,7 +333,7 @@ void dccp_ackvec_check_rcv_ackno(struct dccp_ackvec *av, struct sock *sk,
debug_prefix, 1,
(unsigned long long)av->dccpav_ack_seqno,
(unsigned long long)av->dccpav_ack_ackno);
- dccp_ackvec_trow_away_ack_record(av);
+ dccp_ackvec_throw_away_ack_record(av);
av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1;
}
}
@@ -389,7 +396,7 @@ static void dccp_ackvec_check_rcv_ackvector(struct dccp_ackvec *av,
av->dccpav_ack_seqno,
(unsigned long long)
av->dccpav_ack_ackno);
- dccp_ackvec_trow_away_ack_record(av);
+ dccp_ackvec_throw_away_ack_record(av);
}
/*
* If dccpav_ack_seqno was not received, no problem
diff --git a/net/dccp/ackvec.h b/net/dccp/ackvec.h
index d0fd6c60c574..f7dfb5f67b87 100644
--- a/net/dccp/ackvec.h
+++ b/net/dccp/ackvec.h
@@ -54,16 +54,16 @@
* @dccpav_buf - circular buffer of acknowledgeable packets
*/
struct dccp_ackvec {
- unsigned int dccpav_buf_head;
- unsigned int dccpav_buf_tail;
u64 dccpav_buf_ackno;
u64 dccpav_ack_seqno;
u64 dccpav_ack_ackno;
- unsigned int dccpav_ack_ptr;
- unsigned int dccpav_sent_len;
- unsigned int dccpav_vec_len;
- unsigned int dccpav_buf_len;
struct timeval dccpav_time;
+ u8 dccpav_buf_head;
+ u8 dccpav_buf_tail;
+ u8 dccpav_ack_ptr;
+ u8 dccpav_sent_len;
+ u8 dccpav_vec_len;
+ u8 dccpav_buf_len;
u8 dccpav_buf_nonce;
u8 dccpav_ack_nonce;
u8 dccpav_buf[0];
diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h
index c37eeeaf5c6e..de681c6ad081 100644
--- a/net/dccp/ccid.h
+++ b/net/dccp/ccid.h
@@ -21,6 +21,8 @@
#define CCID_MAX 255
+struct tcp_info;
+
struct ccid {
unsigned char ccid_id;
const char *ccid_name;
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
index f97b85d55ad8..93f26dd6e6cb 100644
--- a/net/dccp/dccp.h
+++ b/net/dccp/dccp.h
@@ -59,7 +59,7 @@ extern void dccp_time_wait(struct sock *sk, int state, int timeo);
#define DCCP_RTO_MAX ((unsigned)(120 * HZ)) /* FIXME: using TCP value */
-extern struct proto dccp_v4_prot;
+extern struct proto dccp_prot;
/* is seq1 < seq2 ? */
static inline int before48(const u64 seq1, const u64 seq2)
@@ -228,6 +228,9 @@ extern int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
extern int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
const struct dccp_hdr *dh, const unsigned len);
+extern int dccp_v4_init_sock(struct sock *sk);
+extern int dccp_v4_destroy_sock(struct sock *sk);
+
extern void dccp_close(struct sock *sk, long timeout);
extern struct sk_buff *dccp_make_response(struct sock *sk,
struct dst_entry *dst,
@@ -238,6 +241,7 @@ extern struct sk_buff *dccp_make_reset(struct sock *sk,
extern int dccp_connect(struct sock *sk);
extern int dccp_disconnect(struct sock *sk, int flags);
+extern void dccp_unhash(struct sock *sk);
extern int dccp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen);
extern int dccp_setsockopt(struct sock *sk, int level, int optname,
@@ -249,6 +253,13 @@ extern int dccp_recvmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len, int nonblock,
int flags, int *addr_len);
extern void dccp_shutdown(struct sock *sk, int how);
+extern int inet_dccp_listen(struct socket *sock, int backlog);
+extern unsigned int dccp_poll(struct file *file, struct socket *sock,
+ poll_table *wait);
+extern void dccp_v4_send_check(struct sock *sk, int len,
+ struct sk_buff *skb);
+extern int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
+ int addr_len);
extern int dccp_v4_checksum(const struct sk_buff *skb,
const u32 saddr, const u32 daddr);
@@ -256,6 +267,17 @@ extern int dccp_v4_checksum(const struct sk_buff *skb,
extern int dccp_v4_send_reset(struct sock *sk,
enum dccp_reset_codes code);
extern void dccp_send_close(struct sock *sk, const int active);
+extern int dccp_invalid_packet(struct sk_buff *skb);
+
+static inline int dccp_bad_service_code(const struct sock *sk,
+ const __u32 service)
+{
+ const struct dccp_sock *dp = dccp_sk(sk);
+
+ if (dp->dccps_service == service)
+ return 0;
+ return !dccp_list_has_service(dp->dccps_service_list, service);
+}
struct dccp_skb_cb {
__u8 dccpd_type:4;
diff --git a/net/dccp/diag.c b/net/dccp/diag.c
index f675d8e642d3..3f78c00e3822 100644
--- a/net/dccp/diag.c
+++ b/net/dccp/diag.c
@@ -28,7 +28,7 @@ static void dccp_get_info(struct sock *sk, struct tcp_info *info)
info->tcpi_retransmits = icsk->icsk_retransmits;
info->tcpi_probes = icsk->icsk_probes_out;
info->tcpi_backoff = icsk->icsk_backoff;
- info->tcpi_pmtu = dp->dccps_pmtu_cookie;
+ info->tcpi_pmtu = icsk->icsk_pmtu_cookie;
if (dp->dccps_options.dccpo_send_ack_vector)
info->tcpi_options |= TCPI_OPT_SACK;
diff --git a/net/dccp/input.c b/net/dccp/input.c
index 3454d5941900..b6cba72b44e8 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -151,29 +151,12 @@ static int dccp_check_seqno(struct sock *sk, struct sk_buff *skb)
return 0;
}
-int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
- const struct dccp_hdr *dh, const unsigned len)
+static inline int __dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
+ const struct dccp_hdr *dh,
+ const unsigned len)
{
struct dccp_sock *dp = dccp_sk(sk);
- if (dccp_check_seqno(sk, skb))
- goto discard;
-
- if (dccp_parse_options(sk, skb))
- goto discard;
-
- if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
- dccp_event_ack_recv(sk, skb);
-
- if (dp->dccps_options.dccpo_send_ack_vector &&
- dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
- DCCP_SKB_CB(skb)->dccpd_seq,
- DCCP_ACKVEC_STATE_RECEIVED))
- goto discard;
-
- ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
- ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
-
switch (dccp_hdr(skb)->dccph_type) {
case DCCP_PKT_DATAACK:
case DCCP_PKT_DATA:
@@ -250,6 +233,37 @@ discard:
return 0;
}
+int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
+ const struct dccp_hdr *dh, const unsigned len)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+
+ if (dccp_check_seqno(sk, skb))
+ goto discard;
+
+ if (dccp_parse_options(sk, skb))
+ goto discard;
+
+ if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
+ dccp_event_ack_recv(sk, skb);
+
+ if (dp->dccps_options.dccpo_send_ack_vector &&
+ dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
+ DCCP_SKB_CB(skb)->dccpd_seq,
+ DCCP_ACKVEC_STATE_RECEIVED))
+ goto discard;
+
+ ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
+ ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
+
+ return __dccp_rcv_established(sk, skb, dh, len);
+discard:
+ __kfree_skb(skb);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(dccp_rcv_established);
+
static int dccp_rcv_request_sent_state_process(struct sock *sk,
struct sk_buff *skb,
const struct dccp_hdr *dh,
@@ -286,6 +300,12 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,
goto out_invalid_packet;
}
+ if (dp->dccps_options.dccpo_send_ack_vector &&
+ dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
+ DCCP_SKB_CB(skb)->dccpd_seq,
+ DCCP_ACKVEC_STATE_RECEIVED))
+ goto out_invalid_packet; /* FIXME: change error code */
+
dp->dccps_isr = DCCP_SKB_CB(skb)->dccpd_seq;
dccp_update_gsr(sk, dp->dccps_isr);
/*
@@ -309,7 +329,7 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,
goto out_invalid_packet;
}
- dccp_sync_mss(sk, dp->dccps_pmtu_cookie);
+ dccp_sync_mss(sk, icsk->icsk_pmtu_cookie);
/*
* Step 10: Process REQUEST state (second part)
@@ -329,7 +349,7 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,
dccp_set_state(sk, DCCP_PARTOPEN);
/* Make sure socket is routed, for correct metrics. */
- inet_sk_rebuild_header(sk);
+ icsk->icsk_af_ops->rebuild_header(sk);
if (!sock_flag(sk, SOCK_DEAD)) {
sk->sk_state_change(sk);
@@ -398,9 +418,9 @@ static int dccp_rcv_respond_partopen_state_process(struct sock *sk,
if (dh->dccph_type == DCCP_PKT_DATAACK ||
dh->dccph_type == DCCP_PKT_DATA) {
- dccp_rcv_established(sk, skb, dh, len);
+ __dccp_rcv_established(sk, skb, dh, len);
queued = 1; /* packet was queued
- (by dccp_rcv_established) */
+ (by __dccp_rcv_established) */
}
break;
}
@@ -444,7 +464,8 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
*/
if (sk->sk_state == DCCP_LISTEN) {
if (dh->dccph_type == DCCP_PKT_REQUEST) {
- if (dccp_v4_conn_request(sk, skb) < 0)
+ if (inet_csk(sk)->icsk_af_ops->conn_request(sk,
+ skb) < 0)
return 1;
/* FIXME: do congestion control initialization */
@@ -471,14 +492,14 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
if (dcb->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
dccp_event_ack_recv(sk, skb);
- ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
- ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
-
if (dp->dccps_options.dccpo_send_ack_vector &&
dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
DCCP_SKB_CB(skb)->dccpd_seq,
DCCP_ACKVEC_STATE_RECEIVED))
goto discard;
+
+ ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb);
+ ccid_hc_tx_packet_recv(dp->dccps_hc_tx_ccid, sk, skb);
}
/*
@@ -566,3 +587,5 @@ discard:
}
return 0;
}
+
+EXPORT_SYMBOL_GPL(dccp_rcv_state_process);
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index ca03521112c5..3f244670764a 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -19,7 +19,9 @@
#include <net/icmp.h>
#include <net/inet_hashtables.h>
+#include <net/inet_sock.h>
#include <net/sock.h>
+#include <net/timewait_sock.h>
#include <net/tcp_states.h>
#include <net/xfrm.h>
@@ -37,7 +39,8 @@ EXPORT_SYMBOL_GPL(dccp_hashinfo);
static int dccp_v4_get_port(struct sock *sk, const unsigned short snum)
{
- return inet_csk_get_port(&dccp_hashinfo, sk, snum);
+ return inet_csk_get_port(&dccp_hashinfo, sk, snum,
+ inet_csk_bind_conflict);
}
static void dccp_v4_hash(struct sock *sk)
@@ -45,171 +48,14 @@ static void dccp_v4_hash(struct sock *sk)
inet_hash(&dccp_hashinfo, sk);
}
-static void dccp_v4_unhash(struct sock *sk)
+void dccp_unhash(struct sock *sk)
{
inet_unhash(&dccp_hashinfo, sk);
}
-/* called with local bh disabled */
-static int __dccp_v4_check_established(struct sock *sk, const __u16 lport,
- struct inet_timewait_sock **twp)
-{
- struct inet_sock *inet = inet_sk(sk);
- const u32 daddr = inet->rcv_saddr;
- const u32 saddr = inet->daddr;
- const int dif = sk->sk_bound_dev_if;
- INET_ADDR_COOKIE(acookie, saddr, daddr)
- const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
- unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport);
- struct inet_ehash_bucket *head = inet_ehash_bucket(&dccp_hashinfo, hash);
- const struct sock *sk2;
- const struct hlist_node *node;
- struct inet_timewait_sock *tw;
-
- prefetch(head->chain.first);
- write_lock(&head->lock);
-
- /* Check TIME-WAIT sockets first. */
- sk_for_each(sk2, node, &(head + dccp_hashinfo.ehash_size)->chain) {
- tw = inet_twsk(sk2);
-
- if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
- goto not_unique;
- }
- tw = NULL;
-
- /* And established part... */
- sk_for_each(sk2, node, &head->chain) {
- if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
- goto not_unique;
- }
+EXPORT_SYMBOL_GPL(dccp_unhash);
- /* Must record num and sport now. Otherwise we will see
- * in hash table socket with a funny identity. */
- inet->num = lport;
- inet->sport = htons(lport);
- sk->sk_hash = hash;
- BUG_TRAP(sk_unhashed(sk));
- __sk_add_node(sk, &head->chain);
- sock_prot_inc_use(sk->sk_prot);
- write_unlock(&head->lock);
-
- if (twp != NULL) {
- *twp = tw;
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
- } else if (tw != NULL) {
- /* Silly. Should hash-dance instead... */
- inet_twsk_deschedule(tw, &dccp_death_row);
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
-
- inet_twsk_put(tw);
- }
-
- return 0;
-
-not_unique:
- write_unlock(&head->lock);
- return -EADDRNOTAVAIL;
-}
-
-/*
- * Bind a port for a connect operation and hash it.
- */
-static int dccp_v4_hash_connect(struct sock *sk)
-{
- const unsigned short snum = inet_sk(sk)->num;
- struct inet_bind_hashbucket *head;
- struct inet_bind_bucket *tb;
- int ret;
-
- if (snum == 0) {
- int low = sysctl_local_port_range[0];
- int high = sysctl_local_port_range[1];
- int remaining = (high - low) + 1;
- int rover = net_random() % (high - low) + low;
- struct hlist_node *node;
- struct inet_timewait_sock *tw = NULL;
-
- local_bh_disable();
- do {
- head = &dccp_hashinfo.bhash[inet_bhashfn(rover,
- dccp_hashinfo.bhash_size)];
- spin_lock(&head->lock);
-
- /* Does not bother with rcv_saddr checks,
- * because the established check is already
- * unique enough.
- */
- inet_bind_bucket_for_each(tb, node, &head->chain) {
- if (tb->port == rover) {
- BUG_TRAP(!hlist_empty(&tb->owners));
- if (tb->fastreuse >= 0)
- goto next_port;
- if (!__dccp_v4_check_established(sk,
- rover,
- &tw))
- goto ok;
- goto next_port;
- }
- }
-
- tb = inet_bind_bucket_create(dccp_hashinfo.bind_bucket_cachep,
- head, rover);
- if (tb == NULL) {
- spin_unlock(&head->lock);
- break;
- }
- tb->fastreuse = -1;
- goto ok;
-
- next_port:
- spin_unlock(&head->lock);
- if (++rover > high)
- rover = low;
- } while (--remaining > 0);
-
- local_bh_enable();
-
- return -EADDRNOTAVAIL;
-
-ok:
- /* All locks still held and bhs disabled */
- inet_bind_hash(sk, tb, rover);
- if (sk_unhashed(sk)) {
- inet_sk(sk)->sport = htons(rover);
- __inet_hash(&dccp_hashinfo, sk, 0);
- }
- spin_unlock(&head->lock);
-
- if (tw != NULL) {
- inet_twsk_deschedule(tw, &dccp_death_row);
- inet_twsk_put(tw);
- }
-
- ret = 0;
- goto out;
- }
-
- head = &dccp_hashinfo.bhash[inet_bhashfn(snum,
- dccp_hashinfo.bhash_size)];
- tb = inet_csk(sk)->icsk_bind_hash;
- spin_lock_bh(&head->lock);
- if (sk_head(&tb->owners) == sk && sk->sk_bind_node.next == NULL) {
- __inet_hash(&dccp_hashinfo, sk, 0);
- spin_unlock_bh(&head->lock);
- return 0;
- } else {
- spin_unlock(&head->lock);
- /* No definite answer... Walk to established hash table */
- ret = __dccp_v4_check_established(sk, snum, NULL);
-out:
- local_bh_enable();
- return ret;
- }
-}
-
-static int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
- int addr_len)
+int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct inet_sock *inet = inet_sk(sk);
struct dccp_sock *dp = dccp_sk(sk);
@@ -259,9 +105,9 @@ static int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
inet->dport = usin->sin_port;
inet->daddr = daddr;
- dp->dccps_ext_header_len = 0;
+ inet_csk(sk)->icsk_ext_hdr_len = 0;
if (inet->opt != NULL)
- dp->dccps_ext_header_len = inet->opt->optlen;
+ inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen;
/*
* Socket identity is still unknown (sport may be zero).
* However we set state to DCCP_REQUESTING and not releasing socket
@@ -269,7 +115,7 @@ static int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
* complete initialization after this.
*/
dccp_set_state(sk, DCCP_REQUESTING);
- err = dccp_v4_hash_connect(sk);
+ err = inet_hash_connect(&dccp_death_row, sk);
if (err != 0)
goto failure;
@@ -287,16 +133,6 @@ static int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
usin->sin_port);
dccp_update_gss(sk, dp->dccps_iss);
- /*
- * SWL and AWL are initially adjusted so that they are not less than
- * the initial Sequence Numbers received and sent, respectively:
- * SWL := max(GSR + 1 - floor(W/4), ISR),
- * AWL := max(GSS - W' + 1, ISS).
- * These adjustments MUST be applied only at the beginning of the
- * connection.
- */
- dccp_set_seqno(&dp->dccps_awl, max48(dp->dccps_awl, dp->dccps_iss));
-
inet->id = dp->dccps_iss ^ jiffies;
err = dccp_connect(sk);
@@ -316,6 +152,8 @@ failure:
goto out;
}
+EXPORT_SYMBOL_GPL(dccp_v4_connect);
+
/*
* This routine does path mtu discovery as defined in RFC1191.
*/
@@ -354,7 +192,7 @@ static inline void dccp_do_pmtu_discovery(struct sock *sk,
mtu = dst_mtu(dst);
if (inet->pmtudisc != IP_PMTUDISC_DONT &&
- dp->dccps_pmtu_cookie > mtu) {
+ inet_csk(sk)->icsk_pmtu_cookie > mtu) {
dccp_sync_mss(sk, mtu);
/*
@@ -606,6 +444,17 @@ out:
sock_put(sk);
}
+/* This routine computes an IPv4 DCCP checksum. */
+void dccp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb)
+{
+ const struct inet_sock *inet = inet_sk(sk);
+ struct dccp_hdr *dh = dccp_hdr(skb);
+
+ dh->dccph_checksum = dccp_v4_checksum(skb, inet->saddr, inet->daddr);
+}
+
+EXPORT_SYMBOL_GPL(dccp_v4_send_check);
+
int dccp_v4_send_reset(struct sock *sk, enum dccp_reset_codes code)
{
struct sk_buff *skb;
@@ -641,16 +490,6 @@ static inline u64 dccp_v4_init_sequence(const struct sock *sk,
dccp_hdr(skb)->dccph_sport);
}
-static inline int dccp_bad_service_code(const struct sock *sk,
- const __u32 service)
-{
- const struct dccp_sock *dp = dccp_sk(sk);
-
- if (dp->dccps_service == service)
- return 0;
- return !dccp_list_has_service(dp->dccps_service_list, service);
-}
-
int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
struct inet_request_sock *ireq;
@@ -662,7 +501,6 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
const __u32 service = dccp_hdr_request(skb)->dccph_req_service;
struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
__u8 reset_code = DCCP_RESET_CODE_TOO_BUSY;
- struct dst_entry *dst = NULL;
/* Never answer to DCCP_PKT_REQUESTs send to broadcast or multicast */
if (((struct rtable *)skb->dst)->rt_flags &
@@ -703,7 +541,6 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
ireq = inet_rsk(req);
ireq->loc_addr = daddr;
ireq->rmt_addr = saddr;
- /* FIXME: Merge Aristeu's option parsing code when ready */
req->rcv_wnd = 100; /* Fake, option parsing will get the
right value */
ireq->opt = NULL;
@@ -721,23 +558,22 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
dreq->dreq_iss = dccp_v4_init_sequence(sk, skb);
dreq->dreq_service = service;
- if (dccp_v4_send_response(sk, req, dst))
+ if (dccp_v4_send_response(sk, req, NULL))
goto drop_and_free;
inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
return 0;
drop_and_free:
- /*
- * FIXME: should be reqsk_free after implementing req->rsk_ops
- */
- __reqsk_free(req);
+ reqsk_free(req);
drop:
DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS);
dcb->dccpd_reset_code = reset_code;
return -1;
}
+EXPORT_SYMBOL_GPL(dccp_v4_conn_request);
+
/*
* The three way handshake has completed - we got a valid ACK or DATAACK -
* now create the new socket.
@@ -792,6 +628,8 @@ exit:
return NULL;
}
+EXPORT_SYMBOL_GPL(dccp_v4_request_recv_sock);
+
static struct sock *dccp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
{
const struct dccp_hdr *dh = dccp_hdr(skb);
@@ -1011,7 +849,9 @@ discard:
return 0;
}
-static inline int dccp_invalid_packet(struct sk_buff *skb)
+EXPORT_SYMBOL_GPL(dccp_v4_do_rcv);
+
+int dccp_invalid_packet(struct sk_buff *skb)
{
const struct dccp_hdr *dh;
@@ -1065,29 +905,30 @@ static inline int dccp_invalid_packet(struct sk_buff *skb)
return 1;
}
- /* If the header checksum is incorrect, drop packet and return */
- if (dccp_v4_verify_checksum(skb, skb->nh.iph->saddr,
- skb->nh.iph->daddr) < 0) {
- LIMIT_NETDEBUG(KERN_WARNING "DCCP: header checksum is "
- "incorrect\n");
- return 1;
- }
-
return 0;
}
+EXPORT_SYMBOL_GPL(dccp_invalid_packet);
+
/* this is called when real data arrives */
int dccp_v4_rcv(struct sk_buff *skb)
{
const struct dccp_hdr *dh;
struct sock *sk;
- int rc;
/* Step 1: Check header basics: */
if (dccp_invalid_packet(skb))
goto discard_it;
+ /* If the header checksum is incorrect, drop packet and return */
+ if (dccp_v4_verify_checksum(skb, skb->nh.iph->saddr,
+ skb->nh.iph->daddr) < 0) {
+ LIMIT_NETDEBUG(KERN_WARNING "%s: incorrect header checksum\n",
+ __FUNCTION__);
+ goto discard_it;
+ }
+
dh = dccp_hdr(skb);
DCCP_SKB_CB(skb)->dccpd_seq = dccp_hdr_seq(skb);
@@ -1143,28 +984,10 @@ int dccp_v4_rcv(struct sk_buff *skb)
goto do_time_wait;
}
- if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) {
- dccp_pr_debug("xfrm4_policy_check failed\n");
+ if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_and_relse;
- }
-
- if (sk_filter(sk, skb, 0)) {
- dccp_pr_debug("sk_filter failed\n");
- goto discard_and_relse;
- }
-
- skb->dev = NULL;
-
- bh_lock_sock(sk);
- rc = 0;
- if (!sock_owned_by_user(sk))
- rc = dccp_v4_do_rcv(sk, skb);
- else
- sk_add_backlog(sk, skb);
- bh_unlock_sock(sk);
- sock_put(sk);
- return rc;
+ return sk_receive_skb(sk, skb);
no_dccp_socket:
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
@@ -1194,9 +1017,23 @@ do_time_wait:
goto no_dccp_socket;
}
-static int dccp_v4_init_sock(struct sock *sk)
+struct inet_connection_sock_af_ops dccp_ipv4_af_ops = {
+ .queue_xmit = ip_queue_xmit,
+ .send_check = dccp_v4_send_check,
+ .rebuild_header = inet_sk_rebuild_header,
+ .conn_request = dccp_v4_conn_request,
+ .syn_recv_sock = dccp_v4_request_recv_sock,
+ .net_header_len = sizeof(struct iphdr),
+ .setsockopt = ip_setsockopt,
+ .getsockopt = ip_getsockopt,
+ .addr2sockaddr = inet_csk_addr2sockaddr,
+ .sockaddr_len = sizeof(struct sockaddr_in),
+};
+
+int dccp_v4_init_sock(struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
static int dccp_ctl_socket_init = 1;
dccp_options_init(&dp->dccps_options);
@@ -1236,9 +1073,11 @@ static int dccp_v4_init_sock(struct sock *sk)
dccp_ctl_socket_init = 0;
dccp_init_xmit_timers(sk);
- inet_csk(sk)->icsk_rto = DCCP_TIMEOUT_INIT;
+ icsk->icsk_rto = DCCP_TIMEOUT_INIT;
sk->sk_state = DCCP_CLOSED;
sk->sk_write_space = dccp_write_space;
+ icsk->icsk_af_ops = &dccp_ipv4_af_ops;
+ icsk->icsk_sync_mss = dccp_sync_mss;
dp->dccps_mss_cache = 536;
dp->dccps_role = DCCP_ROLE_UNDEFINED;
dp->dccps_service = DCCP_SERVICE_INVALID_VALUE;
@@ -1246,12 +1085,14 @@ static int dccp_v4_init_sock(struct sock *sk)
return 0;
}
-static int dccp_v4_destroy_sock(struct sock *sk)
+EXPORT_SYMBOL_GPL(dccp_v4_init_sock);
+
+int dccp_v4_destroy_sock(struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
/*
- * DCCP doesn't use sk_qrite_queue, just sk_send_head
+ * DCCP doesn't use sk_write_queue, just sk_send_head
* for retransmissions
*/
if (sk->sk_send_head != NULL) {
@@ -1279,6 +1120,8 @@ static int dccp_v4_destroy_sock(struct sock *sk)
return 0;
}
+EXPORT_SYMBOL_GPL(dccp_v4_destroy_sock);
+
static void dccp_v4_reqsk_destructor(struct request_sock *req)
{
kfree(inet_rsk(req)->opt);
@@ -1293,7 +1136,11 @@ static struct request_sock_ops dccp_request_sock_ops = {
.send_reset = dccp_v4_ctl_send_reset,
};
-struct proto dccp_v4_prot = {
+static struct timewait_sock_ops dccp_timewait_sock_ops = {
+ .twsk_obj_size = sizeof(struct inet_timewait_sock),
+};
+
+struct proto dccp_prot = {
.name = "DCCP",
.owner = THIS_MODULE,
.close = dccp_close,
@@ -1307,7 +1154,7 @@ struct proto dccp_v4_prot = {
.recvmsg = dccp_recvmsg,
.backlog_rcv = dccp_v4_do_rcv,
.hash = dccp_v4_hash,
- .unhash = dccp_v4_unhash,
+ .unhash = dccp_unhash,
.accept = inet_csk_accept,
.get_port = dccp_v4_get_port,
.shutdown = dccp_shutdown,
@@ -1316,5 +1163,7 @@ struct proto dccp_v4_prot = {
.max_header = MAX_DCCP_HEADER,
.obj_size = sizeof(struct dccp_sock),
.rsk_prot = &dccp_request_sock_ops,
- .twsk_obj_size = sizeof(struct inet_timewait_sock),
+ .twsk_prot = &dccp_timewait_sock_ops,
};
+
+EXPORT_SYMBOL_GPL(dccp_prot);
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
new file mode 100644
index 000000000000..c609dc78f487
--- /dev/null
+++ b/net/dccp/ipv6.c
@@ -0,0 +1,1261 @@
+/*
+ * DCCP over IPv6
+ * Linux INET6 implementation
+ *
+ * Based on net/dccp6/ipv6.c
+ *
+ * Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/xfrm.h>
+
+#include <net/addrconf.h>
+#include <net/inet_common.h>
+#include <net/inet_hashtables.h>
+#include <net/inet_sock.h>
+#include <net/inet6_connection_sock.h>
+#include <net/inet6_hashtables.h>
+#include <net/ip6_route.h>
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/xfrm.h>
+
+#include "dccp.h"
+#include "ipv6.h"
+
+static void dccp_v6_ctl_send_reset(struct sk_buff *skb);
+static void dccp_v6_reqsk_send_ack(struct sk_buff *skb,
+ struct request_sock *req);
+static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb);
+
+static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
+
+static struct inet_connection_sock_af_ops dccp_ipv6_mapped;
+static struct inet_connection_sock_af_ops dccp_ipv6_af_ops;
+
+static int dccp_v6_get_port(struct sock *sk, unsigned short snum)
+{
+ return inet_csk_get_port(&dccp_hashinfo, sk, snum,
+ inet6_csk_bind_conflict);
+}
+
+static void dccp_v6_hash(struct sock *sk)
+{
+ if (sk->sk_state != DCCP_CLOSED) {
+ if (inet_csk(sk)->icsk_af_ops == &dccp_ipv6_mapped) {
+ dccp_prot.hash(sk);
+ return;
+ }
+ local_bh_disable();
+ __inet6_hash(&dccp_hashinfo, sk);
+ local_bh_enable();
+ }
+}
+
+static inline u16 dccp_v6_check(struct dccp_hdr *dh, int len,
+ struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ unsigned long base)
+{
+ return csum_ipv6_magic(saddr, daddr, len, IPPROTO_DCCP, base);
+}
+
+static __u32 dccp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
+{
+ const struct dccp_hdr *dh = dccp_hdr(skb);
+
+ if (skb->protocol == htons(ETH_P_IPV6))
+ return secure_tcpv6_sequence_number(skb->nh.ipv6h->daddr.s6_addr32,
+ skb->nh.ipv6h->saddr.s6_addr32,
+ dh->dccph_dport,
+ dh->dccph_sport);
+ else
+ return secure_dccp_sequence_number(skb->nh.iph->daddr,
+ skb->nh.iph->saddr,
+ dh->dccph_dport,
+ dh->dccph_sport);
+}
+
+static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
+ int addr_len)
+{
+ struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
+ struct inet_connection_sock *icsk = inet_csk(sk);
+ struct inet_sock *inet = inet_sk(sk);
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct in6_addr *saddr = NULL, *final_p = NULL, final;
+ struct flowi fl;
+ struct dst_entry *dst;
+ int addr_type;
+ int err;
+
+ dp->dccps_role = DCCP_ROLE_CLIENT;
+
+ if (addr_len < SIN6_LEN_RFC2133)
+ return -EINVAL;
+
+ if (usin->sin6_family != AF_INET6)
+ return -EAFNOSUPPORT;
+
+ memset(&fl, 0, sizeof(fl));
+
+ if (np->sndflow) {
+ fl.fl6_flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK;
+ IP6_ECN_flow_init(fl.fl6_flowlabel);
+ if (fl.fl6_flowlabel & IPV6_FLOWLABEL_MASK) {
+ struct ip6_flowlabel *flowlabel;
+ flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
+ if (flowlabel == NULL)
+ return -EINVAL;
+ ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
+ fl6_sock_release(flowlabel);
+ }
+ }
+
+ /*
+ * connect() to INADDR_ANY means loopback (BSD'ism).
+ */
+
+ if (ipv6_addr_any(&usin->sin6_addr))
+ usin->sin6_addr.s6_addr[15] = 0x1;
+
+ addr_type = ipv6_addr_type(&usin->sin6_addr);
+
+ if(addr_type & IPV6_ADDR_MULTICAST)
+ return -ENETUNREACH;
+
+ if (addr_type & IPV6_ADDR_LINKLOCAL) {
+ if (addr_len >= sizeof(struct sockaddr_in6) &&
+ usin->sin6_scope_id) {
+ /* If interface is set while binding, indices
+ * must coincide.
+ */
+ if (sk->sk_bound_dev_if &&
+ sk->sk_bound_dev_if != usin->sin6_scope_id)
+ return -EINVAL;
+
+ sk->sk_bound_dev_if = usin->sin6_scope_id;
+ }
+
+ /* Connect to link-local address requires an interface */
+ if (!sk->sk_bound_dev_if)
+ return -EINVAL;
+ }
+
+ ipv6_addr_copy(&np->daddr, &usin->sin6_addr);
+ np->flow_label = fl.fl6_flowlabel;
+
+ /*
+ * DCCP over IPv4
+ */
+
+ if (addr_type == IPV6_ADDR_MAPPED) {
+ u32 exthdrlen = icsk->icsk_ext_hdr_len;
+ struct sockaddr_in sin;
+
+ SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
+
+ if (__ipv6_only_sock(sk))
+ return -ENETUNREACH;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = usin->sin6_port;
+ sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
+
+ icsk->icsk_af_ops = &dccp_ipv6_mapped;
+ sk->sk_backlog_rcv = dccp_v4_do_rcv;
+
+ err = dccp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
+
+ if (err) {
+ icsk->icsk_ext_hdr_len = exthdrlen;
+ icsk->icsk_af_ops = &dccp_ipv6_af_ops;
+ sk->sk_backlog_rcv = dccp_v6_do_rcv;
+ goto failure;
+ } else {
+ ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000FFFF),
+ inet->saddr);
+ ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000FFFF),
+ inet->rcv_saddr);
+ }
+
+ return err;
+ }
+
+ if (!ipv6_addr_any(&np->rcv_saddr))
+ saddr = &np->rcv_saddr;
+
+ fl.proto = IPPROTO_DCCP;
+ ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+ ipv6_addr_copy(&fl.fl6_src, saddr ? saddr : &np->saddr);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = usin->sin6_port;
+ fl.fl_ip_sport = inet->sport;
+
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+
+ err = ip6_dst_lookup(sk, &dst, &fl);
+ if (err)
+ goto failure;
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+
+ if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ goto failure;
+
+ if (saddr == NULL) {
+ saddr = &fl.fl6_src;
+ ipv6_addr_copy(&np->rcv_saddr, saddr);
+ }
+
+ /* set the source address */
+ ipv6_addr_copy(&np->saddr, saddr);
+ inet->rcv_saddr = LOOPBACK4_IPV6;
+
+ ip6_dst_store(sk, dst, NULL);
+
+ icsk->icsk_ext_hdr_len = 0;
+ if (np->opt)
+ icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
+ np->opt->opt_nflen);
+
+ inet->dport = usin->sin6_port;
+
+ dccp_set_state(sk, DCCP_REQUESTING);
+ err = inet6_hash_connect(&dccp_death_row, sk);
+ if (err)
+ goto late_failure;
+ /* FIXME */
+#if 0
+ dp->dccps_gar = secure_dccp_v6_sequence_number(np->saddr.s6_addr32,
+ np->daddr.s6_addr32,
+ inet->sport,
+ inet->dport);
+#endif
+ err = dccp_connect(sk);
+ if (err)
+ goto late_failure;
+
+ return 0;
+
+late_failure:
+ dccp_set_state(sk, DCCP_CLOSED);
+ __sk_dst_reset(sk);
+failure:
+ inet->dport = 0;
+ sk->sk_route_caps = 0;
+ return err;
+}
+
+static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ int type, int code, int offset, __u32 info)
+{
+ struct ipv6hdr *hdr = (struct ipv6hdr *)skb->data;
+ const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data + offset);
+ struct ipv6_pinfo *np;
+ struct sock *sk;
+ int err;
+ __u64 seq;
+
+ sk = inet6_lookup(&dccp_hashinfo, &hdr->daddr, dh->dccph_dport,
+ &hdr->saddr, dh->dccph_sport, skb->dev->ifindex);
+
+ if (sk == NULL) {
+ ICMP6_INC_STATS_BH(__in6_dev_get(skb->dev), ICMP6_MIB_INERRORS);
+ return;
+ }
+
+ if (sk->sk_state == DCCP_TIME_WAIT) {
+ inet_twsk_put((struct inet_timewait_sock *)sk);
+ return;
+ }
+
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk))
+ NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);
+
+ if (sk->sk_state == DCCP_CLOSED)
+ goto out;
+
+ np = inet6_sk(sk);
+
+ if (type == ICMPV6_PKT_TOOBIG) {
+ struct dst_entry *dst = NULL;
+
+ if (sock_owned_by_user(sk))
+ goto out;
+ if ((1 << sk->sk_state) & (DCCPF_LISTEN | DCCPF_CLOSED))
+ goto out;
+
+ /* icmp should have updated the destination cache entry */
+ dst = __sk_dst_check(sk, np->dst_cookie);
+
+ if (dst == NULL) {
+ struct inet_sock *inet = inet_sk(sk);
+ struct flowi fl;
+
+ /* BUGGG_FUTURE: Again, it is not clear how
+ to handle rthdr case. Ignore this complexity
+ for now.
+ */
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = IPPROTO_DCCP;
+ ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+ ipv6_addr_copy(&fl.fl6_src, &np->saddr);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = inet->dport;
+ fl.fl_ip_sport = inet->sport;
+
+ if ((err = ip6_dst_lookup(sk, &dst, &fl))) {
+ sk->sk_err_soft = -err;
+ goto out;
+ }
+
+ if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
+ sk->sk_err_soft = -err;
+ goto out;
+ }
+
+ } else
+ dst_hold(dst);
+
+ if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) {
+ dccp_sync_mss(sk, dst_mtu(dst));
+ } /* else let the usual retransmit timer handle it */
+ dst_release(dst);
+ goto out;
+ }
+
+ icmpv6_err_convert(type, code, &err);
+
+ seq = DCCP_SKB_CB(skb)->dccpd_seq;
+ /* Might be for an request_sock */
+ switch (sk->sk_state) {
+ struct request_sock *req, **prev;
+ case DCCP_LISTEN:
+ if (sock_owned_by_user(sk))
+ goto out;
+
+ req = inet6_csk_search_req(sk, &prev, dh->dccph_dport,
+ &hdr->daddr, &hdr->saddr,
+ inet6_iif(skb));
+ if (!req)
+ goto out;
+
+ /* ICMPs are not backlogged, hence we cannot get
+ * an established socket here.
+ */
+ BUG_TRAP(req->sk == NULL);
+
+ if (seq != dccp_rsk(req)->dreq_iss) {
+ NET_INC_STATS_BH(LINUX_MIB_OUTOFWINDOWICMPS);
+ goto out;
+ }
+
+ inet_csk_reqsk_queue_drop(sk, req, prev);
+ goto out;
+
+ case DCCP_REQUESTING:
+ case DCCP_RESPOND: /* Cannot happen.
+ It can, it SYNs are crossed. --ANK */
+ if (!sock_owned_by_user(sk)) {
+ DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS);
+ sk->sk_err = err;
+ /*
+ * Wake people up to see the error
+ * (see connect in sock.c)
+ */
+ sk->sk_error_report(sk);
+
+ dccp_done(sk);
+ } else
+ sk->sk_err_soft = err;
+ goto out;
+ }
+
+ if (!sock_owned_by_user(sk) && np->recverr) {
+ sk->sk_err = err;
+ sk->sk_error_report(sk);
+ } else
+ sk->sk_err_soft = err;
+
+out:
+ bh_unlock_sock(sk);
+ sock_put(sk);
+}
+
+
+static int dccp_v6_send_response(struct sock *sk, struct request_sock *req,
+ struct dst_entry *dst)
+{
+ struct inet6_request_sock *ireq6 = inet6_rsk(req);
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct sk_buff *skb;
+ struct ipv6_txoptions *opt = NULL;
+ struct in6_addr *final_p = NULL, final;
+ struct flowi fl;
+ int err = -1;
+
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = IPPROTO_DCCP;
+ ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
+ ipv6_addr_copy(&fl.fl6_src, &ireq6->loc_addr);
+ fl.fl6_flowlabel = 0;
+ fl.oif = ireq6->iif;
+ fl.fl_ip_dport = inet_rsk(req)->rmt_port;
+ fl.fl_ip_sport = inet_sk(sk)->sport;
+
+ if (dst == NULL) {
+ opt = np->opt;
+ if (opt == NULL &&
+ np->rxopt.bits.osrcrt == 2 &&
+ ireq6->pktopts) {
+ struct sk_buff *pktopts = ireq6->pktopts;
+ struct inet6_skb_parm *rxopt = IP6CB(pktopts);
+ if (rxopt->srcrt)
+ opt = ipv6_invert_rthdr(sk,
+ (struct ipv6_rt_hdr *)(pktopts->nh.raw +
+ rxopt->srcrt));
+ }
+
+ if (opt && opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *)opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+
+ err = ip6_dst_lookup(sk, &dst, &fl);
+ if (err)
+ goto done;
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+ if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ goto done;
+ }
+
+ skb = dccp_make_response(sk, dst, req);
+ if (skb != NULL) {
+ struct dccp_hdr *dh = dccp_hdr(skb);
+ dh->dccph_checksum = dccp_v6_check(dh, skb->len,
+ &ireq6->loc_addr,
+ &ireq6->rmt_addr,
+ csum_partial((char *)dh,
+ skb->len,
+ skb->csum));
+ ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
+ err = ip6_xmit(sk, skb, &fl, opt, 0);
+ if (err == NET_XMIT_CN)
+ err = 0;
+ }
+
+done:
+ if (opt && opt != np->opt)
+ sock_kfree_s(sk, opt, opt->tot_len);
+ return err;
+}
+
+static void dccp_v6_reqsk_destructor(struct request_sock *req)
+{
+ if (inet6_rsk(req)->pktopts != NULL)
+ kfree_skb(inet6_rsk(req)->pktopts);
+}
+
+static struct request_sock_ops dccp6_request_sock_ops = {
+ .family = AF_INET6,
+ .obj_size = sizeof(struct dccp6_request_sock),
+ .rtx_syn_ack = dccp_v6_send_response,
+ .send_ack = dccp_v6_reqsk_send_ack,
+ .destructor = dccp_v6_reqsk_destructor,
+ .send_reset = dccp_v6_ctl_send_reset,
+};
+
+static struct timewait_sock_ops dccp6_timewait_sock_ops = {
+ .twsk_obj_size = sizeof(struct dccp6_timewait_sock),
+};
+
+static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
+{
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct dccp_hdr *dh = dccp_hdr(skb);
+
+ dh->dccph_checksum = csum_ipv6_magic(&np->saddr, &np->daddr,
+ len, IPPROTO_DCCP,
+ csum_partial((char *)dh,
+ dh->dccph_doff << 2,
+ skb->csum));
+}
+
+static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb)
+{
+ struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
+ const int dccp_hdr_reset_len = sizeof(struct dccp_hdr) +
+ sizeof(struct dccp_hdr_ext) +
+ sizeof(struct dccp_hdr_reset);
+ struct sk_buff *skb;
+ struct flowi fl;
+ u64 seqno;
+
+ if (rxdh->dccph_type == DCCP_PKT_RESET)
+ return;
+
+ if (!ipv6_unicast_destination(rxskb))
+ return;
+
+ /*
+ * We need to grab some memory, and put together an RST,
+ * and then put it into the queue to be sent.
+ */
+
+ skb = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) +
+ dccp_hdr_reset_len, GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr) +
+ dccp_hdr_reset_len);
+
+ skb->h.raw = skb_push(skb, dccp_hdr_reset_len);
+ dh = dccp_hdr(skb);
+ memset(dh, 0, dccp_hdr_reset_len);
+
+ /* Swap the send and the receive. */
+ dh->dccph_type = DCCP_PKT_RESET;
+ dh->dccph_sport = rxdh->dccph_dport;
+ dh->dccph_dport = rxdh->dccph_sport;
+ dh->dccph_doff = dccp_hdr_reset_len / 4;
+ dh->dccph_x = 1;
+ dccp_hdr_reset(skb)->dccph_reset_code =
+ DCCP_SKB_CB(rxskb)->dccpd_reset_code;
+
+ /* See "8.3.1. Abnormal Termination" in draft-ietf-dccp-spec-11 */
+ seqno = 0;
+ if (DCCP_SKB_CB(rxskb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
+ dccp_set_seqno(&seqno, DCCP_SKB_CB(rxskb)->dccpd_ack_seq + 1);
+
+ dccp_hdr_set_seq(dh, seqno);
+ dccp_hdr_set_ack(dccp_hdr_ack_bits(skb),
+ DCCP_SKB_CB(rxskb)->dccpd_seq);
+
+ memset(&fl, 0, sizeof(fl));
+ ipv6_addr_copy(&fl.fl6_dst, &rxskb->nh.ipv6h->saddr);
+ ipv6_addr_copy(&fl.fl6_src, &rxskb->nh.ipv6h->daddr);
+ dh->dccph_checksum = csum_ipv6_magic(&fl.fl6_src, &fl.fl6_dst,
+ sizeof(*dh), IPPROTO_DCCP,
+ skb->csum);
+ fl.proto = IPPROTO_DCCP;
+ fl.oif = inet6_iif(rxskb);
+ fl.fl_ip_dport = dh->dccph_dport;
+ fl.fl_ip_sport = dh->dccph_sport;
+
+ /* sk = NULL, but it is safe for now. RST socket required. */
+ if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
+ if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) {
+ ip6_xmit(NULL, skb, &fl, NULL, 0);
+ DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
+ DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS);
+ return;
+ }
+ }
+
+ kfree_skb(skb);
+}
+
+static void dccp_v6_ctl_send_ack(struct sk_buff *rxskb)
+{
+ struct flowi fl;
+ struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
+ const int dccp_hdr_ack_len = sizeof(struct dccp_hdr) +
+ sizeof(struct dccp_hdr_ext) +
+ sizeof(struct dccp_hdr_ack_bits);
+ struct sk_buff *skb;
+
+ skb = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) +
+ dccp_hdr_ack_len, GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr) +
+ dccp_hdr_ack_len);
+
+ skb->h.raw = skb_push(skb, dccp_hdr_ack_len);
+ dh = dccp_hdr(skb);
+ memset(dh, 0, dccp_hdr_ack_len);
+
+ /* Build DCCP header and checksum it. */
+ dh->dccph_type = DCCP_PKT_ACK;
+ dh->dccph_sport = rxdh->dccph_dport;
+ dh->dccph_dport = rxdh->dccph_sport;
+ dh->dccph_doff = dccp_hdr_ack_len / 4;
+ dh->dccph_x = 1;
+
+ dccp_hdr_set_seq(dh, DCCP_SKB_CB(rxskb)->dccpd_ack_seq);
+ dccp_hdr_set_ack(dccp_hdr_ack_bits(skb),
+ DCCP_SKB_CB(rxskb)->dccpd_seq);
+
+ memset(&fl, 0, sizeof(fl));
+ ipv6_addr_copy(&fl.fl6_dst, &rxskb->nh.ipv6h->saddr);
+ ipv6_addr_copy(&fl.fl6_src, &rxskb->nh.ipv6h->daddr);
+
+ /* FIXME: calculate checksum, IPv4 also should... */
+
+ fl.proto = IPPROTO_DCCP;
+ fl.oif = inet6_iif(rxskb);
+ fl.fl_ip_dport = dh->dccph_dport;
+ fl.fl_ip_sport = dh->dccph_sport;
+
+ if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
+ if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) {
+ ip6_xmit(NULL, skb, &fl, NULL, 0);
+ DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
+ return;
+ }
+ }
+
+ kfree_skb(skb);
+}
+
+static void dccp_v6_reqsk_send_ack(struct sk_buff *skb,
+ struct request_sock *req)
+{
+ dccp_v6_ctl_send_ack(skb);
+}
+
+static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
+{
+ const struct dccp_hdr *dh = dccp_hdr(skb);
+ const struct ipv6hdr *iph = skb->nh.ipv6h;
+ struct sock *nsk;
+ struct request_sock **prev;
+ /* Find possible connection requests. */
+ struct request_sock *req = inet6_csk_search_req(sk, &prev,
+ dh->dccph_sport,
+ &iph->saddr,
+ &iph->daddr,
+ inet6_iif(skb));
+ if (req != NULL)
+ return dccp_check_req(sk, skb, req, prev);
+
+ nsk = __inet6_lookup_established(&dccp_hashinfo,
+ &iph->saddr, dh->dccph_sport,
+ &iph->daddr, ntohs(dh->dccph_dport),
+ inet6_iif(skb));
+
+ if (nsk != NULL) {
+ if (nsk->sk_state != DCCP_TIME_WAIT) {
+ bh_lock_sock(nsk);
+ return nsk;
+ }
+ inet_twsk_put((struct inet_timewait_sock *)nsk);
+ return NULL;
+ }
+
+ return sk;
+}
+
+static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
+{
+ struct inet_request_sock *ireq;
+ struct dccp_sock dp;
+ struct request_sock *req;
+ struct dccp_request_sock *dreq;
+ struct inet6_request_sock *ireq6;
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ const __u32 service = dccp_hdr_request(skb)->dccph_req_service;
+ struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
+ __u8 reset_code = DCCP_RESET_CODE_TOO_BUSY;
+
+ if (skb->protocol == htons(ETH_P_IP))
+ return dccp_v4_conn_request(sk, skb);
+
+ if (!ipv6_unicast_destination(skb))
+ goto drop;
+
+ if (dccp_bad_service_code(sk, service)) {
+ reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE;
+ goto drop;
+ }
+ /*
+ * There are no SYN attacks on IPv6, yet...
+ */
+ if (inet_csk_reqsk_queue_is_full(sk))
+ goto drop;
+
+ if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
+ goto drop;
+
+ req = inet6_reqsk_alloc(sk->sk_prot->rsk_prot);
+ if (req == NULL)
+ goto drop;
+
+ /* FIXME: process options */
+
+ dccp_openreq_init(req, &dp, skb);
+
+ ireq6 = inet6_rsk(req);
+ ireq = inet_rsk(req);
+ ipv6_addr_copy(&ireq6->rmt_addr, &skb->nh.ipv6h->saddr);
+ ipv6_addr_copy(&ireq6->loc_addr, &skb->nh.ipv6h->daddr);
+ req->rcv_wnd = 100; /* Fake, option parsing will get the
+ right value */
+ ireq6->pktopts = NULL;
+
+ if (ipv6_opt_accepted(sk, skb) ||
+ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
+ atomic_inc(&skb->users);
+ ireq6->pktopts = skb;
+ }
+ ireq6->iif = sk->sk_bound_dev_if;
+
+ /* So that link locals have meaning */
+ if (!sk->sk_bound_dev_if &&
+ ipv6_addr_type(&ireq6->rmt_addr) & IPV6_ADDR_LINKLOCAL)
+ ireq6->iif = inet6_iif(skb);
+
+ /*
+ * Step 3: Process LISTEN state
+ *
+ * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie
+ *
+ * In fact we defer setting S.GSR, S.SWL, S.SWH to
+ * dccp_create_openreq_child.
+ */
+ dreq = dccp_rsk(req);
+ dreq->dreq_isr = dcb->dccpd_seq;
+ dreq->dreq_iss = dccp_v6_init_sequence(sk, skb);
+ dreq->dreq_service = service;
+
+ if (dccp_v6_send_response(sk, req, NULL))
+ goto drop_and_free;
+
+ inet6_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
+ return 0;
+
+drop_and_free:
+ reqsk_free(req);
+drop:
+ DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS);
+ dcb->dccpd_reset_code = reset_code;
+ return -1;
+}
+
+static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
+ struct sk_buff *skb,
+ struct request_sock *req,
+ struct dst_entry *dst)
+{
+ struct inet6_request_sock *ireq6 = inet6_rsk(req);
+ struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
+ struct inet_sock *newinet;
+ struct dccp_sock *newdp;
+ struct dccp6_sock *newdp6;
+ struct sock *newsk;
+ struct ipv6_txoptions *opt;
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ /*
+ * v6 mapped
+ */
+
+ newsk = dccp_v4_request_recv_sock(sk, skb, req, dst);
+ if (newsk == NULL)
+ return NULL;
+
+ newdp6 = (struct dccp6_sock *)newsk;
+ newdp = dccp_sk(newsk);
+ newinet = inet_sk(newsk);
+ newinet->pinet6 = &newdp6->inet6;
+ newnp = inet6_sk(newsk);
+
+ memcpy(newnp, np, sizeof(struct ipv6_pinfo));
+
+ ipv6_addr_set(&newnp->daddr, 0, 0, htonl(0x0000FFFF),
+ newinet->daddr);
+
+ ipv6_addr_set(&newnp->saddr, 0, 0, htonl(0x0000FFFF),
+ newinet->saddr);
+
+ ipv6_addr_copy(&newnp->rcv_saddr, &newnp->saddr);
+
+ inet_csk(newsk)->icsk_af_ops = &dccp_ipv6_mapped;
+ newsk->sk_backlog_rcv = dccp_v4_do_rcv;
+ newnp->pktoptions = NULL;
+ newnp->opt = NULL;
+ newnp->mcast_oif = inet6_iif(skb);
+ newnp->mcast_hops = skb->nh.ipv6h->hop_limit;
+
+ /*
+ * No need to charge this sock to the relevant IPv6 refcnt debug socks count
+ * here, dccp_create_openreq_child now does this for us, see the comment in
+ * that function for the gory details. -acme
+ */
+
+ /* It is tricky place. Until this moment IPv4 tcp
+ worked with IPv6 icsk.icsk_af_ops.
+ Sync it now.
+ */
+ dccp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
+
+ return newsk;
+ }
+
+ opt = np->opt;
+
+ if (sk_acceptq_is_full(sk))
+ goto out_overflow;
+
+ if (np->rxopt.bits.osrcrt == 2 &&
+ opt == NULL && ireq6->pktopts) {
+ struct inet6_skb_parm *rxopt = IP6CB(ireq6->pktopts);
+ if (rxopt->srcrt)
+ opt = ipv6_invert_rthdr(sk,
+ (struct ipv6_rt_hdr *)(ireq6->pktopts->nh.raw +
+ rxopt->srcrt));
+ }
+
+ if (dst == NULL) {
+ struct in6_addr *final_p = NULL, final;
+ struct flowi fl;
+
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = IPPROTO_DCCP;
+ ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
+ if (opt && opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+ ipv6_addr_copy(&fl.fl6_src, &ireq6->loc_addr);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = inet_rsk(req)->rmt_port;
+ fl.fl_ip_sport = inet_sk(sk)->sport;
+
+ if (ip6_dst_lookup(sk, &dst, &fl))
+ goto out;
+
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+
+ if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ goto out;
+ }
+
+ newsk = dccp_create_openreq_child(sk, req, skb);
+ if (newsk == NULL)
+ goto out;
+
+ /*
+ * No need to charge this sock to the relevant IPv6 refcnt debug socks
+ * count here, dccp_create_openreq_child now does this for us, see the
+ * comment in that function for the gory details. -acme
+ */
+
+ ip6_dst_store(newsk, dst, NULL);
+ newsk->sk_route_caps = dst->dev->features &
+ ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
+
+ newdp6 = (struct dccp6_sock *)newsk;
+ newinet = inet_sk(newsk);
+ newinet->pinet6 = &newdp6->inet6;
+ newdp = dccp_sk(newsk);
+ newnp = inet6_sk(newsk);
+
+ memcpy(newnp, np, sizeof(struct ipv6_pinfo));
+
+ ipv6_addr_copy(&newnp->daddr, &ireq6->rmt_addr);
+ ipv6_addr_copy(&newnp->saddr, &ireq6->loc_addr);
+ ipv6_addr_copy(&newnp->rcv_saddr, &ireq6->loc_addr);
+ newsk->sk_bound_dev_if = ireq6->iif;
+
+ /* Now IPv6 options...
+
+ First: no IPv4 options.
+ */
+ newinet->opt = NULL;
+
+ /* Clone RX bits */
+ newnp->rxopt.all = np->rxopt.all;
+
+ /* Clone pktoptions received with SYN */
+ newnp->pktoptions = NULL;
+ if (ireq6->pktopts != NULL) {
+ newnp->pktoptions = skb_clone(ireq6->pktopts, GFP_ATOMIC);
+ kfree_skb(ireq6->pktopts);
+ ireq6->pktopts = NULL;
+ if (newnp->pktoptions)
+ skb_set_owner_r(newnp->pktoptions, newsk);
+ }
+ newnp->opt = NULL;
+ newnp->mcast_oif = inet6_iif(skb);
+ newnp->mcast_hops = skb->nh.ipv6h->hop_limit;
+
+ /* Clone native IPv6 options from listening socket (if any)
+
+ Yes, keeping reference count would be much more clever,
+ but we make one more one thing there: reattach optmem
+ to newsk.
+ */
+ if (opt) {
+ newnp->opt = ipv6_dup_options(newsk, opt);
+ if (opt != np->opt)
+ sock_kfree_s(sk, opt, opt->tot_len);
+ }
+
+ inet_csk(newsk)->icsk_ext_hdr_len = 0;
+ if (newnp->opt)
+ inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
+ newnp->opt->opt_flen);
+
+ dccp_sync_mss(newsk, dst_mtu(dst));
+
+ newinet->daddr = newinet->saddr = newinet->rcv_saddr = LOOPBACK4_IPV6;
+
+ __inet6_hash(&dccp_hashinfo, newsk);
+ inet_inherit_port(&dccp_hashinfo, sk, newsk);
+
+ return newsk;
+
+out_overflow:
+ NET_INC_STATS_BH(LINUX_MIB_LISTENOVERFLOWS);
+out:
+ NET_INC_STATS_BH(LINUX_MIB_LISTENDROPS);
+ if (opt && opt != np->opt)
+ sock_kfree_s(sk, opt, opt->tot_len);
+ dst_release(dst);
+ return NULL;
+}
+
+/* The socket must have it's spinlock held when we get
+ * here.
+ *
+ * We have a potential double-lock case here, so even when
+ * doing backlog processing we use the BH locking scheme.
+ * This is because we cannot sleep with the original spinlock
+ * held.
+ */
+static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct sk_buff *opt_skb = NULL;
+
+ /* Imagine: socket is IPv6. IPv4 packet arrives,
+ goes to IPv4 receive handler and backlogged.
+ From backlog it always goes here. Kerboom...
+ Fortunately, dccp_rcv_established and rcv_established
+ handle them correctly, but it is not case with
+ dccp_v6_hnd_req and dccp_v6_ctl_send_reset(). --ANK
+ */
+
+ if (skb->protocol == htons(ETH_P_IP))
+ return dccp_v4_do_rcv(sk, skb);
+
+ if (sk_filter(sk, skb, 0))
+ goto discard;
+
+ /*
+ * socket locking is here for SMP purposes as backlog rcv
+ * is currently called with bh processing disabled.
+ */
+
+ /* Do Stevens' IPV6_PKTOPTIONS.
+
+ Yes, guys, it is the only place in our code, where we
+ may make it not affecting IPv4.
+ The rest of code is protocol independent,
+ and I do not like idea to uglify IPv4.
+
+ Actually, all the idea behind IPV6_PKTOPTIONS
+ looks not very well thought. For now we latch
+ options, received in the last packet, enqueued
+ by tcp. Feel free to propose better solution.
+ --ANK (980728)
+ */
+ if (np->rxopt.all)
+ opt_skb = skb_clone(skb, GFP_ATOMIC);
+
+ if (sk->sk_state == DCCP_OPEN) { /* Fast path */
+ if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len))
+ goto reset;
+ return 0;
+ }
+
+ if (sk->sk_state == DCCP_LISTEN) {
+ struct sock *nsk = dccp_v6_hnd_req(sk, skb);
+ if (!nsk)
+ goto discard;
+
+ /*
+ * Queue it on the new socket if the new socket is active,
+ * otherwise we just shortcircuit this and continue with
+ * the new socket..
+ */
+ if(nsk != sk) {
+ if (dccp_child_process(sk, nsk, skb))
+ goto reset;
+ if (opt_skb)
+ __kfree_skb(opt_skb);
+ return 0;
+ }
+ }
+
+ if (dccp_rcv_state_process(sk, skb, dccp_hdr(skb), skb->len))
+ goto reset;
+ return 0;
+
+reset:
+ dccp_v6_ctl_send_reset(skb);
+discard:
+ if (opt_skb)
+ __kfree_skb(opt_skb);
+ kfree_skb(skb);
+ return 0;
+}
+
+static int dccp_v6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
+{
+ const struct dccp_hdr *dh;
+ struct sk_buff *skb = *pskb;
+ struct sock *sk;
+
+ /* Step 1: Check header basics: */
+
+ if (dccp_invalid_packet(skb))
+ goto discard_it;
+
+ dh = dccp_hdr(skb);
+
+ DCCP_SKB_CB(skb)->dccpd_seq = dccp_hdr_seq(skb);
+ DCCP_SKB_CB(skb)->dccpd_type = dh->dccph_type;
+
+ if (dccp_packet_without_ack(skb))
+ DCCP_SKB_CB(skb)->dccpd_ack_seq = DCCP_PKT_WITHOUT_ACK_SEQ;
+ else
+ DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb);
+
+ /* Step 2:
+ * Look up flow ID in table and get corresponding socket */
+ sk = __inet6_lookup(&dccp_hashinfo, &skb->nh.ipv6h->saddr,
+ dh->dccph_sport,
+ &skb->nh.ipv6h->daddr, ntohs(dh->dccph_dport),
+ inet6_iif(skb));
+ /*
+ * Step 2:
+ * If no socket ...
+ * Generate Reset(No Connection) unless P.type == Reset
+ * Drop packet and return
+ */
+ if (sk == NULL)
+ goto no_dccp_socket;
+
+ /*
+ * Step 2:
+ * ... or S.state == TIMEWAIT,
+ * Generate Reset(No Connection) unless P.type == Reset
+ * Drop packet and return
+ */
+
+ if (sk->sk_state == DCCP_TIME_WAIT)
+ goto do_time_wait;
+
+ if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
+ goto discard_and_relse;
+
+ return sk_receive_skb(sk, skb) ? -1 : 0;
+
+no_dccp_socket:
+ if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
+ goto discard_it;
+ /*
+ * Step 2:
+ * Generate Reset(No Connection) unless P.type == Reset
+ * Drop packet and return
+ */
+ if (dh->dccph_type != DCCP_PKT_RESET) {
+ DCCP_SKB_CB(skb)->dccpd_reset_code =
+ DCCP_RESET_CODE_NO_CONNECTION;
+ dccp_v6_ctl_send_reset(skb);
+ }
+discard_it:
+
+ /*
+ * Discard frame
+ */
+
+ kfree_skb(skb);
+ return 0;
+
+discard_and_relse:
+ sock_put(sk);
+ goto discard_it;
+
+do_time_wait:
+ inet_twsk_put((struct inet_timewait_sock *)sk);
+ goto no_dccp_socket;
+}
+
+static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
+ .queue_xmit = inet6_csk_xmit,
+ .send_check = dccp_v6_send_check,
+ .rebuild_header = inet6_sk_rebuild_header,
+ .conn_request = dccp_v6_conn_request,
+ .syn_recv_sock = dccp_v6_request_recv_sock,
+ .net_header_len = sizeof(struct ipv6hdr),
+ .setsockopt = ipv6_setsockopt,
+ .getsockopt = ipv6_getsockopt,
+ .addr2sockaddr = inet6_csk_addr2sockaddr,
+ .sockaddr_len = sizeof(struct sockaddr_in6)
+};
+
+/*
+ * DCCP over IPv4 via INET6 API
+ */
+static struct inet_connection_sock_af_ops dccp_ipv6_mapped = {
+ .queue_xmit = ip_queue_xmit,
+ .send_check = dccp_v4_send_check,
+ .rebuild_header = inet_sk_rebuild_header,
+ .conn_request = dccp_v6_conn_request,
+ .syn_recv_sock = dccp_v6_request_recv_sock,
+ .net_header_len = sizeof(struct iphdr),
+ .setsockopt = ipv6_setsockopt,
+ .getsockopt = ipv6_getsockopt,
+ .addr2sockaddr = inet6_csk_addr2sockaddr,
+ .sockaddr_len = sizeof(struct sockaddr_in6)
+};
+
+/* NOTE: A lot of things set to zero explicitly by call to
+ * sk_alloc() so need not be done here.
+ */
+static int dccp_v6_init_sock(struct sock *sk)
+{
+ int err = dccp_v4_init_sock(sk);
+
+ if (err == 0)
+ inet_csk(sk)->icsk_af_ops = &dccp_ipv6_af_ops;
+
+ return err;
+}
+
+static int dccp_v6_destroy_sock(struct sock *sk)
+{
+ dccp_v4_destroy_sock(sk);
+ return inet6_destroy_sock(sk);
+}
+
+static struct proto dccp_v6_prot = {
+ .name = "DCCPv6",
+ .owner = THIS_MODULE,
+ .close = dccp_close,
+ .connect = dccp_v6_connect,
+ .disconnect = dccp_disconnect,
+ .ioctl = dccp_ioctl,
+ .init = dccp_v6_init_sock,
+ .setsockopt = dccp_setsockopt,
+ .getsockopt = dccp_getsockopt,
+ .sendmsg = dccp_sendmsg,
+ .recvmsg = dccp_recvmsg,
+ .backlog_rcv = dccp_v6_do_rcv,
+ .hash = dccp_v6_hash,
+ .unhash = dccp_unhash,
+ .accept = inet_csk_accept,
+ .get_port = dccp_v6_get_port,
+ .shutdown = dccp_shutdown,
+ .destroy = dccp_v6_destroy_sock,
+ .orphan_count = &dccp_orphan_count,
+ .max_header = MAX_DCCP_HEADER,
+ .obj_size = sizeof(struct dccp6_sock),
+ .rsk_prot = &dccp6_request_sock_ops,
+ .twsk_prot = &dccp6_timewait_sock_ops,
+};
+
+static struct inet6_protocol dccp_v6_protocol = {
+ .handler = dccp_v6_rcv,
+ .err_handler = dccp_v6_err,
+ .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL,
+};
+
+static struct proto_ops inet6_dccp_ops = {
+ .family = PF_INET6,
+ .owner = THIS_MODULE,
+ .release = inet6_release,
+ .bind = inet6_bind,
+ .connect = inet_stream_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = inet_accept,
+ .getname = inet6_getname,
+ .poll = dccp_poll,
+ .ioctl = inet6_ioctl,
+ .listen = inet_dccp_listen,
+ .shutdown = inet_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = inet_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static struct inet_protosw dccp_v6_protosw = {
+ .type = SOCK_DCCP,
+ .protocol = IPPROTO_DCCP,
+ .prot = &dccp_v6_prot,
+ .ops = &inet6_dccp_ops,
+ .capability = -1,
+ .flags = INET_PROTOSW_ICSK,
+};
+
+static int __init dccp_v6_init(void)
+{
+ int err = proto_register(&dccp_v6_prot, 1);
+
+ if (err != 0)
+ goto out;
+
+ err = inet6_add_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
+ if (err != 0)
+ goto out_unregister_proto;
+
+ inet6_register_protosw(&dccp_v6_protosw);
+out:
+ return err;
+out_unregister_proto:
+ proto_unregister(&dccp_v6_prot);
+ goto out;
+}
+
+static void __exit dccp_v6_exit(void)
+{
+ inet6_del_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
+ inet6_unregister_protosw(&dccp_v6_protosw);
+ proto_unregister(&dccp_v6_prot);
+}
+
+module_init(dccp_v6_init);
+module_exit(dccp_v6_exit);
+
+/*
+ * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33)
+ * values directly, Also cover the case where the protocol is not specified,
+ * i.e. net-pf-PF_INET6-proto-0-type-SOCK_DCCP
+ */
+MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-33-type-6");
+MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-0-type-6");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>");
+MODULE_DESCRIPTION("DCCPv6 - Datagram Congestion Controlled Protocol");
diff --git a/net/dccp/ipv6.h b/net/dccp/ipv6.h
new file mode 100644
index 000000000000..e4d4e9309270
--- /dev/null
+++ b/net/dccp/ipv6.h
@@ -0,0 +1,37 @@
+#ifndef _DCCP_IPV6_H
+#define _DCCP_IPV6_H
+/*
+ * net/dccp/ipv6.h
+ *
+ * An implementation of the DCCP protocol
+ * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/dccp.h>
+#include <linux/ipv6.h>
+
+struct dccp6_sock {
+ struct dccp_sock dccp;
+ /*
+ * ipv6_pinfo has to be the last member of dccp6_sock,
+ * see inet6_sk_generic.
+ */
+ struct ipv6_pinfo inet6;
+};
+
+struct dccp6_request_sock {
+ struct dccp_request_sock dccp;
+ struct inet6_request_sock inet6;
+};
+
+struct dccp6_timewait_sock {
+ struct inet_timewait_sock inet;
+ struct inet6_timewait_sock tw6;
+};
+
+#endif /* _DCCP_IPV6_H */
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index 1393461898bb..29261fc198e7 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -40,6 +40,8 @@ struct inet_timewait_death_row dccp_death_row = {
(unsigned long)&dccp_death_row),
};
+EXPORT_SYMBOL_GPL(dccp_death_row);
+
void dccp_time_wait(struct sock *sk, int state, int timeo)
{
struct inet_timewait_sock *tw = NULL;
@@ -50,7 +52,18 @@ void dccp_time_wait(struct sock *sk, int state, int timeo)
if (tw != NULL) {
const struct inet_connection_sock *icsk = inet_csk(sk);
const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1);
-
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ if (tw->tw_family == PF_INET6) {
+ const struct ipv6_pinfo *np = inet6_sk(sk);
+ struct inet6_timewait_sock *tw6;
+
+ tw->tw_ipv6_offset = inet6_tw_offset(sk->sk_prot);
+ tw6 = inet6_twsk((struct sock *)tw);
+ ipv6_addr_copy(&tw6->tw_v6_daddr, &np->daddr);
+ ipv6_addr_copy(&tw6->tw_v6_rcv_saddr, &np->rcv_saddr);
+ tw->tw_ipv6only = np->ipv6only;
+ }
+#endif
/* Linkage updates. */
__inet_twsk_hashdance(tw, sk, &dccp_hashinfo);
@@ -170,6 +183,8 @@ out_free:
return newsk;
}
+EXPORT_SYMBOL_GPL(dccp_create_openreq_child);
+
/*
* Process an incoming packet for RESPOND sockets represented
* as an request_sock.
@@ -214,7 +229,7 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
goto drop;
}
- child = dccp_v4_request_recv_sock(sk, skb, req, NULL);
+ child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
if (child == NULL)
goto listen_overflow;
@@ -236,6 +251,8 @@ drop:
goto out;
}
+EXPORT_SYMBOL_GPL(dccp_check_req);
+
/*
* Queue segment on the new socket if the new socket is active,
* otherwise we just shortcircuit this and continue with
@@ -266,3 +283,5 @@ int dccp_child_process(struct sock *parent, struct sock *child,
sock_put(child);
return ret;
}
+
+EXPORT_SYMBOL_GPL(dccp_child_process);
diff --git a/net/dccp/output.c b/net/dccp/output.c
index 74ff87025878..efd7ffb903a1 100644
--- a/net/dccp/output.c
+++ b/net/dccp/output.c
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/skbuff.h>
+#include <net/inet_sock.h>
#include <net/sock.h>
#include "ackvec.h"
@@ -43,6 +44,7 @@ static int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb)
{
if (likely(skb != NULL)) {
const struct inet_sock *inet = inet_sk(sk);
+ const struct inet_connection_sock *icsk = inet_csk(sk);
struct dccp_sock *dp = dccp_sk(sk);
struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
struct dccp_hdr *dh;
@@ -108,8 +110,7 @@ static int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb)
break;
}
- dh->dccph_checksum = dccp_v4_checksum(skb, inet->saddr,
- inet->daddr);
+ icsk->icsk_af_ops->send_check(sk, skb->len, skb);
if (set_ack)
dccp_event_ack_sent(sk);
@@ -117,7 +118,7 @@ static int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb)
DCCP_INC_STATS(DCCP_MIB_OUTSEGS);
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
- err = ip_queue_xmit(skb, 0);
+ err = icsk->icsk_af_ops->queue_xmit(skb, 0);
if (err <= 0)
return err;
@@ -134,20 +135,13 @@ static int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb)
unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu)
{
+ struct inet_connection_sock *icsk = inet_csk(sk);
struct dccp_sock *dp = dccp_sk(sk);
- int mss_now;
-
- /*
- * FIXME: we really should be using the af_specific thing to support
- * IPv6.
- * mss_now = pmtu - tp->af_specific->net_header_len -
- * sizeof(struct dccp_hdr) - sizeof(struct dccp_hdr_ext);
- */
- mss_now = pmtu - sizeof(struct iphdr) - sizeof(struct dccp_hdr) -
- sizeof(struct dccp_hdr_ext);
+ int mss_now = (pmtu - icsk->icsk_af_ops->net_header_len -
+ sizeof(struct dccp_hdr) - sizeof(struct dccp_hdr_ext));
/* Now subtract optional transport overhead */
- mss_now -= dp->dccps_ext_header_len;
+ mss_now -= icsk->icsk_ext_hdr_len;
/*
* FIXME: this should come from the CCID infrastructure, where, say,
@@ -160,12 +154,14 @@ unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu)
mss_now -= ((5 + 6 + 10 + 6 + 6 + 6 + 3) / 4) * 4;
/* And store cached results */
- dp->dccps_pmtu_cookie = pmtu;
+ icsk->icsk_pmtu_cookie = pmtu;
dp->dccps_mss_cache = mss_now;
return mss_now;
}
+EXPORT_SYMBOL_GPL(dccp_sync_mss);
+
void dccp_write_space(struct sock *sk)
{
read_lock(&sk->sk_callback_lock);
@@ -266,7 +262,7 @@ int dccp_write_xmit(struct sock *sk, struct sk_buff *skb, long *timeo)
int dccp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
{
- if (inet_sk_rebuild_header(sk) != 0)
+ if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk) != 0)
return -EHOSTUNREACH; /* Routing failure or similar. */
return dccp_transmit_skb(sk, (skb_cloned(skb) ?
@@ -321,6 +317,8 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
return skb;
}
+EXPORT_SYMBOL_GPL(dccp_make_response);
+
struct sk_buff *dccp_make_reset(struct sock *sk, struct dst_entry *dst,
const enum dccp_reset_codes code)
@@ -377,6 +375,7 @@ struct sk_buff *dccp_make_reset(struct sock *sk, struct dst_entry *dst,
*/
static inline void dccp_connect_init(struct sock *sk)
{
+ struct dccp_sock *dp = dccp_sk(sk);
struct dst_entry *dst = __sk_dst_get(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
@@ -385,10 +384,16 @@ static inline void dccp_connect_init(struct sock *sk)
dccp_sync_mss(sk, dst_mtu(dst));
- /*
- * FIXME: set dp->{dccps_swh,dccps_swl}, with
- * something like dccp_inc_seq
- */
+ dccp_update_gss(sk, dp->dccps_iss);
+ /*
+ * SWL and AWL are initially adjusted so that they are not less than
+ * the initial Sequence Numbers received and sent, respectively:
+ * SWL := max(GSR + 1 - floor(W/4), ISR),
+ * AWL := max(GSS - W' + 1, ISS).
+ * These adjustments MUST be applied only at the beginning of the
+ * connection.
+ */
+ dccp_set_seqno(&dp->dccps_awl, max48(dp->dccps_awl, dp->dccps_iss));
icsk->icsk_retransmits = 0;
}
@@ -420,6 +425,8 @@ int dccp_connect(struct sock *sk)
return 0;
}
+EXPORT_SYMBOL_GPL(dccp_connect);
+
void dccp_send_ack(struct sock *sk)
{
/* If we have been reset, we may not send again. */
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 8a6b2a9e4581..65b11ea90d85 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -24,7 +24,7 @@
#include <net/checksum.h>
#include <net/inet_common.h>
-#include <net/ip.h>
+#include <net/inet_sock.h>
#include <net/protocol.h>
#include <net/sock.h>
#include <net/xfrm.h>
@@ -34,15 +34,18 @@
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/poll.h>
-#include <linux/dccp.h>
#include "ccid.h"
#include "dccp.h"
DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
+EXPORT_SYMBOL_GPL(dccp_statistics);
+
atomic_t dccp_orphan_count = ATOMIC_INIT(0);
+EXPORT_SYMBOL_GPL(dccp_orphan_count);
+
static struct net_protocol dccp_protocol = {
.handler = dccp_v4_rcv,
.err_handler = dccp_v4_err,
@@ -149,6 +152,8 @@ int dccp_disconnect(struct sock *sk, int flags)
return err;
}
+EXPORT_SYMBOL_GPL(dccp_disconnect);
+
/*
* Wait for a DCCP event.
*
@@ -156,8 +161,8 @@ int dccp_disconnect(struct sock *sk, int flags)
* take care of normal races (between the test and the event) and we don't
* go look at any of the socket buffers directly.
*/
-static unsigned int dccp_poll(struct file *file, struct socket *sock,
- poll_table *wait)
+unsigned int dccp_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
{
unsigned int mask;
struct sock *sk = sock->sk;
@@ -205,12 +210,16 @@ static unsigned int dccp_poll(struct file *file, struct socket *sock,
return mask;
}
+EXPORT_SYMBOL_GPL(dccp_poll);
+
int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
dccp_pr_debug("entry\n");
return -ENOIOCTLCMD;
}
+EXPORT_SYMBOL_GPL(dccp_ioctl);
+
static int dccp_setsockopt_service(struct sock *sk, const u32 service,
char __user *optval, int optlen)
{
@@ -254,7 +263,9 @@ int dccp_setsockopt(struct sock *sk, int level, int optname,
int val;
if (level != SOL_DCCP)
- return ip_setsockopt(sk, level, optname, optval, optlen);
+ return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
+ optname, optval,
+ optlen);
if (optlen < sizeof(int))
return -EINVAL;
@@ -282,6 +293,8 @@ int dccp_setsockopt(struct sock *sk, int level, int optname,
return err;
}
+EXPORT_SYMBOL_GPL(dccp_setsockopt);
+
static int dccp_getsockopt_service(struct sock *sk, int len,
u32 __user *optval,
int __user *optlen)
@@ -320,8 +333,9 @@ int dccp_getsockopt(struct sock *sk, int level, int optname,
int val, len;
if (level != SOL_DCCP)
- return ip_getsockopt(sk, level, optname, optval, optlen);
-
+ return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
+ optname, optval,
+ optlen);
if (get_user(len, optlen))
return -EFAULT;
@@ -354,6 +368,8 @@ int dccp_getsockopt(struct sock *sk, int level, int optname,
return 0;
}
+EXPORT_SYMBOL_GPL(dccp_getsockopt);
+
int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len)
{
@@ -410,6 +426,8 @@ out_discard:
goto out_release;
}
+EXPORT_SYMBOL_GPL(dccp_sendmsg);
+
int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int nonblock, int flags, int *addr_len)
{
@@ -507,7 +525,9 @@ out:
return len;
}
-static int inet_dccp_listen(struct socket *sock, int backlog)
+EXPORT_SYMBOL_GPL(dccp_recvmsg);
+
+int inet_dccp_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
unsigned char old_state;
@@ -543,6 +563,8 @@ out:
return err;
}
+EXPORT_SYMBOL_GPL(inet_dccp_listen);
+
static const unsigned char dccp_new_state[] = {
/* current state: new state: action: */
[0] = DCCP_CLOSED,
@@ -648,12 +670,16 @@ adjudge_to_death:
sock_put(sk);
}
+EXPORT_SYMBOL_GPL(dccp_close);
+
void dccp_shutdown(struct sock *sk, int how)
{
dccp_pr_debug("entry\n");
}
-static struct proto_ops inet_dccp_ops = {
+EXPORT_SYMBOL_GPL(dccp_shutdown);
+
+static const struct proto_ops inet_dccp_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release,
@@ -681,11 +707,11 @@ extern struct net_proto_family inet_family_ops;
static struct inet_protosw dccp_v4_protosw = {
.type = SOCK_DCCP,
.protocol = IPPROTO_DCCP,
- .prot = &dccp_v4_prot,
+ .prot = &dccp_prot,
.ops = &inet_dccp_ops,
.capability = -1,
.no_check = 0,
- .flags = 0,
+ .flags = INET_PROTOSW_ICSK,
};
/*
@@ -760,13 +786,15 @@ MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
int dccp_debug;
module_param(dccp_debug, int, 0444);
MODULE_PARM_DESC(dccp_debug, "Enable debug messages");
+
+EXPORT_SYMBOL_GPL(dccp_debug);
#endif
static int __init dccp_init(void)
{
unsigned long goal;
int ehash_order, bhash_order, i;
- int rc = proto_register(&dccp_v4_prot, 1);
+ int rc = proto_register(&dccp_prot, 1);
if (rc)
goto out;
@@ -869,7 +897,7 @@ out_free_bind_bucket_cachep:
kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
dccp_hashinfo.bind_bucket_cachep = NULL;
out_proto_unregister:
- proto_unregister(&dccp_v4_prot);
+ proto_unregister(&dccp_prot);
goto out;
}
@@ -892,7 +920,7 @@ static void __exit dccp_fini(void)
get_order(dccp_hashinfo.ehash_size *
sizeof(struct inet_ehash_bucket)));
kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
- proto_unregister(&dccp_v4_prot);
+ proto_unregister(&dccp_prot);
}
module_init(dccp_init);
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index d402e9020c68..78ec5344be86 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -149,7 +149,7 @@ static void dn_keepalive(struct sock *sk);
#define DN_SK_HASH_MASK (DN_SK_HASH_SIZE - 1)
-static struct proto_ops dn_proto_ops;
+static const struct proto_ops dn_proto_ops;
static DEFINE_RWLOCK(dn_hash_lock);
static struct hlist_head dn_sk_hash[DN_SK_HASH_SIZE];
static struct hlist_head dn_wild_sk;
@@ -1252,7 +1252,7 @@ static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
break;
default:
- err = dev_ioctl(cmd, (void __user *)arg);
+ err = -ENOIOCTLCMD;
break;
}
@@ -2342,7 +2342,7 @@ static struct net_proto_family dn_family_ops = {
.owner = THIS_MODULE,
};
-static struct proto_ops dn_proto_ops = {
+static const struct proto_ops dn_proto_ops = {
.family = AF_DECnet,
.owner = THIS_MODULE,
.release = dn_release,
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
index 8d0cc3cf3e49..33ab256cfd4a 100644
--- a/net/decnet/dn_neigh.c
+++ b/net/decnet/dn_neigh.c
@@ -408,11 +408,14 @@ int dn_neigh_router_hello(struct sk_buff *skb)
}
}
- if (!dn_db->router) {
- dn_db->router = neigh_clone(neigh);
- } else {
- if (msg->priority > ((struct dn_neigh *)dn_db->router)->priority)
- neigh_release(xchg(&dn_db->router, neigh_clone(neigh)));
+ /* Only use routers in our area */
+ if ((dn_ntohs(src)>>10) == dn_ntohs((decnet_address)>>10)) {
+ if (!dn_db->router) {
+ dn_db->router = neigh_clone(neigh);
+ } else {
+ if (msg->priority > ((struct dn_neigh *)dn_db->router)->priority)
+ neigh_release(xchg(&dn_db->router, neigh_clone(neigh)));
+ }
}
write_unlock(&neigh->lock);
neigh_release(neigh);
diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c
index 369f25b60f3f..44bda85e678f 100644
--- a/net/decnet/dn_nsp_in.c
+++ b/net/decnet/dn_nsp_in.c
@@ -793,7 +793,6 @@ static int dn_nsp_rx_packet(struct sk_buff *skb)
got_it:
if (sk != NULL) {
struct dn_scp *scp = DN_SK(sk);
- int ret;
/* Reset backoff */
scp->nsp_rxtshift = 0;
@@ -807,21 +806,7 @@ got_it:
goto free_out;
}
- bh_lock_sock(sk);
- ret = NET_RX_SUCCESS;
- if (decnet_debug_level & 8)
- printk(KERN_DEBUG "NSP: 0x%02x 0x%02x 0x%04x 0x%04x %d\n",
- (int)cb->rt_flags, (int)cb->nsp_flags,
- (int)cb->src_port, (int)cb->dst_port,
- !!sock_owned_by_user(sk));
- if (!sock_owned_by_user(sk))
- ret = dn_nsp_backlog_rcv(sk, skb);
- else
- sk_add_backlog(sk, skb);
- bh_unlock_sock(sk);
- sock_put(sk);
-
- return ret;
+ return sk_receive_skb(sk, skb);
}
return dn_nsp_no_socket(skb, reason);
diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c
index 34fdac51df96..c792994d7952 100644
--- a/net/econet/af_econet.c
+++ b/net/econet/af_econet.c
@@ -31,6 +31,7 @@
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <linux/skbuff.h>
+#include <linux/udp.h>
#include <net/sock.h>
#include <net/inet_common.h>
#include <linux/stat.h>
@@ -45,7 +46,7 @@
#include <asm/uaccess.h>
#include <asm/system.h>
-static struct proto_ops econet_ops;
+static const struct proto_ops econet_ops;
static struct hlist_head econet_sklist;
static DEFINE_RWLOCK(econet_lock);
@@ -56,7 +57,7 @@ static struct net_device *net2dev_map[256];
#define EC_PORT_IP 0xd2
#ifdef CONFIG_ECONET_AUNUDP
-static spinlock_t aun_queue_lock;
+static DEFINE_SPINLOCK(aun_queue_lock);
static struct socket *udpsock;
#define AUN_PORT 0x8000
@@ -686,7 +687,7 @@ static int econet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg
break;
default:
- return dev_ioctl(cmd, argp);
+ return -ENOIOCTLCMD;
}
/*NOTREACHED*/
return 0;
@@ -698,7 +699,7 @@ static struct net_proto_family econet_family_ops = {
.owner = THIS_MODULE,
};
-static struct proto_ops SOCKOPS_WRAPPED(econet_ops) = {
+static const struct proto_ops SOCKOPS_WRAPPED(econet_ops) = {
.family = PF_ECONET,
.owner = THIS_MODULE,
.release = econet_release,
diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c
index 03efaacbdb73..4cc6f41c6930 100644
--- a/net/ieee80211/ieee80211_rx.c
+++ b/net/ieee80211/ieee80211_rx.c
@@ -410,9 +410,8 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
return 1;
}
- if ((is_multicast_ether_addr(hdr->addr1) ||
- is_broadcast_ether_addr(hdr->addr2)) ? ieee->host_mc_decrypt :
- ieee->host_decrypt) {
+ if (is_multicast_ether_addr(hdr->addr1)
+ ? ieee->host_mc_decrypt : ieee->host_decrypt) {
int idx = 0;
if (skb->len >= hdrlen + 3)
idx = skb->data[hdrlen + 3] >> 6;
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index e55136ae09f4..011cca7ae02b 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -456,6 +456,14 @@ config TCP_CONG_BIC
increase provides TCP friendliness.
See http://www.csc.ncsu.edu/faculty/rhee/export/bitcp/
+config TCP_CONG_CUBIC
+ tristate "CUBIC TCP"
+ default m
+ ---help---
+ This is version 2.0 of BIC-TCP which uses a cubic growth function
+ among other techniques.
+ See http://www.csc.ncsu.edu/faculty/rhee/export/bitcp/cubic-paper.pdf
+
config TCP_CONG_WESTWOOD
tristate "TCP Westwood+"
default m
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index f0435d00db6b..c54edd76de09 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_INET_DIAG) += inet_diag.o
obj-$(CONFIG_IP_ROUTE_MULTIPATH_CACHED) += multipath.o
obj-$(CONFIG_INET_TCP_DIAG) += tcp_diag.o
obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o
+obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o
obj-$(CONFIG_TCP_CONG_WESTWOOD) += tcp_westwood.o
obj-$(CONFIG_TCP_CONG_HSTCP) += tcp_highspeed.o
obj-$(CONFIG_TCP_CONG_HYBLA) += tcp_hybla.o
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index d368cf249000..966a071a408c 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -93,6 +93,7 @@
#include <linux/smp_lock.h>
#include <linux/inet.h>
#include <linux/igmp.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <net/ip.h>
#include <net/protocol.h>
@@ -302,6 +303,7 @@ lookup_protocol:
sk->sk_reuse = 1;
inet = inet_sk(sk);
+ inet->is_icsk = INET_PROTOSW_ICSK & answer_flags;
if (SOCK_RAW == sock->type) {
inet->num = protocol;
@@ -775,16 +777,16 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
err = devinet_ioctl(cmd, (void __user *)arg);
break;
default:
- if (!sk->sk_prot->ioctl ||
- (err = sk->sk_prot->ioctl(sk, cmd, arg)) ==
- -ENOIOCTLCMD)
- err = dev_ioctl(cmd, (void __user *)arg);
+ if (sk->sk_prot->ioctl)
+ err = sk->sk_prot->ioctl(sk, cmd, arg);
+ else
+ err = -ENOIOCTLCMD;
break;
}
return err;
}
-struct proto_ops inet_stream_ops = {
+const struct proto_ops inet_stream_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release,
@@ -805,7 +807,7 @@ struct proto_ops inet_stream_ops = {
.sendpage = tcp_sendpage
};
-struct proto_ops inet_dgram_ops = {
+const struct proto_ops inet_dgram_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release,
@@ -830,7 +832,7 @@ struct proto_ops inet_dgram_ops = {
* For SOCK_RAW sockets; should be the same as inet_dgram_ops but without
* udp_poll
*/
-static struct proto_ops inet_sockraw_ops = {
+static const struct proto_ops inet_sockraw_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release,
@@ -869,7 +871,8 @@ static struct inet_protosw inetsw_array[] =
.ops = &inet_stream_ops,
.capability = -1,
.no_check = 0,
- .flags = INET_PROTOSW_PERMANENT,
+ .flags = INET_PROTOSW_PERMANENT |
+ INET_PROTOSW_ICSK,
},
{
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c
index 035ad2c9e1ba..aed537fa2c88 100644
--- a/net/ipv4/ah4.c
+++ b/net/ipv4/ah4.c
@@ -6,6 +6,7 @@
#include <linux/crypto.h>
#include <linux/pfkeyv2.h>
#include <net/icmp.h>
+#include <net/protocol.h>
#include <asm/scatterlist.h>
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index b425748f02d7..37432088fe6d 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -86,6 +86,7 @@
#include <linux/in.h>
#include <linux/mm.h>
#include <linux/inet.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/fddidevice.h>
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 04a6fe3e95a2..7b9bb28e2ee9 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -58,6 +58,7 @@
#endif
#include <linux/kmod.h>
+#include <net/arp.h>
#include <net/ip.h>
#include <net/route.h>
#include <net/ip_fib.h>
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 1b18ce66e7b7..73bfcae8af9c 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -9,6 +9,7 @@
#include <linux/pfkeyv2.h>
#include <linux/random.h>
#include <net/icmp.h>
+#include <net/protocol.h>
#include <net/udp.h>
/* decapsulation data for use when post-processing */
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 19b1b984d687..18f5e509281a 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -30,6 +30,7 @@
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/inet.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c
index 7ea0209cb169..e2890ec8159e 100644
--- a/net/ipv4/fib_hash.c
+++ b/net/ipv4/fib_hash.c
@@ -29,6 +29,7 @@
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/inet.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/proc_fs.h>
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c
index 0b298bbc1518..0dd4d06e456d 100644
--- a/net/ipv4/fib_rules.c
+++ b/net/ipv4/fib_rules.c
@@ -33,6 +33,7 @@
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/inet.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/proc_fs.h>
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 6d2a6ac070e3..ef4724de7350 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -29,6 +29,7 @@
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/inet.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/proc_fs.h>
@@ -36,6 +37,7 @@
#include <linux/netlink.h>
#include <linux/init.h>
+#include <net/arp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 705e3ce86df9..e320b32373e5 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -41,6 +41,13 @@
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
+ *
+ * Substantial contributions to this work comes from:
+ *
+ * David S. Miller, <davem@davemloft.net>
+ * Stephen Hemminger <shemminger@osdl.org>
+ * Paul E. McKenney <paulmck@us.ibm.com>
+ * Patrick McHardy <kaber@trash.net>
*/
#define VERSION "0.404"
@@ -59,6 +66,7 @@
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/inet.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/proc_fs.h>
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 92e23b2ad4d2..be5a519cd2f8 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -73,6 +73,7 @@
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/inet.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/string.h>
#include <linux/netfilter_ipv4.h>
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 4a195c724f01..34758118c10c 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -91,6 +91,8 @@
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/times.h>
+
+#include <net/arp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 3fe021f1a566..ae20281d8deb 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -37,7 +37,8 @@ EXPORT_SYMBOL(inet_csk_timer_bug_msg);
*/
int sysctl_local_port_range[2] = { 1024, 4999 };
-static inline int inet_csk_bind_conflict(struct sock *sk, struct inet_bind_bucket *tb)
+int inet_csk_bind_conflict(const struct sock *sk,
+ const struct inet_bind_bucket *tb)
{
const u32 sk_rcv_saddr = inet_rcv_saddr(sk);
struct sock *sk2;
@@ -62,11 +63,15 @@ static inline int inet_csk_bind_conflict(struct sock *sk, struct inet_bind_bucke
return node != NULL;
}
+EXPORT_SYMBOL_GPL(inet_csk_bind_conflict);
+
/* Obtain a reference to a local port for the given sock,
* if snum is zero it means select any available local port.
*/
int inet_csk_get_port(struct inet_hashinfo *hashinfo,
- struct sock *sk, unsigned short snum)
+ struct sock *sk, unsigned short snum,
+ int (*bind_conflict)(const struct sock *sk,
+ const struct inet_bind_bucket *tb))
{
struct inet_bind_hashbucket *head;
struct hlist_node *node;
@@ -125,7 +130,7 @@ tb_found:
goto success;
} else {
ret = 1;
- if (inet_csk_bind_conflict(sk, tb))
+ if (bind_conflict(sk, tb))
goto fail_unlock;
}
}
@@ -380,7 +385,7 @@ struct request_sock *inet_csk_search_req(const struct sock *sk,
EXPORT_SYMBOL_GPL(inet_csk_search_req);
void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
- const unsigned timeout)
+ unsigned long timeout)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
@@ -631,3 +636,15 @@ void inet_csk_listen_stop(struct sock *sk)
}
EXPORT_SYMBOL_GPL(inet_csk_listen_stop);
+
+void inet_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
+ const struct inet_sock *inet = inet_sk(sk);
+
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = inet->daddr;
+ sin->sin_port = inet->dport;
+}
+
+EXPORT_SYMBOL_GPL(inet_csk_addr2sockaddr);
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 39061ed53cfd..c49908192047 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -112,12 +112,12 @@ static int inet_diag_fill(struct sk_buff *skb, struct sock *sk,
r->idiag_inode = 0;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
if (r->idiag_family == AF_INET6) {
- const struct tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk);
+ const struct inet6_timewait_sock *tw6 = inet6_twsk(sk);
ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
- &tcp6tw->tw_v6_rcv_saddr);
+ &tw6->tw_v6_rcv_saddr);
ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
- &tcp6tw->tw_v6_daddr);
+ &tw6->tw_v6_daddr);
}
#endif
nlh->nlmsg_len = skb->tail - b;
@@ -489,9 +489,9 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
if (r->idiag_family == AF_INET6) {
ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
- &tcp6_rsk(req)->loc_addr);
+ &inet6_rsk(req)->loc_addr);
ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
- &tcp6_rsk(req)->rmt_addr);
+ &inet6_rsk(req)->rmt_addr);
}
#endif
nlh->nlmsg_len = skb->tail - b;
@@ -553,13 +553,13 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
entry.saddr =
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
(entry.family == AF_INET6) ?
- tcp6_rsk(req)->loc_addr.s6_addr32 :
+ inet6_rsk(req)->loc_addr.s6_addr32 :
#endif
&ireq->loc_addr;
entry.daddr =
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
(entry.family == AF_INET6) ?
- tcp6_rsk(req)->rmt_addr.s6_addr32 :
+ inet6_rsk(req)->rmt_addr.s6_addr32 :
#endif
&ireq->rmt_addr;
entry.dport = ntohs(ireq->rmt_port);
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index e8d29fe736d2..33228115cda4 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -15,12 +15,14 @@
#include <linux/config.h>
#include <linux/module.h>
+#include <linux/random.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <net/inet_connection_sock.h>
#include <net/inet_hashtables.h>
+#include <net/ip.h>
/*
* Allocate and initialize a new local port bind bucket.
@@ -163,3 +165,179 @@ struct sock *__inet_lookup_listener(const struct hlist_head *head, const u32 dad
}
EXPORT_SYMBOL_GPL(__inet_lookup_listener);
+
+/* called with local bh disabled */
+static int __inet_check_established(struct inet_timewait_death_row *death_row,
+ struct sock *sk, __u16 lport,
+ struct inet_timewait_sock **twp)
+{
+ struct inet_hashinfo *hinfo = death_row->hashinfo;
+ struct inet_sock *inet = inet_sk(sk);
+ u32 daddr = inet->rcv_saddr;
+ u32 saddr = inet->daddr;
+ int dif = sk->sk_bound_dev_if;
+ INET_ADDR_COOKIE(acookie, saddr, daddr)
+ const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
+ unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport);
+ struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
+ struct sock *sk2;
+ const struct hlist_node *node;
+ struct inet_timewait_sock *tw;
+
+ prefetch(head->chain.first);
+ write_lock(&head->lock);
+
+ /* Check TIME-WAIT sockets first. */
+ sk_for_each(sk2, node, &(head + hinfo->ehash_size)->chain) {
+ tw = inet_twsk(sk2);
+
+ if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) {
+ if (twsk_unique(sk, sk2, twp))
+ goto unique;
+ else
+ goto not_unique;
+ }
+ }
+ tw = NULL;
+
+ /* And established part... */
+ sk_for_each(sk2, node, &head->chain) {
+ if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
+ goto not_unique;
+ }
+
+unique:
+ /* Must record num and sport now. Otherwise we will see
+ * in hash table socket with a funny identity. */
+ inet->num = lport;
+ inet->sport = htons(lport);
+ sk->sk_hash = hash;
+ BUG_TRAP(sk_unhashed(sk));
+ __sk_add_node(sk, &head->chain);
+ sock_prot_inc_use(sk->sk_prot);
+ write_unlock(&head->lock);
+
+ if (twp) {
+ *twp = tw;
+ NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
+ } else if (tw) {
+ /* Silly. Should hash-dance instead... */
+ inet_twsk_deschedule(tw, death_row);
+ NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
+
+ inet_twsk_put(tw);
+ }
+
+ return 0;
+
+not_unique:
+ write_unlock(&head->lock);
+ return -EADDRNOTAVAIL;
+}
+
+static inline u32 inet_sk_port_offset(const struct sock *sk)
+{
+ const struct inet_sock *inet = inet_sk(sk);
+ return secure_ipv4_port_ephemeral(inet->rcv_saddr, inet->daddr,
+ inet->dport);
+}
+
+/*
+ * Bind a port for a connect operation and hash it.
+ */
+int inet_hash_connect(struct inet_timewait_death_row *death_row,
+ struct sock *sk)
+{
+ struct inet_hashinfo *hinfo = death_row->hashinfo;
+ const unsigned short snum = inet_sk(sk)->num;
+ struct inet_bind_hashbucket *head;
+ struct inet_bind_bucket *tb;
+ int ret;
+
+ if (!snum) {
+ int low = sysctl_local_port_range[0];
+ int high = sysctl_local_port_range[1];
+ int range = high - low;
+ int i;
+ int port;
+ static u32 hint;
+ u32 offset = hint + inet_sk_port_offset(sk);
+ struct hlist_node *node;
+ struct inet_timewait_sock *tw = NULL;
+
+ local_bh_disable();
+ for (i = 1; i <= range; i++) {
+ port = low + (i + offset) % range;
+ head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)];
+ spin_lock(&head->lock);
+
+ /* Does not bother with rcv_saddr checks,
+ * because the established check is already
+ * unique enough.
+ */
+ inet_bind_bucket_for_each(tb, node, &head->chain) {
+ if (tb->port == port) {
+ BUG_TRAP(!hlist_empty(&tb->owners));
+ if (tb->fastreuse >= 0)
+ goto next_port;
+ if (!__inet_check_established(death_row,
+ sk, port,
+ &tw))
+ goto ok;
+ goto next_port;
+ }
+ }
+
+ tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep, head, port);
+ if (!tb) {
+ spin_unlock(&head->lock);
+ break;
+ }
+ tb->fastreuse = -1;
+ goto ok;
+
+ next_port:
+ spin_unlock(&head->lock);
+ }
+ local_bh_enable();
+
+ return -EADDRNOTAVAIL;
+
+ok:
+ hint += i;
+
+ /* Head lock still held and bh's disabled */
+ inet_bind_hash(sk, tb, port);
+ if (sk_unhashed(sk)) {
+ inet_sk(sk)->sport = htons(port);
+ __inet_hash(hinfo, sk, 0);
+ }
+ spin_unlock(&head->lock);
+
+ if (tw) {
+ inet_twsk_deschedule(tw, death_row);;
+ inet_twsk_put(tw);
+ }
+
+ ret = 0;
+ goto out;
+ }
+
+ head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)];
+ tb = inet_csk(sk)->icsk_bind_hash;
+ spin_lock_bh(&head->lock);
+ if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
+ __inet_hash(hinfo, sk, 0);
+ spin_unlock_bh(&head->lock);
+ return 0;
+ } else {
+ spin_unlock(&head->lock);
+ /* No definite answer... Walk to established hash table */
+ ret = __inet_check_established(death_row, sk, snum, NULL);
+out:
+ local_bh_enable();
+ return ret;
+ }
+}
+
+EXPORT_SYMBOL_GPL(inet_hash_connect);
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index a010e9a68811..417f126c749e 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -90,8 +90,9 @@ EXPORT_SYMBOL_GPL(__inet_twsk_hashdance);
struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int state)
{
- struct inet_timewait_sock *tw = kmem_cache_alloc(sk->sk_prot_creator->twsk_slab,
- SLAB_ATOMIC);
+ struct inet_timewait_sock *tw =
+ kmem_cache_alloc(sk->sk_prot_creator->twsk_prot->twsk_slab,
+ SLAB_ATOMIC);
if (tw != NULL) {
const struct inet_sock *inet = inet_sk(sk);
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index 2fc3fd38924f..ce5fe3f74a3d 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -401,6 +401,7 @@ struct inet_peer *inet_getpeer(__u32 daddr, int create)
return NULL;
n->v4daddr = daddr;
atomic_set(&n->refcnt, 1);
+ atomic_set(&n->rid, 0);
n->ip_id_count = secure_ip_id(daddr);
n->tcp_ts_stamp = 0;
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 8ce0ce2ee48e..ce2b70ce4018 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -22,6 +22,7 @@
* Patrick McHardy : LRU queue of frag heads for evictor.
*/
+#include <linux/compiler.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -38,6 +39,7 @@
#include <net/ip.h>
#include <net/icmp.h>
#include <net/checksum.h>
+#include <net/inetpeer.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/inet.h>
@@ -56,6 +58,8 @@
int sysctl_ipfrag_high_thresh = 256*1024;
int sysctl_ipfrag_low_thresh = 192*1024;
+int sysctl_ipfrag_max_dist = 64;
+
/* Important NOTE! Fragment queue must be destroyed before MSL expires.
* RFC791 is wrong proposing to prolongate timer each fragment arrival by TTL.
*/
@@ -89,8 +93,10 @@ struct ipq {
spinlock_t lock;
atomic_t refcnt;
struct timer_list timer; /* when will this queue expire? */
- int iif;
struct timeval stamp;
+ int iif;
+ unsigned int rid;
+ struct inet_peer *peer;
};
/* Hash table. */
@@ -195,6 +201,9 @@ static void ip_frag_destroy(struct ipq *qp, int *work)
BUG_TRAP(qp->last_in&COMPLETE);
BUG_TRAP(del_timer(&qp->timer) == 0);
+ if (qp->peer)
+ inet_putpeer(qp->peer);
+
/* Release all fragment data. */
fp = qp->fragments;
while (fp) {
@@ -353,6 +362,7 @@ static struct ipq *ip_frag_create(unsigned hash, struct iphdr *iph, u32 user)
qp->meat = 0;
qp->fragments = NULL;
qp->iif = 0;
+ qp->peer = sysctl_ipfrag_max_dist ? inet_getpeer(iph->saddr, 1) : NULL;
/* Initialize a timer for this entry. */
init_timer(&qp->timer);
@@ -398,6 +408,56 @@ static inline struct ipq *ip_find(struct iphdr *iph, u32 user)
return ip_frag_create(hash, iph, user);
}
+/* Is the fragment too far ahead to be part of ipq? */
+static inline int ip_frag_too_far(struct ipq *qp)
+{
+ struct inet_peer *peer = qp->peer;
+ unsigned int max = sysctl_ipfrag_max_dist;
+ unsigned int start, end;
+
+ int rc;
+
+ if (!peer || !max)
+ return 0;
+
+ start = qp->rid;
+ end = atomic_inc_return(&peer->rid);
+ qp->rid = end;
+
+ rc = qp->fragments && (end - start) > max;
+
+ if (rc) {
+ IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
+ }
+
+ return rc;
+}
+
+static int ip_frag_reinit(struct ipq *qp)
+{
+ struct sk_buff *fp;
+
+ if (!mod_timer(&qp->timer, jiffies + sysctl_ipfrag_time)) {
+ atomic_inc(&qp->refcnt);
+ return -ETIMEDOUT;
+ }
+
+ fp = qp->fragments;
+ do {
+ struct sk_buff *xp = fp->next;
+ frag_kfree_skb(fp, NULL);
+ fp = xp;
+ } while (fp);
+
+ qp->last_in = 0;
+ qp->len = 0;
+ qp->meat = 0;
+ qp->fragments = NULL;
+ qp->iif = 0;
+
+ return 0;
+}
+
/* Add new segment to existing queue. */
static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
{
@@ -408,6 +468,12 @@ static void ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
if (qp->last_in & COMPLETE)
goto err;
+ if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) &&
+ unlikely(ip_frag_too_far(qp)) && unlikely(ip_frag_reinit(qp))) {
+ ipq_kill(qp);
+ goto err;
+ }
+
offset = ntohs(skb->nh.iph->frag_off);
flags = offset & ~IP_OFFSET;
offset &= IP_OFFSET;
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
index 473d0f2b2e0d..e45846ae570b 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -128,6 +128,7 @@
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/inet.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index dbe12da8d8b3..d3f6c468faf4 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -22,6 +22,7 @@
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
+#include <net/route.h>
/*
* Write options to IP header, record destination address to
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index eba64e2bd397..2a830de3a699 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -445,6 +445,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
hlen = iph->ihl * 4;
mtu = dst_mtu(&rt->u.dst) - hlen; /* Size of data space */
+ IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE;
/* When frag_list is given, use it. First, check its validity:
* some transformers could create wrong frag_list or break existing
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 4f2d87257309..6986e11d65cc 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -25,12 +25,12 @@
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/icmp.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
-#include <net/tcp.h>
-#include <linux/tcp.h>
+#include <net/tcp_states.h>
#include <linux/udp.h>
#include <linux/igmp.h>
#include <linux/netfilter.h>
@@ -427,8 +427,8 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
err = ip_options_get_from_user(&opt, optval, optlen);
if (err)
break;
- if (sk->sk_type == SOCK_STREAM) {
- struct tcp_sock *tp = tcp_sk(sk);
+ if (inet->is_icsk) {
+ struct inet_connection_sock *icsk = inet_csk(sk);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
if (sk->sk_family == PF_INET ||
(!((1 << sk->sk_state) &
@@ -436,10 +436,10 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
inet->daddr != LOOPBACK4_IPV6)) {
#endif
if (inet->opt)
- tp->ext_header_len -= inet->opt->optlen;
+ icsk->icsk_ext_hdr_len -= inet->opt->optlen;
if (opt)
- tp->ext_header_len += opt->optlen;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ icsk->icsk_ext_hdr_len += opt->optlen;
+ icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
}
#endif
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index fc718df17b40..d64e2ec8da7b 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -28,6 +28,7 @@
#include <net/xfrm.h>
#include <net/icmp.h>
#include <net/ipcomp.h>
+#include <net/protocol.h>
struct ipcomp_tfms {
struct list_head list;
diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
index e8674baaa8d9..bb3613ec448c 100644
--- a/net/ipv4/ipconfig.c
+++ b/net/ipv4/ipconfig.c
@@ -42,6 +42,7 @@
#include <linux/in.h>
#include <linux/if.h>
#include <linux/inet.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
@@ -58,6 +59,7 @@
#include <net/arp.h>
#include <net/ip.h>
#include <net/ipconfig.h>
+#include <net/route.h>
#include <asm/uaccess.h>
#include <net/checksum.h>
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 302b7eb507c9..caa3b7d2e48a 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -52,6 +52,7 @@
#include <net/ip.h>
#include <net/protocol.h>
#include <linux/skbuff.h>
+#include <net/route.h>
#include <net/sock.h>
#include <net/icmp.h>
#include <net/udp.h>
diff --git a/net/ipv4/ipvs/ip_vs_app.c b/net/ipv4/ipvs/ip_vs_app.c
index d7eb680101c2..9b176a942ac5 100644
--- a/net/ipv4/ipvs/ip_vs_app.c
+++ b/net/ipv4/ipvs/ip_vs_app.c
@@ -224,34 +224,6 @@ void unregister_ip_vs_app(struct ip_vs_app *app)
}
-#if 0000
-/*
- * Get reference to app by name (called from user context)
- */
-struct ip_vs_app *ip_vs_app_get_by_name(char *appname)
-{
- struct ip_vs_app *app, *a = NULL;
-
- down(&__ip_vs_app_mutex);
-
- list_for_each_entry(ent, &ip_vs_app_list, a_list) {
- if (strcmp(app->name, appname))
- continue;
-
- /* softirq may call ip_vs_app_get too, so the caller
- must disable softirq on the current CPU */
- if (ip_vs_app_get(app))
- a = app;
- break;
- }
-
- up(&__ip_vs_app_mutex);
-
- return a;
-}
-#endif
-
-
/*
* Bind ip_vs_conn to its ip_vs_app (called by cp constructor)
*/
diff --git a/net/ipv4/ipvs/ip_vs_conn.c b/net/ipv4/ipvs/ip_vs_conn.c
index 2a3a8c59c655..81d90354c928 100644
--- a/net/ipv4/ipvs/ip_vs_conn.c
+++ b/net/ipv4/ipvs/ip_vs_conn.c
@@ -24,7 +24,10 @@
*
*/
+#include <linux/in.h>
+#include <linux/net.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h> /* for proc_net_* */
#include <linux/seq_file.h>
@@ -219,7 +222,7 @@ struct ip_vs_conn *ip_vs_conn_in_get
if (!cp && atomic_read(&ip_vs_conn_no_cport_cnt))
cp = __ip_vs_conn_in_get(protocol, s_addr, 0, d_addr, d_port);
- IP_VS_DBG(7, "lookup/in %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n",
+ IP_VS_DBG(9, "lookup/in %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n",
ip_vs_proto_name(protocol),
NIPQUAD(s_addr), ntohs(s_port),
NIPQUAD(d_addr), ntohs(d_port),
@@ -254,7 +257,7 @@ struct ip_vs_conn *ip_vs_ct_in_get
out:
ct_read_unlock(hash);
- IP_VS_DBG(7, "template lookup/in %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n",
+ IP_VS_DBG(9, "template lookup/in %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n",
ip_vs_proto_name(protocol),
NIPQUAD(s_addr), ntohs(s_port),
NIPQUAD(d_addr), ntohs(d_port),
@@ -295,7 +298,7 @@ struct ip_vs_conn *ip_vs_conn_out_get
ct_read_unlock(hash);
- IP_VS_DBG(7, "lookup/out %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n",
+ IP_VS_DBG(9, "lookup/out %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n",
ip_vs_proto_name(protocol),
NIPQUAD(s_addr), ntohs(s_port),
NIPQUAD(d_addr), ntohs(d_port),
@@ -391,8 +394,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
cp->flags |= atomic_read(&dest->conn_flags);
cp->dest = dest;
- IP_VS_DBG(9, "Bind-dest %s c:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d "
- "d:%u.%u.%u.%u:%d fwd:%c s:%u flg:%X cnt:%d destcnt:%d\n",
+ IP_VS_DBG(7, "Bind-dest %s c:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d "
+ "d:%u.%u.%u.%u:%d fwd:%c s:%u conn->flags:%X conn->refcnt:%d "
+ "dest->refcnt:%d\n",
ip_vs_proto_name(cp->protocol),
NIPQUAD(cp->caddr), ntohs(cp->cport),
NIPQUAD(cp->vaddr), ntohs(cp->vport),
@@ -430,8 +434,9 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
if (!dest)
return;
- IP_VS_DBG(9, "Unbind-dest %s c:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d "
- "d:%u.%u.%u.%u:%d fwd:%c s:%u flg:%X cnt:%d destcnt:%d\n",
+ IP_VS_DBG(7, "Unbind-dest %s c:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d "
+ "d:%u.%u.%u.%u:%d fwd:%c s:%u conn->flags:%X conn->refcnt:%d "
+ "dest->refcnt:%d\n",
ip_vs_proto_name(cp->protocol),
NIPQUAD(cp->caddr), ntohs(cp->cport),
NIPQUAD(cp->vaddr), ntohs(cp->vport),
@@ -571,7 +576,7 @@ static void ip_vs_conn_expire(unsigned long data)
ip_vs_conn_hash(cp);
expire_later:
- IP_VS_DBG(7, "delayed: refcnt-1=%d conn.n_control=%d\n",
+ IP_VS_DBG(7, "delayed: conn->refcnt-1=%d conn->n_control=%d\n",
atomic_read(&cp->refcnt)-1,
atomic_read(&cp->n_control));
diff --git a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c
index 1a0843cd58a9..1aca94a9fd8b 100644
--- a/net/ipv4/ipvs/ip_vs_core.c
+++ b/net/ipv4/ipvs/ip_vs_core.c
@@ -426,7 +426,7 @@ ip_vs_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
return NULL;
IP_VS_DBG(6, "Schedule fwd:%c c:%u.%u.%u.%u:%u v:%u.%u.%u.%u:%u "
- "d:%u.%u.%u.%u:%u flg:%X cnt:%d\n",
+ "d:%u.%u.%u.%u:%u conn->flags:%X conn->refcnt:%d\n",
ip_vs_fwd_tag(cp),
NIPQUAD(cp->caddr), ntohs(cp->cport),
NIPQUAD(cp->vaddr), ntohs(cp->vport),
diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c
index 9bdcf31b760e..c935c5086d33 100644
--- a/net/ipv4/ipvs/ip_vs_ctl.c
+++ b/net/ipv4/ipvs/ip_vs_ctl.c
@@ -35,6 +35,7 @@
#include <linux/netfilter_ipv4.h>
#include <net/ip.h>
+#include <net/route.h>
#include <net/sock.h>
#include <asm/uaccess.h>
@@ -447,7 +448,7 @@ ip_vs_service_get(__u32 fwmark, __u16 protocol, __u32 vaddr, __u16 vport)
out:
read_unlock(&__ip_vs_svc_lock);
- IP_VS_DBG(6, "lookup service: fwm %u %s %u.%u.%u.%u:%u %s\n",
+ IP_VS_DBG(9, "lookup service: fwm %u %s %u.%u.%u.%u:%u %s\n",
fwmark, ip_vs_proto_name(protocol),
NIPQUAD(vaddr), ntohs(vport),
svc?"hit":"not hit");
@@ -597,7 +598,7 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, __u32 daddr, __u16 dport)
*/
list_for_each_entry_safe(dest, nxt, &ip_vs_dest_trash, n_list) {
IP_VS_DBG(3, "Destination %u/%u.%u.%u.%u:%u still in trash, "
- "refcnt=%d\n",
+ "dest->refcnt=%d\n",
dest->vfwmark,
NIPQUAD(dest->addr), ntohs(dest->port),
atomic_read(&dest->refcnt));
@@ -804,7 +805,7 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user *udest)
dest = ip_vs_trash_get_dest(svc, daddr, dport);
if (dest != NULL) {
IP_VS_DBG(3, "Get destination %u.%u.%u.%u:%u from trash, "
- "refcnt=%d, service %u/%u.%u.%u.%u:%u\n",
+ "dest->refcnt=%d, service %u/%u.%u.%u.%u:%u\n",
NIPQUAD(daddr), ntohs(dport),
atomic_read(&dest->refcnt),
dest->vfwmark,
@@ -949,7 +950,8 @@ static void __ip_vs_del_dest(struct ip_vs_dest *dest)
atomic_dec(&dest->svc->refcnt);
kfree(dest);
} else {
- IP_VS_DBG(3, "Moving dest %u.%u.%u.%u:%u into trash, refcnt=%d\n",
+ IP_VS_DBG(3, "Moving dest %u.%u.%u.%u:%u into trash, "
+ "dest->refcnt=%d\n",
NIPQUAD(dest->addr), ntohs(dest->port),
atomic_read(&dest->refcnt));
list_add(&dest->n_list, &ip_vs_dest_trash);
diff --git a/net/ipv4/ipvs/ip_vs_dh.c b/net/ipv4/ipvs/ip_vs_dh.c
index f3bc320dce93..9fee19c4c617 100644
--- a/net/ipv4/ipvs/ip_vs_dh.c
+++ b/net/ipv4/ipvs/ip_vs_dh.c
@@ -37,8 +37,10 @@
*
*/
+#include <linux/ip.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/skbuff.h>
#include <net/ip_vs.h>
diff --git a/net/ipv4/ipvs/ip_vs_est.c b/net/ipv4/ipvs/ip_vs_est.c
index 67b3e2fc1fa1..e7004741ac73 100644
--- a/net/ipv4/ipvs/ip_vs_est.c
+++ b/net/ipv4/ipvs/ip_vs_est.c
@@ -13,7 +13,10 @@
* Changes:
*
*/
+#include <linux/config.h>
#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
#include <linux/types.h>
#include <net/ip_vs.h>
diff --git a/net/ipv4/ipvs/ip_vs_lblc.c b/net/ipv4/ipvs/ip_vs_lblc.c
index 561cda326fa8..6e5cb92a5c83 100644
--- a/net/ipv4/ipvs/ip_vs_lblc.c
+++ b/net/ipv4/ipvs/ip_vs_lblc.c
@@ -41,8 +41,10 @@
* me to write this module.
*/
+#include <linux/ip.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/skbuff.h>
/* for sysctl */
#include <linux/fs.h>
@@ -228,33 +230,6 @@ ip_vs_lblc_hash(struct ip_vs_lblc_table *tbl, struct ip_vs_lblc_entry *en)
}
-#if 0000
-/*
- * Unhash ip_vs_lblc_entry from ip_vs_lblc_table.
- * returns bool success.
- */
-static int ip_vs_lblc_unhash(struct ip_vs_lblc_table *tbl,
- struct ip_vs_lblc_entry *en)
-{
- if (list_empty(&en->list)) {
- IP_VS_ERR("ip_vs_lblc_unhash(): request for not hashed entry, "
- "called from %p\n", __builtin_return_address(0));
- return 0;
- }
-
- /*
- * Remove it from the table
- */
- write_lock(&tbl->lock);
- list_del(&en->list);
- INIT_LIST_HEAD(&en->list);
- write_unlock(&tbl->lock);
-
- return 1;
-}
-#endif
-
-
/*
* Get ip_vs_lblc_entry associated with supplied parameters.
*/
diff --git a/net/ipv4/ipvs/ip_vs_lblcr.c b/net/ipv4/ipvs/ip_vs_lblcr.c
index ce456dbf09a5..32ba37ba72d8 100644
--- a/net/ipv4/ipvs/ip_vs_lblcr.c
+++ b/net/ipv4/ipvs/ip_vs_lblcr.c
@@ -39,8 +39,10 @@
*
*/
+#include <linux/ip.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/skbuff.h>
/* for sysctl */
#include <linux/fs.h>
@@ -414,33 +416,6 @@ ip_vs_lblcr_hash(struct ip_vs_lblcr_table *tbl, struct ip_vs_lblcr_entry *en)
}
-#if 0000
-/*
- * Unhash ip_vs_lblcr_entry from ip_vs_lblcr_table.
- * returns bool success.
- */
-static int ip_vs_lblcr_unhash(struct ip_vs_lblcr_table *tbl,
- struct ip_vs_lblcr_entry *en)
-{
- if (list_empty(&en->list)) {
- IP_VS_ERR("ip_vs_lblcr_unhash(): request for not hashed entry, "
- "called from %p\n", __builtin_return_address(0));
- return 0;
- }
-
- /*
- * Remove it from the table
- */
- write_lock(&tbl->lock);
- list_del(&en->list);
- INIT_LIST_HEAD(&en->list);
- write_unlock(&tbl->lock);
-
- return 1;
-}
-#endif
-
-
/*
* Get ip_vs_lblcr_entry associated with supplied parameters.
*/
diff --git a/net/ipv4/ipvs/ip_vs_proto_ah.c b/net/ipv4/ipvs/ip_vs_proto_ah.c
index 453e94a0bbd7..8b0505b09317 100644
--- a/net/ipv4/ipvs/ip_vs_proto_ah.c
+++ b/net/ipv4/ipvs/ip_vs_proto_ah.c
@@ -12,6 +12,8 @@
*
*/
+#include <linux/in.h>
+#include <linux/ip.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
diff --git a/net/ipv4/ipvs/ip_vs_proto_esp.c b/net/ipv4/ipvs/ip_vs_proto_esp.c
index 478e5c7c7e8e..c36ccf057a19 100644
--- a/net/ipv4/ipvs/ip_vs_proto_esp.c
+++ b/net/ipv4/ipvs/ip_vs_proto_esp.c
@@ -12,6 +12,8 @@
*
*/
+#include <linux/in.h>
+#include <linux/ip.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
diff --git a/net/ipv4/ipvs/ip_vs_proto_tcp.c b/net/ipv4/ipvs/ip_vs_proto_tcp.c
index 0e878fd6215c..bc28b1160a3a 100644
--- a/net/ipv4/ipvs/ip_vs_proto_tcp.c
+++ b/net/ipv4/ipvs/ip_vs_proto_tcp.c
@@ -275,28 +275,6 @@ static int tcp_timeouts[IP_VS_TCP_S_LAST+1] = {
[IP_VS_TCP_S_LAST] = 2*HZ,
};
-
-#if 0
-
-/* FIXME: This is going to die */
-
-static int tcp_timeouts_dos[IP_VS_TCP_S_LAST+1] = {
- [IP_VS_TCP_S_NONE] = 2*HZ,
- [IP_VS_TCP_S_ESTABLISHED] = 8*60*HZ,
- [IP_VS_TCP_S_SYN_SENT] = 60*HZ,
- [IP_VS_TCP_S_SYN_RECV] = 10*HZ,
- [IP_VS_TCP_S_FIN_WAIT] = 60*HZ,
- [IP_VS_TCP_S_TIME_WAIT] = 60*HZ,
- [IP_VS_TCP_S_CLOSE] = 10*HZ,
- [IP_VS_TCP_S_CLOSE_WAIT] = 60*HZ,
- [IP_VS_TCP_S_LAST_ACK] = 30*HZ,
- [IP_VS_TCP_S_LISTEN] = 2*60*HZ,
- [IP_VS_TCP_S_SYNACK] = 100*HZ,
- [IP_VS_TCP_S_LAST] = 2*HZ,
-};
-
-#endif
-
static char * tcp_state_name_table[IP_VS_TCP_S_LAST+1] = {
[IP_VS_TCP_S_NONE] = "NONE",
[IP_VS_TCP_S_ESTABLISHED] = "ESTABLISHED",
@@ -448,7 +426,7 @@ set_tcp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp,
struct ip_vs_dest *dest = cp->dest;
IP_VS_DBG(8, "%s %s [%c%c%c%c] %u.%u.%u.%u:%d->"
- "%u.%u.%u.%u:%d state: %s->%s cnt:%d\n",
+ "%u.%u.%u.%u:%d state: %s->%s conn->refcnt:%d\n",
pp->name,
(state_off==TCP_DIR_OUTPUT)?"output ":"input ",
th->syn? 'S' : '.',
diff --git a/net/ipv4/ipvs/ip_vs_proto_udp.c b/net/ipv4/ipvs/ip_vs_proto_udp.c
index 8ae5f2e0aefa..89d9175d8f28 100644
--- a/net/ipv4/ipvs/ip_vs_proto_udp.c
+++ b/net/ipv4/ipvs/ip_vs_proto_udp.c
@@ -15,8 +15,11 @@
*
*/
+#include <linux/in.h>
+#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/netfilter_ipv4.h>
+#include <linux/udp.h>
#include <net/ip_vs.h>
diff --git a/net/ipv4/ipvs/ip_vs_sh.c b/net/ipv4/ipvs/ip_vs_sh.c
index 6f7c50e44a39..7775e6cc68be 100644
--- a/net/ipv4/ipvs/ip_vs_sh.c
+++ b/net/ipv4/ipvs/ip_vs_sh.c
@@ -34,8 +34,10 @@
*
*/
+#include <linux/ip.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/skbuff.h>
#include <net/ip_vs.h>
diff --git a/net/ipv4/ipvs/ip_vs_sync.c b/net/ipv4/ipvs/ip_vs_sync.c
index 2e5ced3d8062..1bca714bda3d 100644
--- a/net/ipv4/ipvs/ip_vs_sync.c
+++ b/net/ipv4/ipvs/ip_vs_sync.c
@@ -21,12 +21,14 @@
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/inetdevice.h>
#include <linux/net.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/igmp.h> /* for ip_mc_join_group */
+#include <linux/udp.h>
#include <net/ip.h>
#include <net/sock.h>
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index 058c48e258fc..d0a447e520a2 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -12,6 +12,7 @@ ip_nat_pptp-objs := ip_nat_helper_pptp.o ip_nat_proto_gre.o
# connection tracking
obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o
+obj-$(CONFIG_IP_NF_NAT) += ip_nat.o
# conntrack netlink interface
obj-$(CONFIG_IP_NF_CONNTRACK_NETLINK) += ip_conntrack_netlink.o
@@ -41,7 +42,7 @@ obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o
# the three instances of ip_tables
obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o
obj-$(CONFIG_IP_NF_MANGLE) += iptable_mangle.o
-obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o ip_nat.o
+obj-$(CONFIG_IP_NF_NAT) += iptable_nat.o
obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o
# matches
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 3c2e9639bba6..bba156304695 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -68,19 +68,14 @@ struct arpt_table_info {
unsigned int initial_entries;
unsigned int hook_entry[NF_ARP_NUMHOOKS];
unsigned int underflow[NF_ARP_NUMHOOKS];
- char entries[0] __attribute__((aligned(SMP_CACHE_BYTES)));
+ void *entries[NR_CPUS];
};
static LIST_HEAD(arpt_target);
static LIST_HEAD(arpt_tables);
+#define SET_COUNTER(c,b,p) do { (c).bcnt = (b); (c).pcnt = (p); } while(0)
#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
-#ifdef CONFIG_SMP
-#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
-#else
-#define TABLE_OFFSET(t,p) 0
-#endif
-
static inline int arp_devaddr_compare(const struct arpt_devaddr_info *ap,
char *hdr_addr, int len)
{
@@ -269,9 +264,7 @@ unsigned int arpt_do_table(struct sk_buff **pskb,
outdev = out ? out->name : nulldevname;
read_lock_bh(&table->lock);
- table_base = (void *)table->private->entries
- + TABLE_OFFSET(table->private,
- smp_processor_id());
+ table_base = (void *)table->private->entries[smp_processor_id()];
e = get_entry(table_base, table->private->hook_entry[hook]);
back = get_entry(table_base, table->private->underflow[hook]);
@@ -462,7 +455,8 @@ static inline int unconditional(const struct arpt_arp *arp)
/* Figures out from what hook each rule can be called: returns 0 if
* there are loops. Puts hook bitmask in comefrom.
*/
-static int mark_source_chains(struct arpt_table_info *newinfo, unsigned int valid_hooks)
+static int mark_source_chains(struct arpt_table_info *newinfo,
+ unsigned int valid_hooks, void *entry0)
{
unsigned int hook;
@@ -472,7 +466,7 @@ static int mark_source_chains(struct arpt_table_info *newinfo, unsigned int vali
for (hook = 0; hook < NF_ARP_NUMHOOKS; hook++) {
unsigned int pos = newinfo->hook_entry[hook];
struct arpt_entry *e
- = (struct arpt_entry *)(newinfo->entries + pos);
+ = (struct arpt_entry *)(entry0 + pos);
if (!(valid_hooks & (1 << hook)))
continue;
@@ -514,13 +508,13 @@ static int mark_source_chains(struct arpt_table_info *newinfo, unsigned int vali
goto next;
e = (struct arpt_entry *)
- (newinfo->entries + pos);
+ (entry0 + pos);
} while (oldpos == pos + e->next_offset);
/* Move along one */
size = e->next_offset;
e = (struct arpt_entry *)
- (newinfo->entries + pos + size);
+ (entry0 + pos + size);
e->counters.pcnt = pos;
pos += size;
} else {
@@ -537,7 +531,7 @@ static int mark_source_chains(struct arpt_table_info *newinfo, unsigned int vali
newpos = pos + e->next_offset;
}
e = (struct arpt_entry *)
- (newinfo->entries + newpos);
+ (entry0 + newpos);
e->counters.pcnt = pos;
pos = newpos;
}
@@ -689,6 +683,7 @@ static inline int cleanup_entry(struct arpt_entry *e, unsigned int *i)
static int translate_table(const char *name,
unsigned int valid_hooks,
struct arpt_table_info *newinfo,
+ void *entry0,
unsigned int size,
unsigned int number,
const unsigned int *hook_entries,
@@ -710,11 +705,11 @@ static int translate_table(const char *name,
i = 0;
/* Walk through entries, checking offsets. */
- ret = ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ ret = ARPT_ENTRY_ITERATE(entry0, newinfo->size,
check_entry_size_and_hooks,
newinfo,
- newinfo->entries,
- newinfo->entries + size,
+ entry0,
+ entry0 + size,
hook_entries, underflows, &i);
duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret);
if (ret != 0)
@@ -743,29 +738,26 @@ static int translate_table(const char *name,
}
}
- if (!mark_source_chains(newinfo, valid_hooks)) {
+ if (!mark_source_chains(newinfo, valid_hooks, entry0)) {
duprintf("Looping hook\n");
return -ELOOP;
}
/* Finally, each sanity check must pass */
i = 0;
- ret = ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ ret = ARPT_ENTRY_ITERATE(entry0, newinfo->size,
check_entry, name, size, &i);
if (ret != 0) {
- ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ ARPT_ENTRY_ITERATE(entry0, newinfo->size,
cleanup_entry, &i);
return ret;
}
/* And one copy for every other CPU */
for_each_cpu(i) {
- if (i == 0)
- continue;
- memcpy(newinfo->entries + SMP_ALIGN(newinfo->size) * i,
- newinfo->entries,
- SMP_ALIGN(newinfo->size));
+ if (newinfo->entries[i] && newinfo->entries[i] != entry0)
+ memcpy(newinfo->entries[i], entry0, newinfo->size);
}
return ret;
@@ -807,15 +799,42 @@ static inline int add_entry_to_counter(const struct arpt_entry *e,
return 0;
}
+static inline int set_entry_to_counter(const struct arpt_entry *e,
+ struct arpt_counters total[],
+ unsigned int *i)
+{
+ SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
+
+ (*i)++;
+ return 0;
+}
+
static void get_counters(const struct arpt_table_info *t,
struct arpt_counters counters[])
{
unsigned int cpu;
unsigned int i;
+ unsigned int curcpu;
+
+ /* Instead of clearing (by a previous call to memset())
+ * the counters and using adds, we set the counters
+ * with data used by 'current' CPU
+ * We dont care about preemption here.
+ */
+ curcpu = raw_smp_processor_id();
+
+ i = 0;
+ ARPT_ENTRY_ITERATE(t->entries[curcpu],
+ t->size,
+ set_entry_to_counter,
+ counters,
+ &i);
for_each_cpu(cpu) {
+ if (cpu == curcpu)
+ continue;
i = 0;
- ARPT_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
+ ARPT_ENTRY_ITERATE(t->entries[cpu],
t->size,
add_entry_to_counter,
counters,
@@ -831,6 +850,7 @@ static int copy_entries_to_user(unsigned int total_size,
struct arpt_entry *e;
struct arpt_counters *counters;
int ret = 0;
+ void *loc_cpu_entry;
/* We need atomic snapshot of counters: rest doesn't change
* (other than comefrom, which userspace doesn't care
@@ -843,13 +863,13 @@ static int copy_entries_to_user(unsigned int total_size,
return -ENOMEM;
/* First, sum counters... */
- memset(counters, 0, countersize);
write_lock_bh(&table->lock);
get_counters(table->private, counters);
write_unlock_bh(&table->lock);
- /* ... then copy entire thing from CPU 0... */
- if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
+ loc_cpu_entry = table->private->entries[raw_smp_processor_id()];
+ /* ... then copy entire thing ... */
+ if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
ret = -EFAULT;
goto free_counters;
}
@@ -859,7 +879,7 @@ static int copy_entries_to_user(unsigned int total_size,
for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
struct arpt_entry_target *t;
- e = (struct arpt_entry *)(table->private->entries + off);
+ e = (struct arpt_entry *)(loc_cpu_entry + off);
if (copy_to_user(userptr + off
+ offsetof(struct arpt_entry, counters),
&counters[num],
@@ -911,6 +931,47 @@ static int get_entries(const struct arpt_get_entries *entries,
return ret;
}
+static void free_table_info(struct arpt_table_info *info)
+{
+ int cpu;
+ for_each_cpu(cpu) {
+ if (info->size <= PAGE_SIZE)
+ kfree(info->entries[cpu]);
+ else
+ vfree(info->entries[cpu]);
+ }
+ kfree(info);
+}
+
+static struct arpt_table_info *alloc_table_info(unsigned int size)
+{
+ struct arpt_table_info *newinfo;
+ int cpu;
+
+ newinfo = kzalloc(sizeof(struct arpt_table_info), GFP_KERNEL);
+ if (!newinfo)
+ return NULL;
+
+ newinfo->size = size;
+
+ for_each_cpu(cpu) {
+ if (size <= PAGE_SIZE)
+ newinfo->entries[cpu] = kmalloc_node(size,
+ GFP_KERNEL,
+ cpu_to_node(cpu));
+ else
+ newinfo->entries[cpu] = vmalloc_node(size,
+ cpu_to_node(cpu));
+
+ if (newinfo->entries[cpu] == NULL) {
+ free_table_info(newinfo);
+ return NULL;
+ }
+ }
+
+ return newinfo;
+}
+
static int do_replace(void __user *user, unsigned int len)
{
int ret;
@@ -918,6 +979,7 @@ static int do_replace(void __user *user, unsigned int len)
struct arpt_table *t;
struct arpt_table_info *newinfo, *oldinfo;
struct arpt_counters *counters;
+ void *loc_cpu_entry, *loc_cpu_old_entry;
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
return -EFAULT;
@@ -930,13 +992,13 @@ static int do_replace(void __user *user, unsigned int len)
if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
return -ENOMEM;
- newinfo = vmalloc(sizeof(struct arpt_table_info)
- + SMP_ALIGN(tmp.size) *
- (highest_possible_processor_id()+1));
+ newinfo = alloc_table_info(tmp.size);
if (!newinfo)
return -ENOMEM;
- if (copy_from_user(newinfo->entries, user + sizeof(tmp),
+ /* choose the copy that is on our node/cpu */
+ loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
+ if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
tmp.size) != 0) {
ret = -EFAULT;
goto free_newinfo;
@@ -947,10 +1009,9 @@ static int do_replace(void __user *user, unsigned int len)
ret = -ENOMEM;
goto free_newinfo;
}
- memset(counters, 0, tmp.num_counters * sizeof(struct arpt_counters));
ret = translate_table(tmp.name, tmp.valid_hooks,
- newinfo, tmp.size, tmp.num_entries,
+ newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
tmp.hook_entry, tmp.underflow);
if (ret != 0)
goto free_newinfo_counters;
@@ -989,8 +1050,10 @@ static int do_replace(void __user *user, unsigned int len)
/* Get the old counters. */
get_counters(oldinfo, counters);
/* Decrease module usage counts and free resource */
- ARPT_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
- vfree(oldinfo);
+ loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
+ ARPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
+
+ free_table_info(oldinfo);
if (copy_to_user(tmp.counters, counters,
sizeof(struct arpt_counters) * tmp.num_counters) != 0)
ret = -EFAULT;
@@ -1002,11 +1065,11 @@ static int do_replace(void __user *user, unsigned int len)
module_put(t->me);
up(&arpt_mutex);
free_newinfo_counters_untrans:
- ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry, NULL);
+ ARPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
free_newinfo_counters:
vfree(counters);
free_newinfo:
- vfree(newinfo);
+ free_table_info(newinfo);
return ret;
}
@@ -1030,6 +1093,7 @@ static int do_add_counters(void __user *user, unsigned int len)
struct arpt_counters_info tmp, *paddc;
struct arpt_table *t;
int ret = 0;
+ void *loc_cpu_entry;
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
return -EFAULT;
@@ -1059,7 +1123,9 @@ static int do_add_counters(void __user *user, unsigned int len)
}
i = 0;
- ARPT_ENTRY_ITERATE(t->private->entries,
+ /* Choose the copy that is on our node */
+ loc_cpu_entry = t->private->entries[smp_processor_id()];
+ ARPT_ENTRY_ITERATE(loc_cpu_entry,
t->private->size,
add_counter_to_entry,
paddc->counters,
@@ -1220,30 +1286,32 @@ int arpt_register_table(struct arpt_table *table,
struct arpt_table_info *newinfo;
static struct arpt_table_info bootstrap
= { 0, 0, 0, { 0 }, { 0 }, { } };
+ void *loc_cpu_entry;
- newinfo = vmalloc(sizeof(struct arpt_table_info)
- + SMP_ALIGN(repl->size) *
- (highest_possible_processor_id()+1));
+ newinfo = alloc_table_info(repl->size);
if (!newinfo) {
ret = -ENOMEM;
return ret;
}
- memcpy(newinfo->entries, repl->entries, repl->size);
+
+ /* choose the copy on our node/cpu */
+ loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
+ memcpy(loc_cpu_entry, repl->entries, repl->size);
ret = translate_table(table->name, table->valid_hooks,
- newinfo, repl->size,
+ newinfo, loc_cpu_entry, repl->size,
repl->num_entries,
repl->hook_entry,
repl->underflow);
duprintf("arpt_register_table: translate table gives %d\n", ret);
if (ret != 0) {
- vfree(newinfo);
+ free_table_info(newinfo);
return ret;
}
ret = down_interruptible(&arpt_mutex);
if (ret != 0) {
- vfree(newinfo);
+ free_table_info(newinfo);
return ret;
}
@@ -1272,20 +1340,23 @@ int arpt_register_table(struct arpt_table *table,
return ret;
free_unlock:
- vfree(newinfo);
+ free_table_info(newinfo);
goto unlock;
}
void arpt_unregister_table(struct arpt_table *table)
{
+ void *loc_cpu_entry;
+
down(&arpt_mutex);
LIST_DELETE(&arpt_tables, table);
up(&arpt_mutex);
/* Decrease module usage counts and free resources */
- ARPT_ENTRY_ITERATE(table->private->entries, table->private->size,
+ loc_cpu_entry = table->private->entries[raw_smp_processor_id()];
+ ARPT_ENTRY_ITERATE(loc_cpu_entry, table->private->size,
cleanup_entry, NULL);
- vfree(table->private);
+ free_table_info(table->private);
}
/* The built-in targets: standard (NULL) and error. */
diff --git a/net/ipv4/netfilter/ip_conntrack_amanda.c b/net/ipv4/netfilter/ip_conntrack_amanda.c
index e52847fa10f5..0366eedb4d70 100644
--- a/net/ipv4/netfilter/ip_conntrack_amanda.c
+++ b/net/ipv4/netfilter/ip_conntrack_amanda.c
@@ -18,11 +18,13 @@
*
*/
+#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/ip.h>
#include <linux/moduleparam.h>
+#include <linux/udp.h>
#include <net/checksum.h>
#include <net/udp.h>
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_gre.c b/net/ipv4/netfilter/ip_conntrack_proto_gre.c
index 744abb9d377a..57956dee60c8 100644
--- a/net/ipv4/netfilter/ip_conntrack_proto_gre.c
+++ b/net/ipv4/netfilter/ip_conntrack_proto_gre.c
@@ -31,6 +31,7 @@
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/list.h>
+#include <linux/seq_file.h>
static DEFINE_RWLOCK(ip_ct_gre_lock);
#define ASSERT_READ_LOCK(x)
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_udp.c b/net/ipv4/netfilter/ip_conntrack_proto_udp.c
index f2dcac7c7660..46becbe4fe58 100644
--- a/net/ipv4/netfilter/ip_conntrack_proto_udp.c
+++ b/net/ipv4/netfilter/ip_conntrack_proto_udp.c
@@ -11,6 +11,7 @@
#include <linux/timer.h>
#include <linux/netfilter.h>
#include <linux/in.h>
+#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/seq_file.h>
#include <net/checksum.h>
diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c
index dd476b191f4b..a88bcc551244 100644
--- a/net/ipv4/netfilter/ip_conntrack_standalone.c
+++ b/net/ipv4/netfilter/ip_conntrack_standalone.c
@@ -27,6 +27,7 @@
#endif
#include <net/checksum.h>
#include <net/ip.h>
+#include <net/route.h>
#define ASSERT_READ_LOCK(x)
#define ASSERT_WRITE_LOCK(x)
diff --git a/net/ipv4/netfilter/ip_nat_snmp_basic.c b/net/ipv4/netfilter/ip_nat_snmp_basic.c
index 8acb7ed40b47..4f95d477805c 100644
--- a/net/ipv4/netfilter/ip_nat_snmp_basic.c
+++ b/net/ipv4/netfilter/ip_nat_snmp_basic.c
@@ -44,6 +44,7 @@
*
*/
#include <linux/config.h>
+#include <linux/in.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -53,6 +54,7 @@
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
#include <linux/netfilter_ipv4/ip_nat_helper.h>
#include <linux/ip.h>
+#include <linux/udp.h>
#include <net/checksum.h>
#include <net/udp.h>
#include <asm/uaccess.h>
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index 45886c8475e8..2a26d167e149 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -83,11 +83,6 @@ static DECLARE_MUTEX(ipt_mutex);
context stops packets coming through and allows user context to read
the counters or update the rules.
- To be cache friendly on SMP, we arrange them like so:
- [ n-entries ]
- ... cache-align padding ...
- [ n-entries ]
-
Hence the start of any table is given by get_table() below. */
/* The table itself */
@@ -105,20 +100,15 @@ struct ipt_table_info
unsigned int underflow[NF_IP_NUMHOOKS];
/* ipt_entry tables: one per CPU */
- char entries[0] ____cacheline_aligned;
+ void *entries[NR_CPUS];
};
static LIST_HEAD(ipt_target);
static LIST_HEAD(ipt_match);
static LIST_HEAD(ipt_tables);
+#define SET_COUNTER(c,b,p) do { (c).bcnt = (b); (c).pcnt = (p); } while(0)
#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
-#ifdef CONFIG_SMP
-#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
-#else
-#define TABLE_OFFSET(t,p) 0
-#endif
-
#if 0
#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
#define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
@@ -290,8 +280,7 @@ ipt_do_table(struct sk_buff **pskb,
read_lock_bh(&table->lock);
IP_NF_ASSERT(table->valid_hooks & (1 << hook));
- table_base = (void *)table->private->entries
- + TABLE_OFFSET(table->private, smp_processor_id());
+ table_base = (void *)table->private->entries[smp_processor_id()];
e = get_entry(table_base, table->private->hook_entry[hook]);
#ifdef CONFIG_NETFILTER_DEBUG
@@ -563,7 +552,8 @@ unconditional(const struct ipt_ip *ip)
/* Figures out from what hook each rule can be called: returns 0 if
there are loops. Puts hook bitmask in comefrom. */
static int
-mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
+mark_source_chains(struct ipt_table_info *newinfo,
+ unsigned int valid_hooks, void *entry0)
{
unsigned int hook;
@@ -572,7 +562,7 @@ mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
for (hook = 0; hook < NF_IP_NUMHOOKS; hook++) {
unsigned int pos = newinfo->hook_entry[hook];
struct ipt_entry *e
- = (struct ipt_entry *)(newinfo->entries + pos);
+ = (struct ipt_entry *)(entry0 + pos);
if (!(valid_hooks & (1 << hook)))
continue;
@@ -622,13 +612,13 @@ mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
goto next;
e = (struct ipt_entry *)
- (newinfo->entries + pos);
+ (entry0 + pos);
} while (oldpos == pos + e->next_offset);
/* Move along one */
size = e->next_offset;
e = (struct ipt_entry *)
- (newinfo->entries + pos + size);
+ (entry0 + pos + size);
e->counters.pcnt = pos;
pos += size;
} else {
@@ -645,7 +635,7 @@ mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
newpos = pos + e->next_offset;
}
e = (struct ipt_entry *)
- (newinfo->entries + newpos);
+ (entry0 + newpos);
e->counters.pcnt = pos;
pos = newpos;
}
@@ -855,6 +845,7 @@ static int
translate_table(const char *name,
unsigned int valid_hooks,
struct ipt_table_info *newinfo,
+ void *entry0,
unsigned int size,
unsigned int number,
const unsigned int *hook_entries,
@@ -875,11 +866,11 @@ translate_table(const char *name,
duprintf("translate_table: size %u\n", newinfo->size);
i = 0;
/* Walk through entries, checking offsets. */
- ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
check_entry_size_and_hooks,
newinfo,
- newinfo->entries,
- newinfo->entries + size,
+ entry0,
+ entry0 + size,
hook_entries, underflows, &i);
if (ret != 0)
return ret;
@@ -907,27 +898,24 @@ translate_table(const char *name,
}
}
- if (!mark_source_chains(newinfo, valid_hooks))
+ if (!mark_source_chains(newinfo, valid_hooks, entry0))
return -ELOOP;
/* Finally, each sanity check must pass */
i = 0;
- ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
check_entry, name, size, &i);
if (ret != 0) {
- IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ IPT_ENTRY_ITERATE(entry0, newinfo->size,
cleanup_entry, &i);
return ret;
}
/* And one copy for every other CPU */
for_each_cpu(i) {
- if (i == 0)
- continue;
- memcpy(newinfo->entries + SMP_ALIGN(newinfo->size) * i,
- newinfo->entries,
- SMP_ALIGN(newinfo->size));
+ if (newinfo->entries[i] && newinfo->entries[i] != entry0)
+ memcpy(newinfo->entries[i], entry0, newinfo->size);
}
return ret;
@@ -943,15 +931,12 @@ replace_table(struct ipt_table *table,
#ifdef CONFIG_NETFILTER_DEBUG
{
- struct ipt_entry *table_base;
- unsigned int i;
+ int cpu;
- for_each_cpu(i) {
- table_base =
- (void *)newinfo->entries
- + TABLE_OFFSET(newinfo, i);
-
- table_base->comefrom = 0xdead57ac;
+ for_each_cpu(cpu) {
+ struct ipt_entry *table_base = newinfo->entries[cpu];
+ if (table_base)
+ table_base->comefrom = 0xdead57ac;
}
}
#endif
@@ -986,16 +971,44 @@ add_entry_to_counter(const struct ipt_entry *e,
return 0;
}
+static inline int
+set_entry_to_counter(const struct ipt_entry *e,
+ struct ipt_counters total[],
+ unsigned int *i)
+{
+ SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
+
+ (*i)++;
+ return 0;
+}
+
static void
get_counters(const struct ipt_table_info *t,
struct ipt_counters counters[])
{
unsigned int cpu;
unsigned int i;
+ unsigned int curcpu;
+
+ /* Instead of clearing (by a previous call to memset())
+ * the counters and using adds, we set the counters
+ * with data used by 'current' CPU
+ * We dont care about preemption here.
+ */
+ curcpu = raw_smp_processor_id();
+
+ i = 0;
+ IPT_ENTRY_ITERATE(t->entries[curcpu],
+ t->size,
+ set_entry_to_counter,
+ counters,
+ &i);
for_each_cpu(cpu) {
+ if (cpu == curcpu)
+ continue;
i = 0;
- IPT_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
+ IPT_ENTRY_ITERATE(t->entries[cpu],
t->size,
add_entry_to_counter,
counters,
@@ -1012,24 +1025,29 @@ copy_entries_to_user(unsigned int total_size,
struct ipt_entry *e;
struct ipt_counters *counters;
int ret = 0;
+ void *loc_cpu_entry;
/* We need atomic snapshot of counters: rest doesn't change
(other than comefrom, which userspace doesn't care
about). */
countersize = sizeof(struct ipt_counters) * table->private->number;
- counters = vmalloc(countersize);
+ counters = vmalloc_node(countersize, numa_node_id());
if (counters == NULL)
return -ENOMEM;
/* First, sum counters... */
- memset(counters, 0, countersize);
write_lock_bh(&table->lock);
get_counters(table->private, counters);
write_unlock_bh(&table->lock);
- /* ... then copy entire thing from CPU 0... */
- if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
+ /* choose the copy that is on our node/cpu, ...
+ * This choice is lazy (because current thread is
+ * allowed to migrate to another cpu)
+ */
+ loc_cpu_entry = table->private->entries[raw_smp_processor_id()];
+ /* ... then copy entire thing ... */
+ if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
ret = -EFAULT;
goto free_counters;
}
@@ -1041,7 +1059,7 @@ copy_entries_to_user(unsigned int total_size,
struct ipt_entry_match *m;
struct ipt_entry_target *t;
- e = (struct ipt_entry *)(table->private->entries + off);
+ e = (struct ipt_entry *)(loc_cpu_entry + off);
if (copy_to_user(userptr + off
+ offsetof(struct ipt_entry, counters),
&counters[num],
@@ -1110,6 +1128,45 @@ get_entries(const struct ipt_get_entries *entries,
return ret;
}
+static void free_table_info(struct ipt_table_info *info)
+{
+ int cpu;
+ for_each_cpu(cpu) {
+ if (info->size <= PAGE_SIZE)
+ kfree(info->entries[cpu]);
+ else
+ vfree(info->entries[cpu]);
+ }
+ kfree(info);
+}
+
+static struct ipt_table_info *alloc_table_info(unsigned int size)
+{
+ struct ipt_table_info *newinfo;
+ int cpu;
+
+ newinfo = kzalloc(sizeof(struct ipt_table_info), GFP_KERNEL);
+ if (!newinfo)
+ return NULL;
+
+ newinfo->size = size;
+
+ for_each_cpu(cpu) {
+ if (size <= PAGE_SIZE)
+ newinfo->entries[cpu] = kmalloc_node(size,
+ GFP_KERNEL,
+ cpu_to_node(cpu));
+ else
+ newinfo->entries[cpu] = vmalloc_node(size, cpu_to_node(cpu));
+ if (newinfo->entries[cpu] == 0) {
+ free_table_info(newinfo);
+ return NULL;
+ }
+ }
+
+ return newinfo;
+}
+
static int
do_replace(void __user *user, unsigned int len)
{
@@ -1118,6 +1175,7 @@ do_replace(void __user *user, unsigned int len)
struct ipt_table *t;
struct ipt_table_info *newinfo, *oldinfo;
struct ipt_counters *counters;
+ void *loc_cpu_entry, *loc_cpu_old_entry;
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
return -EFAULT;
@@ -1130,13 +1188,13 @@ do_replace(void __user *user, unsigned int len)
if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
return -ENOMEM;
- newinfo = vmalloc(sizeof(struct ipt_table_info)
- + SMP_ALIGN(tmp.size) *
- (highest_possible_processor_id()+1));
+ newinfo = alloc_table_info(tmp.size);
if (!newinfo)
return -ENOMEM;
- if (copy_from_user(newinfo->entries, user + sizeof(tmp),
+ /* choose the copy that is our node/cpu */
+ loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
+ if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
tmp.size) != 0) {
ret = -EFAULT;
goto free_newinfo;
@@ -1147,10 +1205,9 @@ do_replace(void __user *user, unsigned int len)
ret = -ENOMEM;
goto free_newinfo;
}
- memset(counters, 0, tmp.num_counters * sizeof(struct ipt_counters));
ret = translate_table(tmp.name, tmp.valid_hooks,
- newinfo, tmp.size, tmp.num_entries,
+ newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
tmp.hook_entry, tmp.underflow);
if (ret != 0)
goto free_newinfo_counters;
@@ -1189,8 +1246,9 @@ do_replace(void __user *user, unsigned int len)
/* Get the old counters. */
get_counters(oldinfo, counters);
/* Decrease module usage counts and free resource */
- IPT_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
- vfree(oldinfo);
+ loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
+ IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
+ free_table_info(oldinfo);
if (copy_to_user(tmp.counters, counters,
sizeof(struct ipt_counters) * tmp.num_counters) != 0)
ret = -EFAULT;
@@ -1202,11 +1260,11 @@ do_replace(void __user *user, unsigned int len)
module_put(t->me);
up(&ipt_mutex);
free_newinfo_counters_untrans:
- IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
+ IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
free_newinfo_counters:
vfree(counters);
free_newinfo:
- vfree(newinfo);
+ free_table_info(newinfo);
return ret;
}
@@ -1239,6 +1297,7 @@ do_add_counters(void __user *user, unsigned int len)
struct ipt_counters_info tmp, *paddc;
struct ipt_table *t;
int ret = 0;
+ void *loc_cpu_entry;
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
return -EFAULT;
@@ -1246,7 +1305,7 @@ do_add_counters(void __user *user, unsigned int len)
if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ipt_counters))
return -EINVAL;
- paddc = vmalloc(len);
+ paddc = vmalloc_node(len, numa_node_id());
if (!paddc)
return -ENOMEM;
@@ -1268,7 +1327,9 @@ do_add_counters(void __user *user, unsigned int len)
}
i = 0;
- IPT_ENTRY_ITERATE(t->private->entries,
+ /* Choose the copy that is on our node */
+ loc_cpu_entry = t->private->entries[raw_smp_processor_id()];
+ IPT_ENTRY_ITERATE(loc_cpu_entry,
t->private->size,
add_counter_to_entry,
paddc->counters,
@@ -1460,28 +1521,31 @@ int ipt_register_table(struct ipt_table *table, const struct ipt_replace *repl)
struct ipt_table_info *newinfo;
static struct ipt_table_info bootstrap
= { 0, 0, 0, { 0 }, { 0 }, { } };
+ void *loc_cpu_entry;
- newinfo = vmalloc(sizeof(struct ipt_table_info)
- + SMP_ALIGN(repl->size) *
- (highest_possible_processor_id()+1));
+ newinfo = alloc_table_info(repl->size);
if (!newinfo)
return -ENOMEM;
- memcpy(newinfo->entries, repl->entries, repl->size);
+ /* choose the copy on our node/cpu
+ * but dont care of preemption
+ */
+ loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
+ memcpy(loc_cpu_entry, repl->entries, repl->size);
ret = translate_table(table->name, table->valid_hooks,
- newinfo, repl->size,
+ newinfo, loc_cpu_entry, repl->size,
repl->num_entries,
repl->hook_entry,
repl->underflow);
if (ret != 0) {
- vfree(newinfo);
+ free_table_info(newinfo);
return ret;
}
ret = down_interruptible(&ipt_mutex);
if (ret != 0) {
- vfree(newinfo);
+ free_table_info(newinfo);
return ret;
}
@@ -1510,20 +1574,23 @@ int ipt_register_table(struct ipt_table *table, const struct ipt_replace *repl)
return ret;
free_unlock:
- vfree(newinfo);
+ free_table_info(newinfo);
goto unlock;
}
void ipt_unregister_table(struct ipt_table *table)
{
+ void *loc_cpu_entry;
+
down(&ipt_mutex);
LIST_DELETE(&ipt_tables, table);
up(&ipt_mutex);
/* Decrease module usage counts and free resources */
- IPT_ENTRY_ITERATE(table->private->entries, table->private->size,
+ loc_cpu_entry = table->private->entries[raw_smp_processor_id()];
+ IPT_ENTRY_ITERATE(loc_cpu_entry, table->private->size,
cleanup_entry, NULL);
- vfree(table->private);
+ free_table_info(table->private);
}
/* Returns 1 if the port is matched by the range, 0 otherwise */
diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c
index 275a174c6fe6..27860510ca6d 100644
--- a/net/ipv4/netfilter/ipt_MASQUERADE.c
+++ b/net/ipv4/netfilter/ipt_MASQUERADE.c
@@ -11,6 +11,7 @@
#include <linux/config.h>
#include <linux/types.h>
+#include <linux/inetdevice.h>
#include <linux/ip.h>
#include <linux/timer.h>
#include <linux/module.h>
@@ -18,6 +19,7 @@
#include <net/protocol.h>
#include <net/ip.h>
#include <net/checksum.h>
+#include <net/route.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv4/ip_nat_rule.h>
#include <linux/netfilter_ipv4/ip_tables.h>
diff --git a/net/ipv4/netfilter/ipt_physdev.c b/net/ipv4/netfilter/ipt_physdev.c
index 1a53924041fc..03f554857a4d 100644
--- a/net/ipv4/netfilter/ipt_physdev.c
+++ b/net/ipv4/netfilter/ipt_physdev.c
@@ -9,6 +9,7 @@
*/
#include <linux/module.h>
+#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/netfilter_ipv4/ipt_physdev.h>
#include <linux/netfilter_ipv4/ip_tables.h>
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index 0d7dc668db46..39d49dc333a7 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -38,6 +38,7 @@
#include <net/protocol.h>
#include <net/tcp.h>
#include <net/udp.h>
+#include <linux/inetdevice.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <net/sock.h>
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index a34e60ea48a1..e20be3331f67 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -173,10 +173,10 @@ static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct dst_entry *dst)
{
- struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
struct sock *child;
- child = tp->af_specific->syn_recv_sock(sk, skb, req, dst);
+ child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst);
if (child)
inet_csk_reqsk_queue_add(sk, req, child);
else
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 01444a02b48b..16984d4a8a06 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -12,6 +12,7 @@
#include <linux/sysctl.h>
#include <linux/config.h>
#include <linux/igmp.h>
+#include <linux/inetdevice.h>
#include <net/snmp.h>
#include <net/icmp.h>
#include <net/ip.h>
@@ -22,6 +23,7 @@
extern int sysctl_ip_nonlocal_bind;
#ifdef CONFIG_SYSCTL
+static int zero;
static int tcp_retr1_max = 255;
static int ip_local_port_range_min[] = { 1, 1 };
static int ip_local_port_range_max[] = { 65535, 65535 };
@@ -614,6 +616,15 @@ ctl_table ipv4_table[] = {
.strategy = &sysctl_jiffies
},
{
+ .ctl_name = NET_IPV4_IPFRAG_MAX_DIST,
+ .procname = "ipfrag_max_dist",
+ .data = &sysctl_ipfrag_max_dist,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = &zero
+ },
+ {
.ctl_name = NET_TCP_NO_METRICS_SAVE,
.procname = "tcp_no_metrics_save",
.data = &sysctl_tcp_nometrics_save,
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index ef98b14ac56d..00aa80e93243 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1696,8 +1696,8 @@ int tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
int err = 0;
if (level != SOL_TCP)
- return tp->af_specific->setsockopt(sk, level, optname,
- optval, optlen);
+ return icsk->icsk_af_ops->setsockopt(sk, level, optname,
+ optval, optlen);
/* This is a string value all the others are int's */
if (optname == TCP_CONGESTION) {
@@ -1914,7 +1914,7 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info)
info->tcpi_last_data_recv = jiffies_to_msecs(now - icsk->icsk_ack.lrcvtime);
info->tcpi_last_ack_recv = jiffies_to_msecs(now - tp->rcv_tstamp);
- info->tcpi_pmtu = tp->pmtu_cookie;
+ info->tcpi_pmtu = icsk->icsk_pmtu_cookie;
info->tcpi_rcv_ssthresh = tp->rcv_ssthresh;
info->tcpi_rtt = jiffies_to_usecs(tp->srtt)>>3;
info->tcpi_rttvar = jiffies_to_usecs(tp->mdev)>>2;
@@ -1939,8 +1939,8 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
int val, len;
if (level != SOL_TCP)
- return tp->af_specific->getsockopt(sk, level, optname,
- optval, optlen);
+ return icsk->icsk_af_ops->getsockopt(sk, level, optname,
+ optval, optlen);
if (get_user(len, optlen))
return -EFAULT;
diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c
index 1d0cd86621b1..035f2092d73a 100644
--- a/net/ipv4/tcp_bic.c
+++ b/net/ipv4/tcp_bic.c
@@ -30,8 +30,6 @@ static int fast_convergence = 1;
static int max_increment = 16;
static int low_window = 14;
static int beta = 819; /* = 819/1024 (BICTCP_BETA_SCALE) */
-static int low_utilization_threshold = 153;
-static int low_utilization_period = 2;
static int initial_ssthresh = 100;
static int smooth_part = 20;
@@ -43,10 +41,6 @@ module_param(low_window, int, 0644);
MODULE_PARM_DESC(low_window, "lower bound on congestion window (for TCP friendliness)");
module_param(beta, int, 0644);
MODULE_PARM_DESC(beta, "beta for multiplicative increase");
-module_param(low_utilization_threshold, int, 0644);
-MODULE_PARM_DESC(low_utilization_threshold, "percent (scaled by 1024) for low utilization mode");
-module_param(low_utilization_period, int, 0644);
-MODULE_PARM_DESC(low_utilization_period, "if average delay exceeds then goto to low utilization mode (seconds)");
module_param(initial_ssthresh, int, 0644);
MODULE_PARM_DESC(initial_ssthresh, "initial value of slow start threshold");
module_param(smooth_part, int, 0644);
@@ -60,11 +54,6 @@ struct bictcp {
u32 loss_cwnd; /* congestion window at last loss */
u32 last_cwnd; /* the last snd_cwnd */
u32 last_time; /* time when updated last_cwnd */
- u32 delay_min; /* min delay */
- u32 delay_max; /* max delay */
- u32 last_delay;
- u8 low_utilization;/* 0: high; 1: low */
- u32 low_utilization_start; /* starting time of low utilization detection*/
u32 epoch_start; /* beginning of an epoch */
#define ACK_RATIO_SHIFT 4
u32 delayed_ack; /* estimate the ratio of Packets/ACKs << 4 */
@@ -77,11 +66,6 @@ static inline void bictcp_reset(struct bictcp *ca)
ca->loss_cwnd = 0;
ca->last_cwnd = 0;
ca->last_time = 0;
- ca->delay_min = 0;
- ca->delay_max = 0;
- ca->last_delay = 0;
- ca->low_utilization = 0;
- ca->low_utilization_start = 0;
ca->epoch_start = 0;
ca->delayed_ack = 2 << ACK_RATIO_SHIFT;
}
@@ -143,8 +127,7 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd)
}
/* if in slow start or link utilization is very low */
- if ( ca->loss_cwnd == 0 ||
- (cwnd > ca->loss_cwnd && ca->low_utilization)) {
+ if (ca->loss_cwnd == 0) {
if (ca->cnt > 20) /* increase cwnd 5% per RTT */
ca->cnt = 20;
}
@@ -154,69 +137,12 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd)
ca->cnt = 1;
}
-
-/* Detect low utilization in congestion avoidance */
-static inline void bictcp_low_utilization(struct sock *sk, int flag)
-{
- const struct tcp_sock *tp = tcp_sk(sk);
- struct bictcp *ca = inet_csk_ca(sk);
- u32 dist, delay;
-
- /* No time stamp */
- if (!(tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) ||
- /* Discard delay samples right after fast recovery */
- tcp_time_stamp < ca->epoch_start + HZ ||
- /* this delay samples may not be accurate */
- flag == 0) {
- ca->last_delay = 0;
- goto notlow;
- }
-
- delay = ca->last_delay<<3; /* use the same scale as tp->srtt*/
- ca->last_delay = tcp_time_stamp - tp->rx_opt.rcv_tsecr;
- if (delay == 0) /* no previous delay sample */
- goto notlow;
-
- /* first time call or link delay decreases */
- if (ca->delay_min == 0 || ca->delay_min > delay) {
- ca->delay_min = ca->delay_max = delay;
- goto notlow;
- }
-
- if (ca->delay_max < delay)
- ca->delay_max = delay;
-
- /* utilization is low, if avg delay < dist*threshold
- for checking_period time */
- dist = ca->delay_max - ca->delay_min;
- if (dist <= ca->delay_min>>6 ||
- tp->srtt - ca->delay_min >= (dist*low_utilization_threshold)>>10)
- goto notlow;
-
- if (ca->low_utilization_start == 0) {
- ca->low_utilization = 0;
- ca->low_utilization_start = tcp_time_stamp;
- } else if ((s32)(tcp_time_stamp - ca->low_utilization_start)
- > low_utilization_period*HZ) {
- ca->low_utilization = 1;
- }
-
- return;
-
- notlow:
- ca->low_utilization = 0;
- ca->low_utilization_start = 0;
-
-}
-
static void bictcp_cong_avoid(struct sock *sk, u32 ack,
u32 seq_rtt, u32 in_flight, int data_acked)
{
struct tcp_sock *tp = tcp_sk(sk);
struct bictcp *ca = inet_csk_ca(sk);
- bictcp_low_utilization(sk, data_acked);
-
if (!tcp_is_cwnd_limited(sk, in_flight))
return;
@@ -249,11 +175,6 @@ static u32 bictcp_recalc_ssthresh(struct sock *sk)
ca->epoch_start = 0; /* end of epoch */
- /* in case of wrong delay_max*/
- if (ca->delay_min > 0 && ca->delay_max > ca->delay_min)
- ca->delay_max = ca->delay_min
- + ((ca->delay_max - ca->delay_min)* 90) / 100;
-
/* Wmax and fast convergence */
if (tp->snd_cwnd < ca->last_max_cwnd && fast_convergence)
ca->last_max_cwnd = (tp->snd_cwnd * (BICTCP_BETA_SCALE + beta))
@@ -289,14 +210,14 @@ static void bictcp_state(struct sock *sk, u8 new_state)
bictcp_reset(inet_csk_ca(sk));
}
-/* Track delayed acknowledgement ratio using sliding window
+/* Track delayed acknowledgment ratio using sliding window
* ratio = (15*ratio + sample) / 16
*/
static void bictcp_acked(struct sock *sk, u32 cnt)
{
const struct inet_connection_sock *icsk = inet_csk(sk);
- if (cnt > 0 && icsk->icsk_ca_state == TCP_CA_Open) {
+ if (cnt > 0 && icsk->icsk_ca_state == TCP_CA_Open) {
struct bictcp *ca = inet_csk_ca(sk);
cnt -= ca->delayed_ack >> ACK_RATIO_SHIFT;
ca->delayed_ack += cnt;
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index c7cc62c8dc12..e688c687d62d 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -174,6 +174,34 @@ int tcp_set_congestion_control(struct sock *sk, const char *name)
return err;
}
+
+/*
+ * Linear increase during slow start
+ */
+void tcp_slow_start(struct tcp_sock *tp)
+{
+ if (sysctl_tcp_abc) {
+ /* RFC3465: Slow Start
+ * TCP sender SHOULD increase cwnd by the number of
+ * previously unacknowledged bytes ACKed by each incoming
+ * acknowledgment, provided the increase is not more than L
+ */
+ if (tp->bytes_acked < tp->mss_cache)
+ return;
+
+ /* We MAY increase by 2 if discovered delayed ack */
+ if (sysctl_tcp_abc > 1 && tp->bytes_acked > 2*tp->mss_cache) {
+ if (tp->snd_cwnd < tp->snd_cwnd_clamp)
+ tp->snd_cwnd++;
+ }
+ }
+ tp->bytes_acked = 0;
+
+ if (tp->snd_cwnd < tp->snd_cwnd_clamp)
+ tp->snd_cwnd++;
+}
+EXPORT_SYMBOL_GPL(tcp_slow_start);
+
/*
* TCP Reno congestion control
* This is special case used for fallback as well.
diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c
new file mode 100644
index 000000000000..31a4986dfbf7
--- /dev/null
+++ b/net/ipv4/tcp_cubic.c
@@ -0,0 +1,411 @@
+/*
+ * TCP CUBIC: Binary Increase Congestion control for TCP v2.0
+ *
+ * This is from the implementation of CUBIC TCP in
+ * Injong Rhee, Lisong Xu.
+ * "CUBIC: A New TCP-Friendly High-Speed TCP Variant
+ * in PFLDnet 2005
+ * Available from:
+ * http://www.csc.ncsu.edu/faculty/rhee/export/bitcp/cubic-paper.pdf
+ *
+ * Unless CUBIC is enabled and congestion window is large
+ * this behaves the same as the original Reno.
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <net/tcp.h>
+#include <asm/div64.h>
+
+#define BICTCP_BETA_SCALE 1024 /* Scale factor beta calculation
+ * max_cwnd = snd_cwnd * beta
+ */
+#define BICTCP_B 4 /*
+ * In binary search,
+ * go to point (max+min)/N
+ */
+#define BICTCP_HZ 10 /* BIC HZ 2^10 = 1024 */
+
+static int fast_convergence = 1;
+static int max_increment = 16;
+static int beta = 819; /* = 819/1024 (BICTCP_BETA_SCALE) */
+static int initial_ssthresh = 100;
+static int bic_scale = 41;
+static int tcp_friendliness = 1;
+
+static u32 cube_rtt_scale;
+static u32 beta_scale;
+static u64 cube_factor;
+
+/* Note parameters that are used for precomputing scale factors are read-only */
+module_param(fast_convergence, int, 0644);
+MODULE_PARM_DESC(fast_convergence, "turn on/off fast convergence");
+module_param(max_increment, int, 0644);
+MODULE_PARM_DESC(max_increment, "Limit on increment allowed during binary search");
+module_param(beta, int, 0444);
+MODULE_PARM_DESC(beta, "beta for multiplicative increase");
+module_param(initial_ssthresh, int, 0644);
+MODULE_PARM_DESC(initial_ssthresh, "initial value of slow start threshold");
+module_param(bic_scale, int, 0444);
+MODULE_PARM_DESC(bic_scale, "scale (scaled by 1024) value for bic function (bic_scale/1024)");
+module_param(tcp_friendliness, int, 0644);
+MODULE_PARM_DESC(tcp_friendliness, "turn on/off tcp friendliness");
+
+#include <asm/div64.h>
+
+/* BIC TCP Parameters */
+struct bictcp {
+ u32 cnt; /* increase cwnd by 1 after ACKs */
+ u32 last_max_cwnd; /* last maximum snd_cwnd */
+ u32 loss_cwnd; /* congestion window at last loss */
+ u32 last_cwnd; /* the last snd_cwnd */
+ u32 last_time; /* time when updated last_cwnd */
+ u32 bic_origin_point;/* origin point of bic function */
+ u32 bic_K; /* time to origin point from the beginning of the current epoch */
+ u32 delay_min; /* min delay */
+ u32 epoch_start; /* beginning of an epoch */
+ u32 ack_cnt; /* number of acks */
+ u32 tcp_cwnd; /* estimated tcp cwnd */
+#define ACK_RATIO_SHIFT 4
+ u32 delayed_ack; /* estimate the ratio of Packets/ACKs << 4 */
+};
+
+static inline void bictcp_reset(struct bictcp *ca)
+{
+ ca->cnt = 0;
+ ca->last_max_cwnd = 0;
+ ca->loss_cwnd = 0;
+ ca->last_cwnd = 0;
+ ca->last_time = 0;
+ ca->bic_origin_point = 0;
+ ca->bic_K = 0;
+ ca->delay_min = 0;
+ ca->epoch_start = 0;
+ ca->delayed_ack = 2 << ACK_RATIO_SHIFT;
+ ca->ack_cnt = 0;
+ ca->tcp_cwnd = 0;
+}
+
+static void bictcp_init(struct sock *sk)
+{
+ bictcp_reset(inet_csk_ca(sk));
+ if (initial_ssthresh)
+ tcp_sk(sk)->snd_ssthresh = initial_ssthresh;
+}
+
+/* 64bit divisor, dividend and result. dynamic precision */
+static inline u_int64_t div64_64(u_int64_t dividend, u_int64_t divisor)
+{
+ u_int32_t d = divisor;
+
+ if (divisor > 0xffffffffULL) {
+ unsigned int shift = fls(divisor >> 32);
+
+ d = divisor >> shift;
+ dividend >>= shift;
+ }
+
+ /* avoid 64 bit division if possible */
+ if (dividend >> 32)
+ do_div(dividend, d);
+ else
+ dividend = (uint32_t) dividend / d;
+
+ return dividend;
+}
+
+/*
+ * calculate the cubic root of x using Newton-Raphson
+ */
+static u32 cubic_root(u64 a)
+{
+ u32 x, x1;
+
+ /* Initial estimate is based on:
+ * cbrt(x) = exp(log(x) / 3)
+ */
+ x = 1u << (fls64(a)/3);
+
+ /*
+ * Iteration based on:
+ * 2
+ * x = ( 2 * x + a / x ) / 3
+ * k+1 k k
+ */
+ do {
+ x1 = x;
+ x = (2 * x + (uint32_t) div64_64(a, x*x)) / 3;
+ } while (abs(x1 - x) > 1);
+
+ return x;
+}
+
+/*
+ * Compute congestion window to use.
+ */
+static inline void bictcp_update(struct bictcp *ca, u32 cwnd)
+{
+ u64 offs;
+ u32 delta, t, bic_target, min_cnt, max_cnt;
+
+ ca->ack_cnt++; /* count the number of ACKs */
+
+ if (ca->last_cwnd == cwnd &&
+ (s32)(tcp_time_stamp - ca->last_time) <= HZ / 32)
+ return;
+
+ ca->last_cwnd = cwnd;
+ ca->last_time = tcp_time_stamp;
+
+ if (ca->epoch_start == 0) {
+ ca->epoch_start = tcp_time_stamp; /* record the beginning of an epoch */
+ ca->ack_cnt = 1; /* start counting */
+ ca->tcp_cwnd = cwnd; /* syn with cubic */
+
+ if (ca->last_max_cwnd <= cwnd) {
+ ca->bic_K = 0;
+ ca->bic_origin_point = cwnd;
+ } else {
+ /* Compute new K based on
+ * (wmax-cwnd) * (srtt>>3 / HZ) / c * 2^(3*bictcp_HZ)
+ */
+ ca->bic_K = cubic_root(cube_factor
+ * (ca->last_max_cwnd - cwnd));
+ ca->bic_origin_point = ca->last_max_cwnd;
+ }
+ }
+
+ /* cubic function - calc*/
+ /* calculate c * time^3 / rtt,
+ * while considering overflow in calculation of time^3
+ * (so time^3 is done by using 64 bit)
+ * and without the support of division of 64bit numbers
+ * (so all divisions are done by using 32 bit)
+ * also NOTE the unit of those veriables
+ * time = (t - K) / 2^bictcp_HZ
+ * c = bic_scale >> 10
+ * rtt = (srtt >> 3) / HZ
+ * !!! The following code does not have overflow problems,
+ * if the cwnd < 1 million packets !!!
+ */
+
+ /* change the unit from HZ to bictcp_HZ */
+ t = ((tcp_time_stamp + ca->delay_min - ca->epoch_start)
+ << BICTCP_HZ) / HZ;
+
+ if (t < ca->bic_K) /* t - K */
+ offs = ca->bic_K - t;
+ else
+ offs = t - ca->bic_K;
+
+ /* c/rtt * (t-K)^3 */
+ delta = (cube_rtt_scale * offs * offs * offs) >> (10+3*BICTCP_HZ);
+ if (t < ca->bic_K) /* below origin*/
+ bic_target = ca->bic_origin_point - delta;
+ else /* above origin*/
+ bic_target = ca->bic_origin_point + delta;
+
+ /* cubic function - calc bictcp_cnt*/
+ if (bic_target > cwnd) {
+ ca->cnt = cwnd / (bic_target - cwnd);
+ } else {
+ ca->cnt = 100 * cwnd; /* very small increment*/
+ }
+
+ if (ca->delay_min > 0) {
+ /* max increment = Smax * rtt / 0.1 */
+ min_cnt = (cwnd * HZ * 8)/(10 * max_increment * ca->delay_min);
+ if (ca->cnt < min_cnt)
+ ca->cnt = min_cnt;
+ }
+
+ /* slow start and low utilization */
+ if (ca->loss_cwnd == 0) /* could be aggressive in slow start */
+ ca->cnt = 50;
+
+ /* TCP Friendly */
+ if (tcp_friendliness) {
+ u32 scale = beta_scale;
+ delta = (cwnd * scale) >> 3;
+ while (ca->ack_cnt > delta) { /* update tcp cwnd */
+ ca->ack_cnt -= delta;
+ ca->tcp_cwnd++;
+ }
+
+ if (ca->tcp_cwnd > cwnd){ /* if bic is slower than tcp */
+ delta = ca->tcp_cwnd - cwnd;
+ max_cnt = cwnd / delta;
+ if (ca->cnt > max_cnt)
+ ca->cnt = max_cnt;
+ }
+ }
+
+ ca->cnt = (ca->cnt << ACK_RATIO_SHIFT) / ca->delayed_ack;
+ if (ca->cnt == 0) /* cannot be zero */
+ ca->cnt = 1;
+}
+
+
+/* Keep track of minimum rtt */
+static inline void measure_delay(struct sock *sk)
+{
+ const struct tcp_sock *tp = tcp_sk(sk);
+ struct bictcp *ca = inet_csk_ca(sk);
+ u32 delay;
+
+ /* No time stamp */
+ if (!(tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) ||
+ /* Discard delay samples right after fast recovery */
+ (s32)(tcp_time_stamp - ca->epoch_start) < HZ)
+ return;
+
+ delay = tcp_time_stamp - tp->rx_opt.rcv_tsecr;
+ if (delay == 0)
+ delay = 1;
+
+ /* first time call or link delay decreases */
+ if (ca->delay_min == 0 || ca->delay_min > delay)
+ ca->delay_min = delay;
+}
+
+static void bictcp_cong_avoid(struct sock *sk, u32 ack,
+ u32 seq_rtt, u32 in_flight, int data_acked)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct bictcp *ca = inet_csk_ca(sk);
+
+ if (data_acked)
+ measure_delay(sk);
+
+ if (!tcp_is_cwnd_limited(sk, in_flight))
+ return;
+
+ if (tp->snd_cwnd <= tp->snd_ssthresh)
+ tcp_slow_start(tp);
+ else {
+ bictcp_update(ca, tp->snd_cwnd);
+
+ /* In dangerous area, increase slowly.
+ * In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd
+ */
+ if (tp->snd_cwnd_cnt >= ca->cnt) {
+ if (tp->snd_cwnd < tp->snd_cwnd_clamp)
+ tp->snd_cwnd++;
+ tp->snd_cwnd_cnt = 0;
+ } else
+ tp->snd_cwnd_cnt++;
+ }
+
+}
+
+static u32 bictcp_recalc_ssthresh(struct sock *sk)
+{
+ const struct tcp_sock *tp = tcp_sk(sk);
+ struct bictcp *ca = inet_csk_ca(sk);
+
+ ca->epoch_start = 0; /* end of epoch */
+
+ /* Wmax and fast convergence */
+ if (tp->snd_cwnd < ca->last_max_cwnd && fast_convergence)
+ ca->last_max_cwnd = (tp->snd_cwnd * (BICTCP_BETA_SCALE + beta))
+ / (2 * BICTCP_BETA_SCALE);
+ else
+ ca->last_max_cwnd = tp->snd_cwnd;
+
+ ca->loss_cwnd = tp->snd_cwnd;
+
+ return max((tp->snd_cwnd * beta) / BICTCP_BETA_SCALE, 2U);
+}
+
+static u32 bictcp_undo_cwnd(struct sock *sk)
+{
+ struct bictcp *ca = inet_csk_ca(sk);
+
+ return max(tcp_sk(sk)->snd_cwnd, ca->last_max_cwnd);
+}
+
+static u32 bictcp_min_cwnd(struct sock *sk)
+{
+ return tcp_sk(sk)->snd_ssthresh;
+}
+
+static void bictcp_state(struct sock *sk, u8 new_state)
+{
+ if (new_state == TCP_CA_Loss)
+ bictcp_reset(inet_csk_ca(sk));
+}
+
+/* Track delayed acknowledgment ratio using sliding window
+ * ratio = (15*ratio + sample) / 16
+ */
+static void bictcp_acked(struct sock *sk, u32 cnt)
+{
+ const struct inet_connection_sock *icsk = inet_csk(sk);
+
+ if (cnt > 0 && icsk->icsk_ca_state == TCP_CA_Open) {
+ struct bictcp *ca = inet_csk_ca(sk);
+ cnt -= ca->delayed_ack >> ACK_RATIO_SHIFT;
+ ca->delayed_ack += cnt;
+ }
+}
+
+
+static struct tcp_congestion_ops cubictcp = {
+ .init = bictcp_init,
+ .ssthresh = bictcp_recalc_ssthresh,
+ .cong_avoid = bictcp_cong_avoid,
+ .set_state = bictcp_state,
+ .undo_cwnd = bictcp_undo_cwnd,
+ .min_cwnd = bictcp_min_cwnd,
+ .pkts_acked = bictcp_acked,
+ .owner = THIS_MODULE,
+ .name = "cubic",
+};
+
+static int __init cubictcp_register(void)
+{
+ BUG_ON(sizeof(struct bictcp) > ICSK_CA_PRIV_SIZE);
+
+ /* Precompute a bunch of the scaling factors that are used per-packet
+ * based on SRTT of 100ms
+ */
+
+ beta_scale = 8*(BICTCP_BETA_SCALE+beta)/ 3 / (BICTCP_BETA_SCALE - beta);
+
+ cube_rtt_scale = (bic_scale << 3) / 10; /* 1024*c/rtt */
+
+ /* calculate the "K" for (wmax-cwnd) = c/rtt * K^3
+ * so K = cubic_root( (wmax-cwnd)*rtt/c )
+ * the unit of K is bictcp_HZ=2^10, not HZ
+ *
+ * c = bic_scale >> 10
+ * rtt = 100ms
+ *
+ * the following code has been designed and tested for
+ * cwnd < 1 million packets
+ * RTT < 100 seconds
+ * HZ < 1,000,00 (corresponding to 10 nano-second)
+ */
+
+ /* 1/c * 2^2*bictcp_HZ * srtt */
+ cube_factor = 1ull << (10+3*BICTCP_HZ); /* 2^40 */
+
+ /* divide by bic_scale and by constant Srtt (100ms) */
+ do_div(cube_factor, bic_scale * 10);
+
+ return tcp_register_congestion_control(&cubictcp);
+}
+
+static void __exit cubictcp_unregister(void)
+{
+ tcp_unregister_congestion_control(&cubictcp);
+}
+
+module_init(cubictcp_register);
+module_exit(cubictcp_unregister);
+
+MODULE_AUTHOR("Sangtae Ha, Stephen Hemminger");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CUBIC TCP");
+MODULE_VERSION("2.0");
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index bf2e23086bce..0a461232329f 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -115,8 +115,8 @@ int sysctl_tcp_abc = 1;
/* Adapt the MSS value used to make delayed ack decision to the
* real world.
*/
-static inline void tcp_measure_rcv_mss(struct sock *sk,
- const struct sk_buff *skb)
+static void tcp_measure_rcv_mss(struct sock *sk,
+ const struct sk_buff *skb)
{
struct inet_connection_sock *icsk = inet_csk(sk);
const unsigned int lss = icsk->icsk_ack.last_seg_size;
@@ -246,8 +246,8 @@ static int __tcp_grow_window(const struct sock *sk, struct tcp_sock *tp,
return 0;
}
-static inline void tcp_grow_window(struct sock *sk, struct tcp_sock *tp,
- struct sk_buff *skb)
+static void tcp_grow_window(struct sock *sk, struct tcp_sock *tp,
+ struct sk_buff *skb)
{
/* Check #1 */
if (tp->rcv_ssthresh < tp->window_clamp &&
@@ -341,6 +341,26 @@ static void tcp_clamp_window(struct sock *sk, struct tcp_sock *tp)
tp->rcv_ssthresh = min(tp->window_clamp, 2U*tp->advmss);
}
+
+/* Initialize RCV_MSS value.
+ * RCV_MSS is an our guess about MSS used by the peer.
+ * We haven't any direct information about the MSS.
+ * It's better to underestimate the RCV_MSS rather than overestimate.
+ * Overestimations make us ACKing less frequently than needed.
+ * Underestimations are more easy to detect and fix by tcp_measure_rcv_mss().
+ */
+void tcp_initialize_rcv_mss(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ unsigned int hint = min_t(unsigned int, tp->advmss, tp->mss_cache);
+
+ hint = min(hint, tp->rcv_wnd/2);
+ hint = min(hint, TCP_MIN_RCVMSS);
+ hint = max(hint, TCP_MIN_MSS);
+
+ inet_csk(sk)->icsk_ack.rcv_mss = hint;
+}
+
/* Receiver "autotuning" code.
*
* The algorithm for RTT estimation w/o timestamps is based on
@@ -735,6 +755,27 @@ __u32 tcp_init_cwnd(struct tcp_sock *tp, struct dst_entry *dst)
return min_t(__u32, cwnd, tp->snd_cwnd_clamp);
}
+/* Set slow start threshold and cwnd not falling to slow start */
+void tcp_enter_cwr(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ tp->prior_ssthresh = 0;
+ tp->bytes_acked = 0;
+ if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) {
+ tp->undo_marker = 0;
+ tp->snd_ssthresh = inet_csk(sk)->icsk_ca_ops->ssthresh(sk);
+ tp->snd_cwnd = min(tp->snd_cwnd,
+ tcp_packets_in_flight(tp) + 1U);
+ tp->snd_cwnd_cnt = 0;
+ tp->high_seq = tp->snd_nxt;
+ tp->snd_cwnd_stamp = tcp_time_stamp;
+ TCP_ECN_queue_cwr(tp);
+
+ tcp_set_ca_state(sk, TCP_CA_CWR);
+ }
+}
+
/* Initialize metrics on socket. */
static void tcp_init_metrics(struct sock *sk)
@@ -2070,8 +2111,8 @@ static inline void tcp_ack_update_rtt(struct sock *sk, const int flag,
tcp_ack_no_tstamp(sk, seq_rtt, flag);
}
-static inline void tcp_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
- u32 in_flight, int good)
+static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
+ u32 in_flight, int good)
{
const struct inet_connection_sock *icsk = inet_csk(sk);
icsk->icsk_ca_ops->cong_avoid(sk, ack, rtt, in_flight, good);
@@ -2082,7 +2123,7 @@ static inline void tcp_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
* RFC2988 recommends to restart timer to now+rto.
*/
-static inline void tcp_ack_packets_out(struct sock *sk, struct tcp_sock *tp)
+static void tcp_ack_packets_out(struct sock *sk, struct tcp_sock *tp)
{
if (!tp->packets_out) {
inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
@@ -2147,7 +2188,7 @@ static int tcp_tso_acked(struct sock *sk, struct sk_buff *skb,
return acked;
}
-static inline u32 tcp_usrtt(const struct sk_buff *skb)
+static u32 tcp_usrtt(const struct sk_buff *skb)
{
struct timeval tv, now;
@@ -2342,7 +2383,7 @@ static int tcp_ack_update_window(struct sock *sk, struct tcp_sock *tp,
if (nwin > tp->max_window) {
tp->max_window = nwin;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ tcp_sync_mss(sk, inet_csk(sk)->icsk_pmtu_cookie);
}
}
}
@@ -2583,8 +2624,8 @@ void tcp_parse_options(struct sk_buff *skb, struct tcp_options_received *opt_rx,
/* Fast parse options. This hopes to only see timestamps.
* If it is wrong it falls back on tcp_parse_options().
*/
-static inline int tcp_fast_parse_options(struct sk_buff *skb, struct tcphdr *th,
- struct tcp_sock *tp)
+static int tcp_fast_parse_options(struct sk_buff *skb, struct tcphdr *th,
+ struct tcp_sock *tp)
{
if (th->doff == sizeof(struct tcphdr)>>2) {
tp->rx_opt.saw_tstamp = 0;
@@ -2804,8 +2845,7 @@ static void tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th)
}
}
-static __inline__ int
-tcp_sack_extend(struct tcp_sack_block *sp, u32 seq, u32 end_seq)
+static inline int tcp_sack_extend(struct tcp_sack_block *sp, u32 seq, u32 end_seq)
{
if (!after(seq, sp->end_seq) && !after(sp->start_seq, end_seq)) {
if (before(seq, sp->start_seq))
@@ -2817,7 +2857,7 @@ tcp_sack_extend(struct tcp_sack_block *sp, u32 seq, u32 end_seq)
return 0;
}
-static inline void tcp_dsack_set(struct tcp_sock *tp, u32 seq, u32 end_seq)
+static void tcp_dsack_set(struct tcp_sock *tp, u32 seq, u32 end_seq)
{
if (tp->rx_opt.sack_ok && sysctl_tcp_dsack) {
if (before(seq, tp->rcv_nxt))
@@ -2832,7 +2872,7 @@ static inline void tcp_dsack_set(struct tcp_sock *tp, u32 seq, u32 end_seq)
}
}
-static inline void tcp_dsack_extend(struct tcp_sock *tp, u32 seq, u32 end_seq)
+static void tcp_dsack_extend(struct tcp_sock *tp, u32 seq, u32 end_seq)
{
if (!tp->rx_opt.dsack)
tcp_dsack_set(tp, seq, end_seq);
@@ -2890,7 +2930,7 @@ static void tcp_sack_maybe_coalesce(struct tcp_sock *tp)
}
}
-static __inline__ void tcp_sack_swap(struct tcp_sack_block *sack1, struct tcp_sack_block *sack2)
+static inline void tcp_sack_swap(struct tcp_sack_block *sack1, struct tcp_sack_block *sack2)
{
__u32 tmp;
@@ -3455,7 +3495,7 @@ void tcp_cwnd_application_limited(struct sock *sk)
tp->snd_cwnd_stamp = tcp_time_stamp;
}
-static inline int tcp_should_expand_sndbuf(struct sock *sk, struct tcp_sock *tp)
+static int tcp_should_expand_sndbuf(struct sock *sk, struct tcp_sock *tp)
{
/* If the user specified a specific send buffer setting, do
* not modify it.
@@ -3502,7 +3542,7 @@ static void tcp_new_space(struct sock *sk)
sk->sk_write_space(sk);
}
-static inline void tcp_check_space(struct sock *sk)
+static void tcp_check_space(struct sock *sk)
{
if (sock_flag(sk, SOCK_QUEUE_SHRUNK)) {
sock_reset_flag(sk, SOCK_QUEUE_SHRUNK);
@@ -3512,7 +3552,7 @@ static inline void tcp_check_space(struct sock *sk)
}
}
-static __inline__ void tcp_data_snd_check(struct sock *sk, struct tcp_sock *tp)
+static inline void tcp_data_snd_check(struct sock *sk, struct tcp_sock *tp)
{
tcp_push_pending_frames(sk, tp);
tcp_check_space(sk);
@@ -3544,7 +3584,7 @@ static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible)
}
}
-static __inline__ void tcp_ack_snd_check(struct sock *sk)
+static inline void tcp_ack_snd_check(struct sock *sk)
{
if (!inet_csk_ack_scheduled(sk)) {
/* We sent a data segment already. */
@@ -3692,8 +3732,7 @@ static int __tcp_checksum_complete_user(struct sock *sk, struct sk_buff *skb)
return result;
}
-static __inline__ int
-tcp_checksum_complete_user(struct sock *sk, struct sk_buff *skb)
+static inline int tcp_checksum_complete_user(struct sock *sk, struct sk_buff *skb)
{
return skb->ip_summed != CHECKSUM_UNNECESSARY &&
__tcp_checksum_complete_user(sk, skb);
@@ -3967,12 +4006,12 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
struct tcphdr *th, unsigned len)
{
struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
int saved_clamp = tp->rx_opt.mss_clamp;
tcp_parse_options(skb, &tp->rx_opt, 0);
if (th->ack) {
- struct inet_connection_sock *icsk;
/* rfc793:
* "If the state is SYN-SENT then
* first check the ACK bit
@@ -4061,7 +4100,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
if (tp->rx_opt.sack_ok && sysctl_tcp_fack)
tp->rx_opt.sack_ok |= 2;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
tcp_initialize_rcv_mss(sk);
/* Remember, tcp_poll() does not lock socket!
@@ -4072,7 +4111,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
tcp_set_state(sk, TCP_ESTABLISHED);
/* Make sure socket is routed, for correct metrics. */
- tp->af_specific->rebuild_header(sk);
+ icsk->icsk_af_ops->rebuild_header(sk);
tcp_init_metrics(sk);
@@ -4098,8 +4137,6 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
sk_wake_async(sk, 0, POLL_OUT);
}
- icsk = inet_csk(sk);
-
if (sk->sk_write_pending ||
icsk->icsk_accept_queue.rskq_defer_accept ||
icsk->icsk_ack.pingpong) {
@@ -4173,7 +4210,7 @@ discard:
if (tp->ecn_flags&TCP_ECN_OK)
sock_set_flag(sk, SOCK_NO_LARGESEND);
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
tcp_initialize_rcv_mss(sk);
@@ -4220,6 +4257,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
struct tcphdr *th, unsigned len)
{
struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
int queued = 0;
tp->rx_opt.saw_tstamp = 0;
@@ -4236,7 +4274,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
goto discard;
if(th->syn) {
- if(tp->af_specific->conn_request(sk, skb) < 0)
+ if (icsk->icsk_af_ops->conn_request(sk, skb) < 0)
return 1;
/* Now we have several options: In theory there is
@@ -4349,7 +4387,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
/* Make sure socket is routed, for
* correct metrics.
*/
- tp->af_specific->rebuild_header(sk);
+ icsk->icsk_af_ops->rebuild_header(sk);
tcp_init_metrics(sk);
@@ -4475,3 +4513,4 @@ EXPORT_SYMBOL(sysctl_tcp_abc);
EXPORT_SYMBOL(tcp_parse_options);
EXPORT_SYMBOL(tcp_rcv_established);
EXPORT_SYMBOL(tcp_rcv_state_process);
+EXPORT_SYMBOL(tcp_initialize_rcv_mss);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 4d5021e1929b..e9f83e5b28ce 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -69,6 +69,7 @@
#include <net/transp_v6.h>
#include <net/ipv6.h>
#include <net/inet_common.h>
+#include <net/timewait_sock.h>
#include <net/xfrm.h>
#include <linux/inet.h>
@@ -86,8 +87,7 @@ int sysctl_tcp_low_latency;
/* Socket used for sending RSTs */
static struct socket *tcp_socket;
-void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
- struct sk_buff *skb);
+void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb);
struct inet_hashinfo __cacheline_aligned tcp_hashinfo = {
.lhash_lock = RW_LOCK_UNLOCKED,
@@ -97,7 +97,8 @@ struct inet_hashinfo __cacheline_aligned tcp_hashinfo = {
static int tcp_v4_get_port(struct sock *sk, unsigned short snum)
{
- return inet_csk_get_port(&tcp_hashinfo, sk, snum);
+ return inet_csk_get_port(&tcp_hashinfo, sk, snum,
+ inet_csk_bind_conflict);
}
static void tcp_v4_hash(struct sock *sk)
@@ -118,202 +119,38 @@ static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
skb->h.th->source);
}
-/* called with local bh disabled */
-static int __tcp_v4_check_established(struct sock *sk, __u16 lport,
- struct inet_timewait_sock **twp)
+int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
{
- struct inet_sock *inet = inet_sk(sk);
- u32 daddr = inet->rcv_saddr;
- u32 saddr = inet->daddr;
- int dif = sk->sk_bound_dev_if;
- INET_ADDR_COOKIE(acookie, saddr, daddr)
- const __u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
- unsigned int hash = inet_ehashfn(daddr, lport, saddr, inet->dport);
- struct inet_ehash_bucket *head = inet_ehash_bucket(&tcp_hashinfo, hash);
- struct sock *sk2;
- const struct hlist_node *node;
- struct inet_timewait_sock *tw;
-
- prefetch(head->chain.first);
- write_lock(&head->lock);
-
- /* Check TIME-WAIT sockets first. */
- sk_for_each(sk2, node, &(head + tcp_hashinfo.ehash_size)->chain) {
- tw = inet_twsk(sk2);
-
- if (INET_TW_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif)) {
- const struct tcp_timewait_sock *tcptw = tcp_twsk(sk2);
- struct tcp_sock *tp = tcp_sk(sk);
-
- /* With PAWS, it is safe from the viewpoint
- of data integrity. Even without PAWS it
- is safe provided sequence spaces do not
- overlap i.e. at data rates <= 80Mbit/sec.
-
- Actually, the idea is close to VJ's one,
- only timestamp cache is held not per host,
- but per port pair and TW bucket is used
- as state holder.
+ const struct tcp_timewait_sock *tcptw = tcp_twsk(sktw);
+ struct tcp_sock *tp = tcp_sk(sk);
- If TW bucket has been already destroyed we
- fall back to VJ's scheme and use initial
- timestamp retrieved from peer table.
- */
- if (tcptw->tw_ts_recent_stamp &&
- (!twp || (sysctl_tcp_tw_reuse &&
- xtime.tv_sec -
- tcptw->tw_ts_recent_stamp > 1))) {
- tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
- if (tp->write_seq == 0)
- tp->write_seq = 1;
- tp->rx_opt.ts_recent = tcptw->tw_ts_recent;
- tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
- sock_hold(sk2);
- goto unique;
- } else
- goto not_unique;
- }
- }
- tw = NULL;
+ /* With PAWS, it is safe from the viewpoint
+ of data integrity. Even without PAWS it is safe provided sequence
+ spaces do not overlap i.e. at data rates <= 80Mbit/sec.
- /* And established part... */
- sk_for_each(sk2, node, &head->chain) {
- if (INET_MATCH(sk2, hash, acookie, saddr, daddr, ports, dif))
- goto not_unique;
- }
+ Actually, the idea is close to VJ's one, only timestamp cache is
+ held not per host, but per port pair and TW bucket is used as state
+ holder.
-unique:
- /* Must record num and sport now. Otherwise we will see
- * in hash table socket with a funny identity. */
- inet->num = lport;
- inet->sport = htons(lport);
- sk->sk_hash = hash;
- BUG_TRAP(sk_unhashed(sk));
- __sk_add_node(sk, &head->chain);
- sock_prot_inc_use(sk->sk_prot);
- write_unlock(&head->lock);
-
- if (twp) {
- *twp = tw;
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
- } else if (tw) {
- /* Silly. Should hash-dance instead... */
- inet_twsk_deschedule(tw, &tcp_death_row);
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
-
- inet_twsk_put(tw);
+ If TW bucket has been already destroyed we fall back to VJ's scheme
+ and use initial timestamp retrieved from peer table.
+ */
+ if (tcptw->tw_ts_recent_stamp &&
+ (twp == NULL || (sysctl_tcp_tw_reuse &&
+ xtime.tv_sec - tcptw->tw_ts_recent_stamp > 1))) {
+ tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
+ if (tp->write_seq == 0)
+ tp->write_seq = 1;
+ tp->rx_opt.ts_recent = tcptw->tw_ts_recent;
+ tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
+ sock_hold(sktw);
+ return 1;
}
return 0;
-
-not_unique:
- write_unlock(&head->lock);
- return -EADDRNOTAVAIL;
}
-static inline u32 connect_port_offset(const struct sock *sk)
-{
- const struct inet_sock *inet = inet_sk(sk);
-
- return secure_tcp_port_ephemeral(inet->rcv_saddr, inet->daddr,
- inet->dport);
-}
-
-/*
- * Bind a port for a connect operation and hash it.
- */
-static inline int tcp_v4_hash_connect(struct sock *sk)
-{
- const unsigned short snum = inet_sk(sk)->num;
- struct inet_bind_hashbucket *head;
- struct inet_bind_bucket *tb;
- int ret;
-
- if (!snum) {
- int low = sysctl_local_port_range[0];
- int high = sysctl_local_port_range[1];
- int range = high - low;
- int i;
- int port;
- static u32 hint;
- u32 offset = hint + connect_port_offset(sk);
- struct hlist_node *node;
- struct inet_timewait_sock *tw = NULL;
-
- local_bh_disable();
- for (i = 1; i <= range; i++) {
- port = low + (i + offset) % range;
- head = &tcp_hashinfo.bhash[inet_bhashfn(port, tcp_hashinfo.bhash_size)];
- spin_lock(&head->lock);
-
- /* Does not bother with rcv_saddr checks,
- * because the established check is already
- * unique enough.
- */
- inet_bind_bucket_for_each(tb, node, &head->chain) {
- if (tb->port == port) {
- BUG_TRAP(!hlist_empty(&tb->owners));
- if (tb->fastreuse >= 0)
- goto next_port;
- if (!__tcp_v4_check_established(sk,
- port,
- &tw))
- goto ok;
- goto next_port;
- }
- }
-
- tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, port);
- if (!tb) {
- spin_unlock(&head->lock);
- break;
- }
- tb->fastreuse = -1;
- goto ok;
-
- next_port:
- spin_unlock(&head->lock);
- }
- local_bh_enable();
-
- return -EADDRNOTAVAIL;
-
-ok:
- hint += i;
-
- /* Head lock still held and bh's disabled */
- inet_bind_hash(sk, tb, port);
- if (sk_unhashed(sk)) {
- inet_sk(sk)->sport = htons(port);
- __inet_hash(&tcp_hashinfo, sk, 0);
- }
- spin_unlock(&head->lock);
-
- if (tw) {
- inet_twsk_deschedule(tw, &tcp_death_row);;
- inet_twsk_put(tw);
- }
-
- ret = 0;
- goto out;
- }
-
- head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)];
- tb = inet_csk(sk)->icsk_bind_hash;
- spin_lock_bh(&head->lock);
- if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
- __inet_hash(&tcp_hashinfo, sk, 0);
- spin_unlock_bh(&head->lock);
- return 0;
- } else {
- spin_unlock(&head->lock);
- /* No definite answer... Walk to established hash table */
- ret = __tcp_v4_check_established(sk, snum, NULL);
-out:
- local_bh_enable();
- return ret;
- }
-}
+EXPORT_SYMBOL_GPL(tcp_twsk_unique);
/* This will initiate an outgoing connection. */
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
@@ -383,9 +220,9 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
inet->dport = usin->sin_port;
inet->daddr = daddr;
- tp->ext_header_len = 0;
+ inet_csk(sk)->icsk_ext_hdr_len = 0;
if (inet->opt)
- tp->ext_header_len = inet->opt->optlen;
+ inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen;
tp->rx_opt.mss_clamp = 536;
@@ -395,7 +232,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
* complete initialization after this.
*/
tcp_set_state(sk, TCP_SYN_SENT);
- err = tcp_v4_hash_connect(sk);
+ err = inet_hash_connect(&tcp_death_row, sk);
if (err)
goto failure;
@@ -433,12 +270,10 @@ failure:
/*
* This routine does path mtu discovery as defined in RFC1191.
*/
-static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *iph,
- u32 mtu)
+static void do_pmtu_discovery(struct sock *sk, struct iphdr *iph, u32 mtu)
{
struct dst_entry *dst;
struct inet_sock *inet = inet_sk(sk);
- struct tcp_sock *tp = tcp_sk(sk);
/* We are not interested in TCP_LISTEN and open_requests (SYN-ACKs
* send out by Linux are always <576bytes so they should go through
@@ -467,7 +302,7 @@ static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *iph,
mtu = dst_mtu(dst);
if (inet->pmtudisc != IP_PMTUDISC_DONT &&
- tp->pmtu_cookie > mtu) {
+ inet_csk(sk)->icsk_pmtu_cookie > mtu) {
tcp_sync_mss(sk, mtu);
/* Resend the TCP packet because it's
@@ -644,10 +479,10 @@ out:
}
/* This routine computes an IPv4 TCP checksum. */
-void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
- struct sk_buff *skb)
+void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb)
{
struct inet_sock *inet = inet_sk(sk);
+ struct tcphdr *th = skb->h.th;
if (skb->ip_summed == CHECKSUM_HW) {
th->check = ~tcp_v4_check(th, len, inet->saddr, inet->daddr, 0);
@@ -826,7 +661,8 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req)
kfree(inet_rsk(req)->opt);
}
-static inline void syn_flood_warning(struct sk_buff *skb)
+#ifdef CONFIG_SYN_COOKIES
+static void syn_flood_warning(struct sk_buff *skb)
{
static unsigned long warntime;
@@ -837,12 +673,13 @@ static inline void syn_flood_warning(struct sk_buff *skb)
ntohs(skb->h.th->dest));
}
}
+#endif
/*
* Save and compile IPv4 options into the request_sock if needed.
*/
-static inline struct ip_options *tcp_v4_save_options(struct sock *sk,
- struct sk_buff *skb)
+static struct ip_options *tcp_v4_save_options(struct sock *sk,
+ struct sk_buff *skb)
{
struct ip_options *opt = &(IPCB(skb)->opt);
struct ip_options *dopt = NULL;
@@ -869,6 +706,11 @@ struct request_sock_ops tcp_request_sock_ops = {
.send_reset = tcp_v4_send_reset,
};
+static struct timewait_sock_ops tcp_timewait_sock_ops = {
+ .twsk_obj_size = sizeof(struct tcp_timewait_sock),
+ .twsk_unique = tcp_twsk_unique,
+};
+
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
struct inet_request_sock *ireq;
@@ -1053,9 +895,9 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
ireq->opt = NULL;
newinet->mc_index = inet_iif(skb);
newinet->mc_ttl = skb->nh.iph->ttl;
- newtp->ext_header_len = 0;
+ inet_csk(newsk)->icsk_ext_hdr_len = 0;
if (newinet->opt)
- newtp->ext_header_len = newinet->opt->optlen;
+ inet_csk(newsk)->icsk_ext_hdr_len = newinet->opt->optlen;
newinet->id = newtp->write_seq ^ jiffies;
tcp_sync_mss(newsk, dst_mtu(dst));
@@ -1314,16 +1156,6 @@ do_time_wait:
goto discard_it;
}
-static void v4_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
-{
- struct sockaddr_in *sin = (struct sockaddr_in *) uaddr;
- struct inet_sock *inet = inet_sk(sk);
-
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = inet->daddr;
- sin->sin_port = inet->dport;
-}
-
/* VJ's idea. Save last timestamp seen from this destination
* and hold it at least for normal timewait interval to use for duplicate
* segment detection in subsequent connections, before they enter synchronized
@@ -1382,7 +1214,7 @@ int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw)
return 0;
}
-struct tcp_func ipv4_specific = {
+struct inet_connection_sock_af_ops ipv4_specific = {
.queue_xmit = ip_queue_xmit,
.send_check = tcp_v4_send_check,
.rebuild_header = inet_sk_rebuild_header,
@@ -1392,7 +1224,7 @@ struct tcp_func ipv4_specific = {
.net_header_len = sizeof(struct iphdr),
.setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt,
- .addr2sockaddr = v4_addr2sockaddr,
+ .addr2sockaddr = inet_csk_addr2sockaddr,
.sockaddr_len = sizeof(struct sockaddr_in),
};
@@ -1433,7 +1265,8 @@ static int tcp_v4_init_sock(struct sock *sk)
sk->sk_write_space = sk_stream_write_space;
sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
- tp->af_specific = &ipv4_specific;
+ icsk->icsk_af_ops = &ipv4_specific;
+ icsk->icsk_sync_mss = tcp_sync_mss;
sk->sk_sndbuf = sysctl_tcp_wmem[1];
sk->sk_rcvbuf = sysctl_tcp_rmem[1];
@@ -1989,7 +1822,7 @@ struct proto tcp_prot = {
.sysctl_rmem = sysctl_tcp_rmem,
.max_header = MAX_TCP_HEADER,
.obj_size = sizeof(struct tcp_sock),
- .twsk_obj_size = sizeof(struct tcp_timewait_sock),
+ .twsk_prot = &tcp_timewait_sock_ops,
.rsk_prot = &tcp_request_sock_ops,
};
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 1b66a2ac4321..2b9b7f6c7f7c 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -274,18 +274,18 @@ kill:
void tcp_time_wait(struct sock *sk, int state, int timeo)
{
struct inet_timewait_sock *tw = NULL;
+ const struct inet_connection_sock *icsk = inet_csk(sk);
const struct tcp_sock *tp = tcp_sk(sk);
int recycle_ok = 0;
if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp)
- recycle_ok = tp->af_specific->remember_stamp(sk);
+ recycle_ok = icsk->icsk_af_ops->remember_stamp(sk);
if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets)
tw = inet_twsk_alloc(sk, state);
if (tw != NULL) {
struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
- const struct inet_connection_sock *icsk = inet_csk(sk);
const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1);
tw->tw_rcv_wscale = tp->rx_opt.rcv_wscale;
@@ -298,10 +298,12 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
if (tw->tw_family == PF_INET6) {
struct ipv6_pinfo *np = inet6_sk(sk);
- struct tcp6_timewait_sock *tcp6tw = tcp6_twsk((struct sock *)tw);
+ struct inet6_timewait_sock *tw6;
- ipv6_addr_copy(&tcp6tw->tw_v6_daddr, &np->daddr);
- ipv6_addr_copy(&tcp6tw->tw_v6_rcv_saddr, &np->rcv_saddr);
+ tw->tw_ipv6_offset = inet6_tw_offset(sk->sk_prot);
+ tw6 = inet6_twsk((struct sock *)tw);
+ ipv6_addr_copy(&tw6->tw_v6_daddr, &np->daddr);
+ ipv6_addr_copy(&tw6->tw_v6_rcv_saddr, &np->rcv_saddr);
tw->tw_ipv6only = np->ipv6only;
}
#endif
@@ -456,7 +458,6 @@ struct sock *tcp_check_req(struct sock *sk,struct sk_buff *skb,
struct request_sock **prev)
{
struct tcphdr *th = skb->h.th;
- struct tcp_sock *tp = tcp_sk(sk);
u32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK);
int paws_reject = 0;
struct tcp_options_received tmp_opt;
@@ -613,7 +614,8 @@ struct sock *tcp_check_req(struct sock *sk,struct sk_buff *skb,
* ESTABLISHED STATE. If it will be dropped after
* socket is created, wait for troubles.
*/
- child = tp->af_specific->syn_recv_sock(sk, skb, req, NULL);
+ child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb,
+ req, NULL);
if (child == NULL)
goto listen_overflow;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index b7325e0b406a..a7623ead39a8 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -51,8 +51,8 @@ int sysctl_tcp_retrans_collapse = 1;
*/
int sysctl_tcp_tso_win_divisor = 3;
-static inline void update_send_head(struct sock *sk, struct tcp_sock *tp,
- struct sk_buff *skb)
+static void update_send_head(struct sock *sk, struct tcp_sock *tp,
+ struct sk_buff *skb)
{
sk->sk_send_head = skb->next;
if (sk->sk_send_head == (struct sk_buff *)&sk->sk_write_queue)
@@ -124,8 +124,8 @@ static void tcp_cwnd_restart(struct sock *sk, struct dst_entry *dst)
tp->snd_cwnd_used = 0;
}
-static inline void tcp_event_data_sent(struct tcp_sock *tp,
- struct sk_buff *skb, struct sock *sk)
+static void tcp_event_data_sent(struct tcp_sock *tp,
+ struct sk_buff *skb, struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
const u32 now = tcp_time_stamp;
@@ -142,7 +142,7 @@ static inline void tcp_event_data_sent(struct tcp_sock *tp,
icsk->icsk_ack.pingpong = 1;
}
-static __inline__ void tcp_event_ack_sent(struct sock *sk, unsigned int pkts)
+static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts)
{
tcp_dec_quickack_mode(sk, pkts);
inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
@@ -212,7 +212,7 @@ void tcp_select_initial_window(int __space, __u32 mss,
* value can be stuffed directly into th->window for an outgoing
* frame.
*/
-static __inline__ u16 tcp_select_window(struct sock *sk)
+static u16 tcp_select_window(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
u32 cur_win = tcp_receive_window(tp);
@@ -250,6 +250,75 @@ static __inline__ u16 tcp_select_window(struct sock *sk)
return new_win;
}
+static void tcp_build_and_update_options(__u32 *ptr, struct tcp_sock *tp,
+ __u32 tstamp)
+{
+ if (tp->rx_opt.tstamp_ok) {
+ *ptr++ = __constant_htonl((TCPOPT_NOP << 24) |
+ (TCPOPT_NOP << 16) |
+ (TCPOPT_TIMESTAMP << 8) |
+ TCPOLEN_TIMESTAMP);
+ *ptr++ = htonl(tstamp);
+ *ptr++ = htonl(tp->rx_opt.ts_recent);
+ }
+ if (tp->rx_opt.eff_sacks) {
+ struct tcp_sack_block *sp = tp->rx_opt.dsack ? tp->duplicate_sack : tp->selective_acks;
+ int this_sack;
+
+ *ptr++ = htonl((TCPOPT_NOP << 24) |
+ (TCPOPT_NOP << 16) |
+ (TCPOPT_SACK << 8) |
+ (TCPOLEN_SACK_BASE + (tp->rx_opt.eff_sacks *
+ TCPOLEN_SACK_PERBLOCK)));
+ for(this_sack = 0; this_sack < tp->rx_opt.eff_sacks; this_sack++) {
+ *ptr++ = htonl(sp[this_sack].start_seq);
+ *ptr++ = htonl(sp[this_sack].end_seq);
+ }
+ if (tp->rx_opt.dsack) {
+ tp->rx_opt.dsack = 0;
+ tp->rx_opt.eff_sacks--;
+ }
+ }
+}
+
+/* Construct a tcp options header for a SYN or SYN_ACK packet.
+ * If this is every changed make sure to change the definition of
+ * MAX_SYN_SIZE to match the new maximum number of options that you
+ * can generate.
+ */
+static void tcp_syn_build_options(__u32 *ptr, int mss, int ts, int sack,
+ int offer_wscale, int wscale, __u32 tstamp,
+ __u32 ts_recent)
+{
+ /* We always get an MSS option.
+ * The option bytes which will be seen in normal data
+ * packets should timestamps be used, must be in the MSS
+ * advertised. But we subtract them from tp->mss_cache so
+ * that calculations in tcp_sendmsg are simpler etc.
+ * So account for this fact here if necessary. If we
+ * don't do this correctly, as a receiver we won't
+ * recognize data packets as being full sized when we
+ * should, and thus we won't abide by the delayed ACK
+ * rules correctly.
+ * SACKs don't matter, we never delay an ACK when we
+ * have any of those going out.
+ */
+ *ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | mss);
+ if (ts) {
+ if(sack)
+ *ptr++ = __constant_htonl((TCPOPT_SACK_PERM << 24) | (TCPOLEN_SACK_PERM << 16) |
+ (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
+ else
+ *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
+ (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
+ *ptr++ = htonl(tstamp); /* TSVAL */
+ *ptr++ = htonl(ts_recent); /* TSECR */
+ } else if(sack)
+ *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
+ (TCPOPT_SACK_PERM << 8) | TCPOLEN_SACK_PERM);
+ if (offer_wscale)
+ *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_WINDOW << 16) | (TCPOLEN_WINDOW << 8) | (wscale));
+}
/* This routine actually transmits TCP packets queued in by
* tcp_do_sendmsg(). This is used by both the initial
@@ -371,7 +440,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
TCP_ECN_send(sk, tp, skb, tcp_header_size);
}
- tp->af_specific->send_check(sk, th, skb->len, skb);
+ icsk->icsk_af_ops->send_check(sk, skb->len, skb);
if (likely(tcb->flags & TCPCB_FLAG_ACK))
tcp_event_ack_sent(sk, tcp_skb_pcount(skb));
@@ -381,7 +450,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
TCP_INC_STATS(TCP_MIB_OUTSEGS);
- err = tp->af_specific->queue_xmit(skb, 0);
+ err = icsk->icsk_af_ops->queue_xmit(skb, 0);
if (unlikely(err <= 0))
return err;
@@ -621,7 +690,7 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
It is minimum of user_mss and mss received with SYN.
It also does not include TCP options.
- tp->pmtu_cookie is last pmtu, seen by this function.
+ inet_csk(sk)->icsk_pmtu_cookie is last pmtu, seen by this function.
tp->mss_cache is current effective sending mss, including
all tcp options except for SACKs. It is evaluated,
@@ -631,26 +700,26 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
NOTE1. rfc1122 clearly states that advertised MSS
DOES NOT include either tcp or ip options.
- NOTE2. tp->pmtu_cookie and tp->mss_cache are READ ONLY outside
- this function. --ANK (980731)
+ NOTE2. inet_csk(sk)->icsk_pmtu_cookie and tp->mss_cache
+ are READ ONLY outside this function. --ANK (980731)
*/
unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu)
{
struct tcp_sock *tp = tcp_sk(sk);
- int mss_now;
-
+ struct inet_connection_sock *icsk = inet_csk(sk);
/* Calculate base mss without TCP options:
It is MMS_S - sizeof(tcphdr) of rfc1122
*/
- mss_now = pmtu - tp->af_specific->net_header_len - sizeof(struct tcphdr);
+ int mss_now = (pmtu - icsk->icsk_af_ops->net_header_len -
+ sizeof(struct tcphdr));
/* Clamp it (mss_clamp does not include tcp options) */
if (mss_now > tp->rx_opt.mss_clamp)
mss_now = tp->rx_opt.mss_clamp;
/* Now subtract optional transport overhead */
- mss_now -= tp->ext_header_len;
+ mss_now -= icsk->icsk_ext_hdr_len;
/* Then reserve room for full set of TCP options and 8 bytes of data */
if (mss_now < 48)
@@ -664,7 +733,7 @@ unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu)
mss_now = max((tp->max_window>>1), 68U - tp->tcp_header_len);
/* And store cached results */
- tp->pmtu_cookie = pmtu;
+ icsk->icsk_pmtu_cookie = pmtu;
tp->mss_cache = mss_now;
return mss_now;
@@ -694,7 +763,7 @@ unsigned int tcp_current_mss(struct sock *sk, int large_allowed)
if (dst) {
u32 mtu = dst_mtu(dst);
- if (mtu != tp->pmtu_cookie)
+ if (mtu != inet_csk(sk)->icsk_pmtu_cookie)
mss_now = tcp_sync_mss(sk, mtu);
}
@@ -705,9 +774,10 @@ unsigned int tcp_current_mss(struct sock *sk, int large_allowed)
xmit_size_goal = mss_now;
if (doing_tso) {
- xmit_size_goal = 65535 -
- tp->af_specific->net_header_len -
- tp->ext_header_len - tp->tcp_header_len;
+ xmit_size_goal = (65535 -
+ inet_csk(sk)->icsk_af_ops->net_header_len -
+ inet_csk(sk)->icsk_ext_hdr_len -
+ tp->tcp_header_len);
if (tp->max_window &&
(xmit_size_goal > (tp->max_window >> 1)))
@@ -723,7 +793,7 @@ unsigned int tcp_current_mss(struct sock *sk, int large_allowed)
/* Congestion window validation. (RFC2861) */
-static inline void tcp_cwnd_validate(struct sock *sk, struct tcp_sock *tp)
+static void tcp_cwnd_validate(struct sock *sk, struct tcp_sock *tp)
{
__u32 packets_out = tp->packets_out;
@@ -772,7 +842,7 @@ static inline unsigned int tcp_cwnd_test(struct tcp_sock *tp, struct sk_buff *sk
/* This must be invoked the first time we consider transmitting
* SKB onto the wire.
*/
-static inline int tcp_init_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned int mss_now)
+static int tcp_init_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned int mss_now)
{
int tso_segs = tcp_skb_pcount(skb);
@@ -1422,7 +1492,7 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
(sysctl_tcp_retrans_collapse != 0))
tcp_retrans_try_collapse(sk, skb, cur_mss);
- if(tp->af_specific->rebuild_header(sk))
+ if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk))
return -EHOSTUNREACH; /* Routing failure or similar. */
/* Some Solaris stacks overoptimize and ignore the FIN on a
@@ -1793,7 +1863,7 @@ struct sk_buff * tcp_make_synack(struct sock *sk, struct dst_entry *dst,
/*
* Do all connect socket setups that can be done AF independent.
*/
-static inline void tcp_connect_init(struct sock *sk)
+static void tcp_connect_init(struct sock *sk)
{
struct dst_entry *dst = __sk_dst_get(sk);
struct tcp_sock *tp = tcp_sk(sk);
diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c
index 13e7e6e8df16..3b7403495052 100644
--- a/net/ipv4/tcp_vegas.c
+++ b/net/ipv4/tcp_vegas.c
@@ -330,6 +330,10 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack,
vegas->cntRTT = 0;
vegas->minRTT = 0x7fffffff;
}
+ /* Use normal slow start */
+ else if (tp->snd_cwnd <= tp->snd_ssthresh)
+ tcp_slow_start(tp);
+
}
/* Extract info for Tcp socket info provided via netlink. */
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 2422a5f7195d..223abaa72bc5 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -86,6 +86,7 @@
#include <linux/module.h>
#include <linux/socket.h>
#include <linux/sockios.h>
+#include <linux/igmp.h>
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/timer.h>
@@ -846,20 +847,7 @@ out:
csum_copy_err:
UDP_INC_STATS_BH(UDP_MIB_INERRORS);
- /* Clear queue. */
- if (flags&MSG_PEEK) {
- int clear = 0;
- spin_lock_bh(&sk->sk_receive_queue.lock);
- if (skb == skb_peek(&sk->sk_receive_queue)) {
- __skb_unlink(skb, &sk->sk_receive_queue);
- clear = 1;
- }
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- if (clear)
- kfree_skb(skb);
- }
-
- skb_free_datagram(sk, skb);
+ skb_kill_datagram(sk, skb, flags);
if (noblock)
return -EAGAIN;
@@ -1094,7 +1082,7 @@ static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh,
* Otherwise, csum completion requires chacksumming packet body,
* including udp header and folding it to skb->csum.
*/
-static int udp_checksum_init(struct sk_buff *skb, struct udphdr *uh,
+static void udp_checksum_init(struct sk_buff *skb, struct udphdr *uh,
unsigned short ulen, u32 saddr, u32 daddr)
{
if (uh->check == 0) {
@@ -1108,7 +1096,6 @@ static int udp_checksum_init(struct sk_buff *skb, struct udphdr *uh,
/* Probably, we should checksum udp header (it should be in cache
* in any case) and data in tiny packets (< rx copybreak).
*/
- return 0;
}
/*
@@ -1141,8 +1128,7 @@ int udp_rcv(struct sk_buff *skb)
if (pskb_trim_rcsum(skb, ulen))
goto short_packet;
- if (udp_checksum_init(skb, uh, ulen, saddr, daddr) < 0)
- goto csum_error;
+ udp_checksum_init(skb, uh, ulen, saddr, daddr);
if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index b2b60f3e9cdd..42196ba3b0b9 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -182,6 +182,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl)
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_SCTP:
+ case IPPROTO_DCCP:
if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
u16 *ports = (u16 *)xprth;
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 6460eec834b7..9601fd7f9d66 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -8,7 +8,8 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \
route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \
protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
- ip6_flowlabel.o ipv6_syms.o netfilter.o
+ ip6_flowlabel.o ipv6_syms.o netfilter.o \
+ inet6_connection_sock.o
ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \
xfrm6_output.o
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 73a23b4130a5..704fb73e6c5f 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -137,6 +137,7 @@ static int addrconf_ifdown(struct net_device *dev, int how);
static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags);
static void addrconf_dad_timer(unsigned long data);
static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
+static void addrconf_dad_run(struct inet6_dev *idev);
static void addrconf_rs_timer(unsigned long data);
static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
@@ -388,6 +389,9 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
}
#endif
+ if (netif_carrier_ok(dev))
+ ndev->if_flags |= IF_READY;
+
write_lock_bh(&addrconf_lock);
dev->ip6_ptr = ndev;
write_unlock_bh(&addrconf_lock);
@@ -415,6 +419,7 @@ static struct inet6_dev * ipv6_find_idev(struct net_device *dev)
if ((idev = ipv6_add_dev(dev)) == NULL)
return NULL;
}
+
if (dev->flags&IFF_UP)
ipv6_mc_up(idev);
return idev;
@@ -634,8 +639,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
}
#endif
- for (ifap = &idev->addr_list; (ifa=*ifap) != NULL;
- ifap = &ifa->if_next) {
+ for (ifap = &idev->addr_list; (ifa=*ifap) != NULL;) {
if (ifa == ifp) {
*ifap = ifa->if_next;
__in6_ifa_put(ifp);
@@ -643,6 +647,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
if (!(ifp->flags & IFA_F_PERMANENT) || onlink > 0)
break;
deleted = 1;
+ continue;
} else if (ifp->flags & IFA_F_PERMANENT) {
if (ipv6_prefix_equal(&ifa->addr, &ifp->addr,
ifp->prefix_len)) {
@@ -666,6 +671,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
}
}
}
+ ifap = &ifa->if_next;
}
write_unlock_bh(&idev->lock);
@@ -903,11 +909,18 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
score.addr_type = __ipv6_addr_type(&ifa->addr);
- /* Rule 0: Candidate Source Address (section 4)
+ /* Rule 0:
+ * - Tentative Address (RFC2462 section 5.4)
+ * - A tentative address is not considered
+ * "assigned to an interface" in the traditional
+ * sense.
+ * - Candidate Source Address (section 4)
* - In any case, anycast addresses, multicast
* addresses, and the unspecified address MUST
* NOT be included in a candidate set.
*/
+ if (ifa->flags & IFA_F_TENTATIVE)
+ continue;
if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
score.addr_type & IPV6_ADDR_MULTICAST)) {
LIMIT_NETDEBUG(KERN_DEBUG
@@ -1182,7 +1195,7 @@ struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, struct net_device *
int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
{
const struct in6_addr *sk_rcv_saddr6 = &inet6_sk(sk)->rcv_saddr;
- const struct in6_addr *sk2_rcv_saddr6 = tcp_v6_rcv_saddr(sk2);
+ const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2);
u32 sk_rcv_saddr = inet_sk(sk)->rcv_saddr;
u32 sk2_rcv_saddr = inet_rcv_saddr(sk2);
int sk_ipv6only = ipv6_only_sock(sk);
@@ -1215,10 +1228,8 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
/* Gets referenced address, destroys ifaddr */
-void addrconf_dad_failure(struct inet6_ifaddr *ifp)
+void addrconf_dad_stop(struct inet6_ifaddr *ifp)
{
- if (net_ratelimit())
- printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);
if (ifp->flags&IFA_F_PERMANENT) {
spin_lock_bh(&ifp->lock);
addrconf_del_timer(ifp);
@@ -1244,6 +1255,12 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
ipv6_del_addr(ifp);
}
+void addrconf_dad_failure(struct inet6_ifaddr *ifp)
+{
+ if (net_ratelimit())
+ printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);
+ addrconf_dad_stop(ifp);
+}
/* Join to solicited addr multicast group. */
@@ -1596,9 +1613,17 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
not good.
*/
if (valid_lft >= 0x7FFFFFFF/HZ)
- rt_expires = 0;
+ rt_expires = 0x7FFFFFFF - (0x7FFFFFFF % HZ);
else
- rt_expires = jiffies + valid_lft * HZ;
+ rt_expires = valid_lft * HZ;
+
+ /*
+ * We convert this (in jiffies) to clock_t later.
+ * Avoid arithmetic overflow there as well.
+ * Overflow can happen only if HZ < USER_HZ.
+ */
+ if (HZ < USER_HZ && rt_expires > 0x7FFFFFFF / USER_HZ)
+ rt_expires = 0x7FFFFFFF / USER_HZ;
if (pinfo->onlink) {
struct rt6_info *rt;
@@ -1610,12 +1635,12 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
ip6_del_rt(rt, NULL, NULL, NULL);
rt = NULL;
} else {
- rt->rt6i_expires = rt_expires;
+ rt->rt6i_expires = jiffies + rt_expires;
}
}
} else if (valid_lft) {
addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
- dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES|RTF_PREFIX_RT);
+ dev, jiffies_to_clock_t(rt_expires), RTF_ADDRCONF|RTF_EXPIRES|RTF_PREFIX_RT);
}
if (rt)
dst_release(&rt->u.dst);
@@ -2125,9 +2150,42 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
{
struct net_device *dev = (struct net_device *) data;
struct inet6_dev *idev = __in6_dev_get(dev);
+ int run_pending = 0;
switch(event) {
case NETDEV_UP:
+ case NETDEV_CHANGE:
+ if (event == NETDEV_UP) {
+ if (!netif_carrier_ok(dev)) {
+ /* device is not ready yet. */
+ printk(KERN_INFO
+ "ADDRCONF(NETDEV_UP): %s: "
+ "link is not ready\n",
+ dev->name);
+ break;
+ }
+ } else {
+ if (!netif_carrier_ok(dev)) {
+ /* device is still not ready. */
+ break;
+ }
+
+ if (idev) {
+ if (idev->if_flags & IF_READY) {
+ /* device is already configured. */
+ break;
+ }
+ idev->if_flags |= IF_READY;
+ }
+
+ printk(KERN_INFO
+ "ADDRCONF(NETDEV_CHANGE): %s: "
+ "link becomes ready\n",
+ dev->name);
+
+ run_pending = 1;
+ }
+
switch(dev->type) {
case ARPHRD_SIT:
addrconf_sit_config(dev);
@@ -2144,6 +2202,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
break;
};
if (idev) {
+ if (run_pending)
+ addrconf_dad_run(idev);
+
/* If the MTU changed during the interface down, when the
interface up, the changed MTU must be reflected in the
idev as well as routers.
@@ -2178,8 +2239,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
*/
addrconf_ifdown(dev, event != NETDEV_DOWN);
break;
- case NETDEV_CHANGE:
- break;
+
case NETDEV_CHANGENAME:
#ifdef CONFIG_SYSCTL
if (idev) {
@@ -2260,7 +2320,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
/* Step 3: clear flags for stateless addrconf */
if (how != 1)
- idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD);
+ idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY);
/* Step 4: clear address list */
#ifdef CONFIG_IPV6_PRIVACY
@@ -2369,11 +2429,20 @@ out:
/*
* Duplicate Address Detection
*/
+static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
+{
+ unsigned long rand_num;
+ struct inet6_dev *idev = ifp->idev;
+
+ rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+ ifp->probes = idev->cnf.dad_transmits;
+ addrconf_mod_timer(ifp, AC_DAD, rand_num);
+}
+
static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
{
struct inet6_dev *idev = ifp->idev;
struct net_device *dev = idev->dev;
- unsigned long rand_num;
addrconf_join_solict(dev, &ifp->addr);
@@ -2382,7 +2451,6 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
flags);
net_srandom(ifp->addr.s6_addr32[3]);
- rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
read_lock_bh(&idev->lock);
if (ifp->dead)
@@ -2399,9 +2467,19 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
return;
}
- ifp->probes = idev->cnf.dad_transmits;
- addrconf_mod_timer(ifp, AC_DAD, rand_num);
-
+ if (!(idev->if_flags & IF_READY)) {
+ spin_unlock_bh(&ifp->lock);
+ read_unlock_bh(&idev->lock);
+ /*
+ * If the defice is not ready:
+ * - keep it tentative if it is a permanent address.
+ * - otherwise, kill it.
+ */
+ in6_ifa_hold(ifp);
+ addrconf_dad_stop(ifp);
+ return;
+ }
+ addrconf_dad_kick(ifp);
spin_unlock_bh(&ifp->lock);
out:
read_unlock_bh(&idev->lock);
@@ -2484,6 +2562,22 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
}
}
+static void addrconf_dad_run(struct inet6_dev *idev) {
+ struct inet6_ifaddr *ifp;
+
+ read_lock_bh(&idev->lock);
+ for (ifp = idev->addr_list; ifp; ifp = ifp->if_next) {
+ spin_lock_bh(&ifp->lock);
+ if (!(ifp->flags & IFA_F_TENTATIVE)) {
+ spin_unlock_bh(&ifp->lock);
+ continue;
+ }
+ spin_unlock_bh(&ifp->lock);
+ addrconf_dad_kick(ifp);
+ }
+ read_unlock_bh(&idev->lock);
+}
+
#ifdef CONFIG_PROC_FS
struct if6_iter_state {
int bucket;
@@ -2689,6 +2783,9 @@ restart:
in6_ifa_hold(ifpub);
spin_unlock(&ifp->lock);
read_unlock(&addrconf_hash_lock);
+ spin_lock(&ifpub->lock);
+ ifpub->regen_count = 0;
+ spin_unlock(&ifpub->lock);
ipv6_create_tempaddr(ifpub, ifp);
in6_ifa_put(ifpub);
in6_ifa_put(ifp);
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index d9546380fa04..68afc53be662 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -167,6 +167,7 @@ lookup_protocol:
sk->sk_reuse = 1;
inet = inet_sk(sk);
+ inet->is_icsk = INET_PROTOSW_ICSK & answer_flags;
if (SOCK_RAW == sock->type) {
inet->num = protocol;
@@ -389,6 +390,8 @@ int inet6_destroy_sock(struct sock *sk)
return 0;
}
+EXPORT_SYMBOL_GPL(inet6_destroy_sock);
+
/*
* This does both peername and sockname.
*/
@@ -431,7 +434,6 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
- int err = -EINVAL;
switch(cmd)
{
@@ -450,16 +452,15 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCSIFDSTADDR:
return addrconf_set_dstaddr((void __user *) arg);
default:
- if (!sk->sk_prot->ioctl ||
- (err = sk->sk_prot->ioctl(sk, cmd, arg)) == -ENOIOCTLCMD)
- return(dev_ioctl(cmd,(void __user *) arg));
- return err;
+ if (!sk->sk_prot->ioctl)
+ return -ENOIOCTLCMD;
+ return sk->sk_prot->ioctl(sk, cmd, arg);
}
/*NOTREACHED*/
return(0);
}
-struct proto_ops inet6_stream_ops = {
+const struct proto_ops inet6_stream_ops = {
.family = PF_INET6,
.owner = THIS_MODULE,
.release = inet6_release,
@@ -480,7 +481,7 @@ struct proto_ops inet6_stream_ops = {
.sendpage = tcp_sendpage
};
-struct proto_ops inet6_dgram_ops = {
+const struct proto_ops inet6_dgram_ops = {
.family = PF_INET6,
.owner = THIS_MODULE,
.release = inet6_release,
@@ -508,7 +509,7 @@ static struct net_proto_family inet6_family_ops = {
};
/* Same as inet6_dgram_ops, sans udp_poll. */
-static struct proto_ops inet6_sockraw_ops = {
+static const struct proto_ops inet6_sockraw_ops = {
.family = PF_INET6,
.owner = THIS_MODULE,
.release = inet6_release,
@@ -609,6 +610,79 @@ inet6_unregister_protosw(struct inet_protosw *p)
}
}
+int inet6_sk_rebuild_header(struct sock *sk)
+{
+ int err;
+ struct dst_entry *dst;
+ struct ipv6_pinfo *np = inet6_sk(sk);
+
+ dst = __sk_dst_check(sk, np->dst_cookie);
+
+ if (dst == NULL) {
+ struct inet_sock *inet = inet_sk(sk);
+ struct in6_addr *final_p = NULL, final;
+ struct flowi fl;
+
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = sk->sk_protocol;
+ ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+ ipv6_addr_copy(&fl.fl6_src, &np->saddr);
+ fl.fl6_flowlabel = np->flow_label;
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = inet->dport;
+ fl.fl_ip_sport = inet->sport;
+
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+
+ err = ip6_dst_lookup(sk, &dst, &fl);
+ if (err) {
+ sk->sk_route_caps = 0;
+ return err;
+ }
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+
+ if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
+ sk->sk_err_soft = -err;
+ return err;
+ }
+
+ ip6_dst_store(sk, dst, NULL);
+ sk->sk_route_caps = dst->dev->features &
+ ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(inet6_sk_rebuild_header);
+
+int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb)
+{
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct inet6_skb_parm *opt = IP6CB(skb);
+
+ if (np->rxopt.all) {
+ if ((opt->hop && (np->rxopt.bits.hopopts ||
+ np->rxopt.bits.ohopopts)) ||
+ ((IPV6_FLOWINFO_MASK & *(u32*)skb->nh.raw) &&
+ np->rxopt.bits.rxflow) ||
+ (opt->srcrt && (np->rxopt.bits.srcrt ||
+ np->rxopt.bits.osrcrt)) ||
+ ((opt->dst1 || opt->dst0) &&
+ (np->rxopt.bits.dstopts || np->rxopt.bits.odstopts)))
+ return 1;
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(ipv6_opt_accepted);
+
int
snmp6_mib_init(void *ptr[2], size_t mibsize, size_t mibalign)
{
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index f3629730eb15..13cc7f895583 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -33,6 +33,7 @@
#include <linux/string.h>
#include <net/icmp.h>
#include <net/ipv6.h>
+#include <net/protocol.h>
#include <net/xfrm.h>
#include <asm/scatterlist.h>
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 8bfbe9970793..6de8ee1a5ad9 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -36,6 +36,7 @@
#include <linux/random.h>
#include <net/icmp.h>
#include <net/ipv6.h>
+#include <net/protocol.h>
#include <linux/icmpv6.h>
static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index be6faf311387..113374dc342c 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -413,6 +413,8 @@ ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
return opt;
}
+EXPORT_SYMBOL_GPL(ipv6_invert_rthdr);
+
/**********************************
Hop-by-hop options.
**********************************/
@@ -579,6 +581,8 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
return opt2;
}
+EXPORT_SYMBOL_GPL(ipv6_dup_options);
+
static int ipv6_renew_option(void *ohdr,
struct ipv6_opt_hdr __user *newopt, int newoptlen,
int inherit,
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 34a332225c17..6ec6a2b549bb 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -328,8 +328,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
iif = skb->dev->ifindex;
/*
- * Must not send if we know that source is Anycast also.
- * for now we don't know that.
+ * Must not send error if the source does not uniquely
+ * identify a single node (RFC2463 Section 2.4).
+ * We check unspecified / multicast addresses here,
+ * and anycast addresses will be checked later.
*/
if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: addr_any/mcast source\n");
@@ -373,6 +375,16 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
err = ip6_dst_lookup(sk, &dst, &fl);
if (err)
goto out;
+
+ /*
+ * We won't send icmp if the destination is known
+ * anycast.
+ */
+ if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) {
+ LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n");
+ goto out_dst_release;
+ }
+
if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
goto out;
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
new file mode 100644
index 000000000000..792f90f0f9ec
--- /dev/null
+++ b/net/ipv6/inet6_connection_sock.c
@@ -0,0 +1,199 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Support for INET6 connection oriented protocols.
+ *
+ * Authors: See the TCPv6 sources
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or(at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/in6.h>
+#include <linux/ipv6.h>
+#include <linux/jhash.h>
+
+#include <net/addrconf.h>
+#include <net/inet_connection_sock.h>
+#include <net/inet_ecn.h>
+#include <net/inet_hashtables.h>
+#include <net/ip6_route.h>
+#include <net/sock.h>
+
+int inet6_csk_bind_conflict(const struct sock *sk,
+ const struct inet_bind_bucket *tb)
+{
+ const struct sock *sk2;
+ const struct hlist_node *node;
+
+ /* We must walk the whole port owner list in this case. -DaveM */
+ sk_for_each_bound(sk2, node, &tb->owners) {
+ if (sk != sk2 &&
+ (!sk->sk_bound_dev_if ||
+ !sk2->sk_bound_dev_if ||
+ sk->sk_bound_dev_if == sk2->sk_bound_dev_if) &&
+ (!sk->sk_reuse || !sk2->sk_reuse ||
+ sk2->sk_state == TCP_LISTEN) &&
+ ipv6_rcv_saddr_equal(sk, sk2))
+ break;
+ }
+
+ return node != NULL;
+}
+
+EXPORT_SYMBOL_GPL(inet6_csk_bind_conflict);
+
+/*
+ * request_sock (formerly open request) hash tables.
+ */
+static u32 inet6_synq_hash(const struct in6_addr *raddr, const u16 rport,
+ const u32 rnd, const u16 synq_hsize)
+{
+ u32 a = raddr->s6_addr32[0];
+ u32 b = raddr->s6_addr32[1];
+ u32 c = raddr->s6_addr32[2];
+
+ a += JHASH_GOLDEN_RATIO;
+ b += JHASH_GOLDEN_RATIO;
+ c += rnd;
+ __jhash_mix(a, b, c);
+
+ a += raddr->s6_addr32[3];
+ b += (u32)rport;
+ __jhash_mix(a, b, c);
+
+ return c & (synq_hsize - 1);
+}
+
+struct request_sock *inet6_csk_search_req(const struct sock *sk,
+ struct request_sock ***prevp,
+ const __u16 rport,
+ const struct in6_addr *raddr,
+ const struct in6_addr *laddr,
+ const int iif)
+{
+ const struct inet_connection_sock *icsk = inet_csk(sk);
+ struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
+ struct request_sock *req, **prev;
+
+ for (prev = &lopt->syn_table[inet6_synq_hash(raddr, rport,
+ lopt->hash_rnd,
+ lopt->nr_table_entries)];
+ (req = *prev) != NULL;
+ prev = &req->dl_next) {
+ const struct inet6_request_sock *treq = inet6_rsk(req);
+
+ if (inet_rsk(req)->rmt_port == rport &&
+ req->rsk_ops->family == AF_INET6 &&
+ ipv6_addr_equal(&treq->rmt_addr, raddr) &&
+ ipv6_addr_equal(&treq->loc_addr, laddr) &&
+ (!treq->iif || treq->iif == iif)) {
+ BUG_TRAP(req->sk == NULL);
+ *prevp = prev;
+ return req;
+ }
+ }
+
+ return NULL;
+}
+
+EXPORT_SYMBOL_GPL(inet6_csk_search_req);
+
+void inet6_csk_reqsk_queue_hash_add(struct sock *sk,
+ struct request_sock *req,
+ const unsigned long timeout)
+{
+ struct inet_connection_sock *icsk = inet_csk(sk);
+ struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
+ const u32 h = inet6_synq_hash(&inet6_rsk(req)->rmt_addr,
+ inet_rsk(req)->rmt_port,
+ lopt->hash_rnd, lopt->nr_table_entries);
+
+ reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, timeout);
+ inet_csk_reqsk_queue_added(sk, timeout);
+}
+
+EXPORT_SYMBOL_GPL(inet6_csk_reqsk_queue_hash_add);
+
+void inet6_csk_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
+{
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) uaddr;
+
+ sin6->sin6_family = AF_INET6;
+ ipv6_addr_copy(&sin6->sin6_addr, &np->daddr);
+ sin6->sin6_port = inet_sk(sk)->dport;
+ /* We do not store received flowlabel for TCP */
+ sin6->sin6_flowinfo = 0;
+ sin6->sin6_scope_id = 0;
+ if (sk->sk_bound_dev_if &&
+ ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
+ sin6->sin6_scope_id = sk->sk_bound_dev_if;
+}
+
+EXPORT_SYMBOL_GPL(inet6_csk_addr2sockaddr);
+
+int inet6_csk_xmit(struct sk_buff *skb, int ipfragok)
+{
+ struct sock *sk = skb->sk;
+ struct inet_sock *inet = inet_sk(sk);
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct flowi fl;
+ struct dst_entry *dst;
+ struct in6_addr *final_p = NULL, final;
+
+ memset(&fl, 0, sizeof(fl));
+ fl.proto = sk->sk_protocol;
+ ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+ ipv6_addr_copy(&fl.fl6_src, &np->saddr);
+ fl.fl6_flowlabel = np->flow_label;
+ IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_sport = inet->sport;
+ fl.fl_ip_dport = inet->dport;
+
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
+ ipv6_addr_copy(&final, &fl.fl6_dst);
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ final_p = &final;
+ }
+
+ dst = __sk_dst_check(sk, np->dst_cookie);
+
+ if (dst == NULL) {
+ int err = ip6_dst_lookup(sk, &dst, &fl);
+
+ if (err) {
+ sk->sk_err_soft = -err;
+ return err;
+ }
+
+ if (final_p)
+ ipv6_addr_copy(&fl.fl6_dst, final_p);
+
+ if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
+ sk->sk_route_caps = 0;
+ return err;
+ }
+
+ ip6_dst_store(sk, dst, NULL);
+ sk->sk_route_caps = dst->dev->features &
+ ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
+ }
+
+ skb->dst = dst_clone(dst);
+
+ /* Restore final destination back after routing done */
+ ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+
+ return ip6_xmit(sk, skb, &fl, np->opt, 0);
+}
+
+EXPORT_SYMBOL_GPL(inet6_csk_xmit);
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index 01d5f46d4e40..4154f3a8b6cf 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -5,7 +5,8 @@
*
* Generic INET6 transport hashtables
*
- * Authors: Lotsa people, from code originally in tcp
+ * Authors: Lotsa people, from code originally in tcp, generalised here
+ * by Arnaldo Carvalho de Melo <acme@mandriva.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -14,12 +15,13 @@
*/
#include <linux/config.h>
-
#include <linux/module.h>
+#include <linux/random.h>
#include <net/inet_connection_sock.h>
#include <net/inet_hashtables.h>
#include <net/inet6_hashtables.h>
+#include <net/ip.h>
struct sock *inet6_lookup_listener(struct inet_hashinfo *hashinfo,
const struct in6_addr *daddr,
@@ -79,3 +81,180 @@ struct sock *inet6_lookup(struct inet_hashinfo *hashinfo,
}
EXPORT_SYMBOL_GPL(inet6_lookup);
+
+static int __inet6_check_established(struct inet_timewait_death_row *death_row,
+ struct sock *sk, const __u16 lport,
+ struct inet_timewait_sock **twp)
+{
+ struct inet_hashinfo *hinfo = death_row->hashinfo;
+ const struct inet_sock *inet = inet_sk(sk);
+ const struct ipv6_pinfo *np = inet6_sk(sk);
+ const struct in6_addr *daddr = &np->rcv_saddr;
+ const struct in6_addr *saddr = &np->daddr;
+ const int dif = sk->sk_bound_dev_if;
+ const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
+ const unsigned int hash = inet6_ehashfn(daddr, inet->num, saddr,
+ inet->dport);
+ struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
+ struct sock *sk2;
+ const struct hlist_node *node;
+ struct inet_timewait_sock *tw;
+
+ prefetch(head->chain.first);
+ write_lock(&head->lock);
+
+ /* Check TIME-WAIT sockets first. */
+ sk_for_each(sk2, node, &(head + hinfo->ehash_size)->chain) {
+ const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);
+
+ tw = inet_twsk(sk2);
+
+ if(*((__u32 *)&(tw->tw_dport)) == ports &&
+ sk2->sk_family == PF_INET6 &&
+ ipv6_addr_equal(&tw6->tw_v6_daddr, saddr) &&
+ ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr) &&
+ sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
+ if (twsk_unique(sk, sk2, twp))
+ goto unique;
+ else
+ goto not_unique;
+ }
+ }
+ tw = NULL;
+
+ /* And established part... */
+ sk_for_each(sk2, node, &head->chain) {
+ if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
+ goto not_unique;
+ }
+
+unique:
+ BUG_TRAP(sk_unhashed(sk));
+ __sk_add_node(sk, &head->chain);
+ sk->sk_hash = hash;
+ sock_prot_inc_use(sk->sk_prot);
+ write_unlock(&head->lock);
+
+ if (twp != NULL) {
+ *twp = tw;
+ NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
+ } else if (tw != NULL) {
+ /* Silly. Should hash-dance instead... */
+ inet_twsk_deschedule(tw, death_row);
+ NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
+
+ inet_twsk_put(tw);
+ }
+ return 0;
+
+not_unique:
+ write_unlock(&head->lock);
+ return -EADDRNOTAVAIL;
+}
+
+static inline u32 inet6_sk_port_offset(const struct sock *sk)
+{
+ const struct inet_sock *inet = inet_sk(sk);
+ const struct ipv6_pinfo *np = inet6_sk(sk);
+ return secure_ipv6_port_ephemeral(np->rcv_saddr.s6_addr32,
+ np->daddr.s6_addr32,
+ inet->dport);
+}
+
+int inet6_hash_connect(struct inet_timewait_death_row *death_row,
+ struct sock *sk)
+{
+ struct inet_hashinfo *hinfo = death_row->hashinfo;
+ const unsigned short snum = inet_sk(sk)->num;
+ struct inet_bind_hashbucket *head;
+ struct inet_bind_bucket *tb;
+ int ret;
+
+ if (snum == 0) {
+ const int low = sysctl_local_port_range[0];
+ const int high = sysctl_local_port_range[1];
+ const int range = high - low;
+ int i, port;
+ static u32 hint;
+ const u32 offset = hint + inet6_sk_port_offset(sk);
+ struct hlist_node *node;
+ struct inet_timewait_sock *tw = NULL;
+
+ local_bh_disable();
+ for (i = 1; i <= range; i++) {
+ port = low + (i + offset) % range;
+ head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)];
+ spin_lock(&head->lock);
+
+ /* Does not bother with rcv_saddr checks,
+ * because the established check is already
+ * unique enough.
+ */
+ inet_bind_bucket_for_each(tb, node, &head->chain) {
+ if (tb->port == port) {
+ BUG_TRAP(!hlist_empty(&tb->owners));
+ if (tb->fastreuse >= 0)
+ goto next_port;
+ if (!__inet6_check_established(death_row,
+ sk, port,
+ &tw))
+ goto ok;
+ goto next_port;
+ }
+ }
+
+ tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
+ head, port);
+ if (!tb) {
+ spin_unlock(&head->lock);
+ break;
+ }
+ tb->fastreuse = -1;
+ goto ok;
+
+ next_port:
+ spin_unlock(&head->lock);
+ }
+ local_bh_enable();
+
+ return -EADDRNOTAVAIL;
+
+ok:
+ hint += i;
+
+ /* Head lock still held and bh's disabled */
+ inet_bind_hash(sk, tb, port);
+ if (sk_unhashed(sk)) {
+ inet_sk(sk)->sport = htons(port);
+ __inet6_hash(hinfo, sk);
+ }
+ spin_unlock(&head->lock);
+
+ if (tw) {
+ inet_twsk_deschedule(tw, death_row);
+ inet_twsk_put(tw);
+ }
+
+ ret = 0;
+ goto out;
+ }
+
+ head = &hinfo->bhash[inet_bhashfn(snum, hinfo->bhash_size)];
+ tb = inet_csk(sk)->icsk_bind_hash;
+ spin_lock_bh(&head->lock);
+
+ if (sk_head(&tb->owners) == sk && sk->sk_bind_node.next == NULL) {
+ __inet6_hash(hinfo, sk);
+ spin_unlock_bh(&head->lock);
+ return 0;
+ } else {
+ spin_unlock(&head->lock);
+ /* No definite answer... Walk to established hash table */
+ ret = __inet6_check_established(death_row, sk, snum, NULL);
+out:
+ local_bh_enable();
+ return ret;
+ }
+}
+
+EXPORT_SYMBOL_GPL(inet6_hash_connect);
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index 1cf02765fb5c..89d12b4817a9 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -200,6 +200,8 @@ struct ip6_flowlabel * fl6_sock_lookup(struct sock *sk, u32 label)
return NULL;
}
+EXPORT_SYMBOL_GPL(fl6_sock_lookup);
+
void fl6_free_socklist(struct sock *sk)
{
struct ipv6_pinfo *np = inet6_sk(sk);
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 8523c76ebf76..b4c4beba0ede 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -775,6 +775,8 @@ out_err_release:
return err;
}
+EXPORT_SYMBOL_GPL(ip6_dst_lookup);
+
static inline int ip6_ufo_append_data(struct sock *sk,
int getfrag(void *from, char *to, int offset, int len,
int odd, struct sk_buff *skb),
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index 55917fb17094..626dd39685f2 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -47,6 +47,7 @@
#include <linux/rtnetlink.h>
#include <net/icmp.h>
#include <net/ipv6.h>
+#include <net/protocol.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 3620718defe6..c63868dd2ca2 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -163,17 +163,17 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
sk_refcnt_debug_dec(sk);
if (sk->sk_protocol == IPPROTO_TCP) {
- struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
local_bh_disable();
sock_prot_dec_use(sk->sk_prot);
sock_prot_inc_use(&tcp_prot);
local_bh_enable();
sk->sk_prot = &tcp_prot;
- tp->af_specific = &ipv4_specific;
+ icsk->icsk_af_ops = &ipv4_specific;
sk->sk_socket->ops = &inet_stream_ops;
sk->sk_family = PF_INET;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
} else {
local_bh_disable();
sock_prot_dec_use(sk->sk_prot);
@@ -317,14 +317,15 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname,
}
retv = 0;
- if (sk->sk_type == SOCK_STREAM) {
+ if (inet_sk(sk)->is_icsk) {
if (opt) {
- struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
if (!((1 << sk->sk_state) &
(TCPF_LISTEN | TCPF_CLOSE))
&& inet_sk(sk)->daddr != LOOPBACK4_IPV6) {
- tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ icsk->icsk_ext_hdr_len =
+ opt->opt_flen + opt->opt_nflen;
+ icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
}
}
opt = xchg(&np->opt, opt);
@@ -380,14 +381,15 @@ sticky_done:
goto done;
update:
retv = 0;
- if (sk->sk_type == SOCK_STREAM) {
+ if (inet_sk(sk)->is_icsk) {
if (opt) {
- struct tcp_sock *tp = tcp_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
if (!((1 << sk->sk_state) &
(TCPF_LISTEN | TCPF_CLOSE))
&& inet_sk(sk)->daddr != LOOPBACK4_IPV6) {
- tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
- tcp_sync_mss(sk, tp->pmtu_cookie);
+ icsk->icsk_ext_hdr_len =
+ opt->opt_flen + opt->opt_nflen;
+ icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
}
}
opt = xchg(&np->opt, opt);
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index fd939da090c4..1cf305a9f8dd 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -170,7 +170,7 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
#define MLDV2_QQIC(value) MLDV2_EXP(0x80, 4, 3, value)
#define MLDV2_MRC(value) MLDV2_EXP(0x8000, 12, 3, value)
-#define IPV6_MLD_MAX_MSF 10
+#define IPV6_MLD_MAX_MSF 64
int sysctl_mld_max_msf = IPV6_MLD_MAX_MSF;
@@ -224,6 +224,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
mc_lst->ifindex = dev->ifindex;
mc_lst->sfmode = MCAST_EXCLUDE;
+ rwlock_init(&mc_lst->sflock);
mc_lst->sflist = NULL;
/*
@@ -360,6 +361,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
struct ip6_sf_socklist *psl;
int i, j, rv;
int leavegroup = 0;
+ int pmclocked = 0;
int err;
if (pgsr->gsr_group.ss_family != AF_INET6 ||
@@ -403,6 +405,9 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
pmc->sfmode = omode;
}
+ write_lock_bh(&pmc->sflock);
+ pmclocked = 1;
+
psl = pmc->sflist;
if (!add) {
if (!psl)
@@ -475,6 +480,8 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
/* update the interface list */
ip6_mc_add_src(idev, group, omode, 1, source, 1);
done:
+ if (pmclocked)
+ write_unlock_bh(&pmc->sflock);
read_unlock_bh(&ipv6_sk_mc_lock);
read_unlock_bh(&idev->lock);
in6_dev_put(idev);
@@ -510,6 +517,8 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
dev = idev->dev;
err = 0;
+ read_lock_bh(&ipv6_sk_mc_lock);
+
if (gsf->gf_fmode == MCAST_INCLUDE && gsf->gf_numsrc == 0) {
leavegroup = 1;
goto done;
@@ -549,6 +558,8 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
newpsl = NULL;
(void) ip6_mc_add_src(idev, group, gsf->gf_fmode, 0, NULL, 0);
}
+
+ write_lock_bh(&pmc->sflock);
psl = pmc->sflist;
if (psl) {
(void) ip6_mc_del_src(idev, group, pmc->sfmode,
@@ -558,8 +569,10 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
(void) ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0);
pmc->sflist = newpsl;
pmc->sfmode = gsf->gf_fmode;
+ write_unlock_bh(&pmc->sflock);
err = 0;
done:
+ read_unlock_bh(&ipv6_sk_mc_lock);
read_unlock_bh(&idev->lock);
in6_dev_put(idev);
dev_put(dev);
@@ -592,6 +605,11 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
dev = idev->dev;
err = -EADDRNOTAVAIL;
+ /*
+ * changes to the ipv6_mc_list require the socket lock and
+ * a read lock on ip6_sk_mc_lock. We have the socket lock,
+ * so reading the list is safe.
+ */
for (pmc=inet6->ipv6_mc_list; pmc; pmc=pmc->next) {
if (pmc->ifindex != gsf->gf_interface)
@@ -614,6 +632,10 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) {
return -EFAULT;
}
+ /* changes to psl require the socket lock, a read lock on
+ * on ipv6_sk_mc_lock and a write lock on pmc->sflock. We
+ * have the socket lock, so reading here is safe.
+ */
for (i=0; i<copycount; i++) {
struct sockaddr_in6 *psin6;
struct sockaddr_storage ss;
@@ -650,6 +672,7 @@ int inet6_mc_check(struct sock *sk, struct in6_addr *mc_addr,
read_unlock(&ipv6_sk_mc_lock);
return 1;
}
+ read_lock(&mc->sflock);
psl = mc->sflist;
if (!psl) {
rv = mc->sfmode == MCAST_EXCLUDE;
@@ -665,6 +688,7 @@ int inet6_mc_check(struct sock *sk, struct in6_addr *mc_addr,
if (mc->sfmode == MCAST_EXCLUDE && i < psl->sl_count)
rv = 0;
}
+ read_unlock(&mc->sflock);
read_unlock(&ipv6_sk_mc_lock);
return rv;
@@ -1068,7 +1092,8 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
ma->mca_flags |= MAF_TIMER_RUNNING;
}
-static void mld_marksources(struct ifmcaddr6 *pmc, int nsrcs,
+/* mark EXCLUDE-mode sources */
+static int mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs,
struct in6_addr *srcs)
{
struct ip6_sf_list *psf;
@@ -1078,13 +1103,53 @@ static void mld_marksources(struct ifmcaddr6 *pmc, int nsrcs,
for (psf=pmc->mca_sources; psf; psf=psf->sf_next) {
if (scount == nsrcs)
break;
- for (i=0; i<nsrcs; i++)
+ for (i=0; i<nsrcs; i++) {
+ /* skip inactive filters */
+ if (pmc->mca_sfcount[MCAST_INCLUDE] ||
+ pmc->mca_sfcount[MCAST_EXCLUDE] !=
+ psf->sf_count[MCAST_EXCLUDE])
+ continue;
+ if (ipv6_addr_equal(&srcs[i], &psf->sf_addr)) {
+ scount++;
+ break;
+ }
+ }
+ }
+ pmc->mca_flags &= ~MAF_GSQUERY;
+ if (scount == nsrcs) /* all sources excluded */
+ return 0;
+ return 1;
+}
+
+static int mld_marksources(struct ifmcaddr6 *pmc, int nsrcs,
+ struct in6_addr *srcs)
+{
+ struct ip6_sf_list *psf;
+ int i, scount;
+
+ if (pmc->mca_sfmode == MCAST_EXCLUDE)
+ return mld_xmarksources(pmc, nsrcs, srcs);
+
+ /* mark INCLUDE-mode sources */
+
+ scount = 0;
+ for (psf=pmc->mca_sources; psf; psf=psf->sf_next) {
+ if (scount == nsrcs)
+ break;
+ for (i=0; i<nsrcs; i++) {
if (ipv6_addr_equal(&srcs[i], &psf->sf_addr)) {
psf->sf_gsresp = 1;
scount++;
break;
}
+ }
+ }
+ if (!scount) {
+ pmc->mca_flags &= ~MAF_GSQUERY;
+ return 0;
}
+ pmc->mca_flags |= MAF_GSQUERY;
+ return 1;
}
int igmp6_event_query(struct sk_buff *skb)
@@ -1167,7 +1232,7 @@ int igmp6_event_query(struct sk_buff *skb)
/* mark sources to include, if group & source-specific */
if (mlh2->nsrcs != 0) {
if (!pskb_may_pull(skb, srcs_offset +
- mlh2->nsrcs * sizeof(struct in6_addr))) {
+ ntohs(mlh2->nsrcs) * sizeof(struct in6_addr))) {
in6_dev_put(idev);
return -EINVAL;
}
@@ -1203,10 +1268,9 @@ int igmp6_event_query(struct sk_buff *skb)
else
ma->mca_flags &= ~MAF_GSQUERY;
}
- if (ma->mca_flags & MAF_GSQUERY)
- mld_marksources(ma, ntohs(mlh2->nsrcs),
- mlh2->srcs);
- igmp6_group_queried(ma, max_delay);
+ if (!(ma->mca_flags & MAF_GSQUERY) ||
+ mld_marksources(ma, ntohs(mlh2->nsrcs), mlh2->srcs))
+ igmp6_group_queried(ma, max_delay);
spin_unlock_bh(&ma->mca_lock);
if (group_type != IPV6_ADDR_ANY)
break;
@@ -1281,7 +1345,18 @@ static int is_in(struct ifmcaddr6 *pmc, struct ip6_sf_list *psf, int type,
case MLD2_MODE_IS_EXCLUDE:
if (gdeleted || sdeleted)
return 0;
- return !((pmc->mca_flags & MAF_GSQUERY) && !psf->sf_gsresp);
+ if (!((pmc->mca_flags & MAF_GSQUERY) && !psf->sf_gsresp)) {
+ if (pmc->mca_sfmode == MCAST_INCLUDE)
+ return 1;
+ /* don't include if this source is excluded
+ * in all filters
+ */
+ if (psf->sf_count[MCAST_INCLUDE])
+ return 0;
+ return pmc->mca_sfcount[MCAST_EXCLUDE] ==
+ psf->sf_count[MCAST_EXCLUDE];
+ }
+ return 0;
case MLD2_CHANGE_TO_INCLUDE:
if (gdeleted || sdeleted)
return 0;
@@ -1450,7 +1525,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
struct mld2_report *pmr;
struct mld2_grec *pgr = NULL;
struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list;
- int scount, first, isquery, truncate;
+ int scount, stotal, first, isquery, truncate;
if (pmc->mca_flags & MAF_NOREPORT)
return skb;
@@ -1460,25 +1535,13 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
truncate = type == MLD2_MODE_IS_EXCLUDE ||
type == MLD2_CHANGE_TO_EXCLUDE;
+ stotal = scount = 0;
+
psf_list = sdeleted ? &pmc->mca_tomb : &pmc->mca_sources;
- if (!*psf_list) {
- if (type == MLD2_ALLOW_NEW_SOURCES ||
- type == MLD2_BLOCK_OLD_SOURCES)
- return skb;
- if (pmc->mca_crcount || isquery) {
- /* make sure we have room for group header and at
- * least one source.
- */
- if (skb && AVAILABLE(skb) < sizeof(struct mld2_grec)+
- sizeof(struct in6_addr)) {
- mld_sendpack(skb);
- skb = NULL; /* add_grhead will get a new one */
- }
- skb = add_grhead(skb, pmc, type, &pgr);
- }
- return skb;
- }
+ if (!*psf_list)
+ goto empty_source;
+
pmr = skb ? (struct mld2_report *)skb->h.raw : NULL;
/* EX and TO_EX get a fresh packet, if needed */
@@ -1491,7 +1554,6 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
}
}
first = 1;
- scount = 0;
psf_prev = NULL;
for (psf=*psf_list; psf; psf=psf_next) {
struct in6_addr *psrc;
@@ -1525,7 +1587,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
}
psrc = (struct in6_addr *)skb_put(skb, sizeof(*psrc));
*psrc = psf->sf_addr;
- scount++;
+ scount++; stotal++;
if ((type == MLD2_ALLOW_NEW_SOURCES ||
type == MLD2_BLOCK_OLD_SOURCES) && psf->sf_crcount) {
psf->sf_crcount--;
@@ -1540,6 +1602,21 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
}
psf_prev = psf;
}
+
+empty_source:
+ if (!stotal) {
+ if (type == MLD2_ALLOW_NEW_SOURCES ||
+ type == MLD2_BLOCK_OLD_SOURCES)
+ return skb;
+ if (pmc->mca_crcount || isquery) {
+ /* make sure we have room for group header */
+ if (skb && AVAILABLE(skb) < sizeof(struct mld2_grec)) {
+ mld_sendpack(skb);
+ skb = NULL; /* add_grhead will get a new one */
+ }
+ skb = add_grhead(skb, pmc, type, &pgr);
+ }
+ }
if (pgr)
pgr->grec_nsrcs = htons(scount);
@@ -1621,11 +1698,11 @@ static void mld_send_cr(struct inet6_dev *idev)
skb = add_grec(skb, pmc, dtype, 1, 1);
}
if (pmc->mca_crcount) {
- pmc->mca_crcount--;
if (pmc->mca_sfmode == MCAST_EXCLUDE) {
type = MLD2_CHANGE_TO_INCLUDE;
skb = add_grec(skb, pmc, type, 1, 0);
}
+ pmc->mca_crcount--;
if (pmc->mca_crcount == 0) {
mld_clear_zeros(&pmc->mca_tomb);
mld_clear_zeros(&pmc->mca_sources);
@@ -1659,12 +1736,12 @@ static void mld_send_cr(struct inet6_dev *idev)
/* filter mode changes */
if (pmc->mca_crcount) {
- pmc->mca_crcount--;
if (pmc->mca_sfmode == MCAST_EXCLUDE)
type = MLD2_CHANGE_TO_EXCLUDE;
else
type = MLD2_CHANGE_TO_INCLUDE;
skb = add_grec(skb, pmc, type, 0, 0);
+ pmc->mca_crcount--;
}
spin_unlock_bh(&pmc->mca_lock);
}
@@ -2023,6 +2100,9 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
{
int err;
+ /* callers have the socket lock and a write lock on ipv6_sk_mc_lock,
+ * so no other readers or writers of iml or its sflist
+ */
if (iml->sflist == 0) {
/* any-source empty exclude case */
return ip6_mc_del_src(idev, &iml->addr, iml->sfmode, 0, NULL, 0);
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index 060d61202412..04912f9b35c3 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -211,7 +211,7 @@ config IP6_NF_TARGET_REJECT
config IP6_NF_TARGET_NFQUEUE
tristate "NFQUEUE Target Support"
- depends on IP_NF_IPTABLES
+ depends on IP6_NF_IPTABLES
help
This Target replaced the old obsolete QUEUE target.
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 95d469271c4d..ea43ef1d94a7 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -15,6 +15,7 @@
* - new extension header parser code
*/
#include <linux/config.h>
+#include <linux/in.h>
#include <linux/skbuff.h>
#include <linux/kmod.h>
#include <linux/vmalloc.h>
@@ -86,11 +87,6 @@ static DECLARE_MUTEX(ip6t_mutex);
context stops packets coming through and allows user context to read
the counters or update the rules.
- To be cache friendly on SMP, we arrange them like so:
- [ n-entries ]
- ... cache-align padding ...
- [ n-entries ]
-
Hence the start of any table is given by get_table() below. */
/* The table itself */
@@ -108,20 +104,15 @@ struct ip6t_table_info
unsigned int underflow[NF_IP6_NUMHOOKS];
/* ip6t_entry tables: one per CPU */
- char entries[0] ____cacheline_aligned;
+ void *entries[NR_CPUS];
};
static LIST_HEAD(ip6t_target);
static LIST_HEAD(ip6t_match);
static LIST_HEAD(ip6t_tables);
+#define SET_COUNTER(c,b,p) do { (c).bcnt = (b); (c).pcnt = (p); } while(0)
#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
-#ifdef CONFIG_SMP
-#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
-#else
-#define TABLE_OFFSET(t,p) 0
-#endif
-
#if 0
#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
#define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
@@ -376,8 +367,7 @@ ip6t_do_table(struct sk_buff **pskb,
read_lock_bh(&table->lock);
IP_NF_ASSERT(table->valid_hooks & (1 << hook));
- table_base = (void *)table->private->entries
- + TABLE_OFFSET(table->private, smp_processor_id());
+ table_base = (void *)table->private->entries[smp_processor_id()];
e = get_entry(table_base, table->private->hook_entry[hook]);
#ifdef CONFIG_NETFILTER_DEBUG
@@ -649,7 +639,8 @@ unconditional(const struct ip6t_ip6 *ipv6)
/* Figures out from what hook each rule can be called: returns 0 if
there are loops. Puts hook bitmask in comefrom. */
static int
-mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
+mark_source_chains(struct ip6t_table_info *newinfo,
+ unsigned int valid_hooks, void *entry0)
{
unsigned int hook;
@@ -658,7 +649,7 @@ mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
unsigned int pos = newinfo->hook_entry[hook];
struct ip6t_entry *e
- = (struct ip6t_entry *)(newinfo->entries + pos);
+ = (struct ip6t_entry *)(entry0 + pos);
if (!(valid_hooks & (1 << hook)))
continue;
@@ -708,13 +699,13 @@ mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
goto next;
e = (struct ip6t_entry *)
- (newinfo->entries + pos);
+ (entry0 + pos);
} while (oldpos == pos + e->next_offset);
/* Move along one */
size = e->next_offset;
e = (struct ip6t_entry *)
- (newinfo->entries + pos + size);
+ (entry0 + pos + size);
e->counters.pcnt = pos;
pos += size;
} else {
@@ -731,7 +722,7 @@ mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
newpos = pos + e->next_offset;
}
e = (struct ip6t_entry *)
- (newinfo->entries + newpos);
+ (entry0 + newpos);
e->counters.pcnt = pos;
pos = newpos;
}
@@ -941,6 +932,7 @@ static int
translate_table(const char *name,
unsigned int valid_hooks,
struct ip6t_table_info *newinfo,
+ void *entry0,
unsigned int size,
unsigned int number,
const unsigned int *hook_entries,
@@ -961,11 +953,11 @@ translate_table(const char *name,
duprintf("translate_table: size %u\n", newinfo->size);
i = 0;
/* Walk through entries, checking offsets. */
- ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
check_entry_size_and_hooks,
newinfo,
- newinfo->entries,
- newinfo->entries + size,
+ entry0,
+ entry0 + size,
hook_entries, underflows, &i);
if (ret != 0)
return ret;
@@ -993,27 +985,24 @@ translate_table(const char *name,
}
}
- if (!mark_source_chains(newinfo, valid_hooks))
+ if (!mark_source_chains(newinfo, valid_hooks, entry0))
return -ELOOP;
/* Finally, each sanity check must pass */
i = 0;
- ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
check_entry, name, size, &i);
if (ret != 0) {
- IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ IP6T_ENTRY_ITERATE(entry0, newinfo->size,
cleanup_entry, &i);
return ret;
}
/* And one copy for every other CPU */
for_each_cpu(i) {
- if (i == 0)
- continue;
- memcpy(newinfo->entries + SMP_ALIGN(newinfo->size) * i,
- newinfo->entries,
- SMP_ALIGN(newinfo->size));
+ if (newinfo->entries[i] && newinfo->entries[i] != entry0)
+ memcpy(newinfo->entries[i], entry0, newinfo->size);
}
return ret;
@@ -1029,15 +1018,12 @@ replace_table(struct ip6t_table *table,
#ifdef CONFIG_NETFILTER_DEBUG
{
- struct ip6t_entry *table_base;
- unsigned int i;
+ int cpu;
- for_each_cpu(i) {
- table_base =
- (void *)newinfo->entries
- + TABLE_OFFSET(newinfo, i);
-
- table_base->comefrom = 0xdead57ac;
+ for_each_cpu(cpu) {
+ struct ip6t_entry *table_base = newinfo->entries[cpu];
+ if (table_base)
+ table_base->comefrom = 0xdead57ac;
}
}
#endif
@@ -1072,16 +1058,44 @@ add_entry_to_counter(const struct ip6t_entry *e,
return 0;
}
+static inline int
+set_entry_to_counter(const struct ip6t_entry *e,
+ struct ip6t_counters total[],
+ unsigned int *i)
+{
+ SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
+
+ (*i)++;
+ return 0;
+}
+
static void
get_counters(const struct ip6t_table_info *t,
struct ip6t_counters counters[])
{
unsigned int cpu;
unsigned int i;
+ unsigned int curcpu;
+
+ /* Instead of clearing (by a previous call to memset())
+ * the counters and using adds, we set the counters
+ * with data used by 'current' CPU
+ * We dont care about preemption here.
+ */
+ curcpu = raw_smp_processor_id();
+
+ i = 0;
+ IP6T_ENTRY_ITERATE(t->entries[curcpu],
+ t->size,
+ set_entry_to_counter,
+ counters,
+ &i);
for_each_cpu(cpu) {
+ if (cpu == curcpu)
+ continue;
i = 0;
- IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
+ IP6T_ENTRY_ITERATE(t->entries[cpu],
t->size,
add_entry_to_counter,
counters,
@@ -1098,6 +1112,7 @@ copy_entries_to_user(unsigned int total_size,
struct ip6t_entry *e;
struct ip6t_counters *counters;
int ret = 0;
+ void *loc_cpu_entry;
/* We need atomic snapshot of counters: rest doesn't change
(other than comefrom, which userspace doesn't care
@@ -1109,13 +1124,13 @@ copy_entries_to_user(unsigned int total_size,
return -ENOMEM;
/* First, sum counters... */
- memset(counters, 0, countersize);
write_lock_bh(&table->lock);
get_counters(table->private, counters);
write_unlock_bh(&table->lock);
- /* ... then copy entire thing from CPU 0... */
- if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
+ /* choose the copy that is on ourc node/cpu */
+ loc_cpu_entry = table->private->entries[raw_smp_processor_id()];
+ if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
ret = -EFAULT;
goto free_counters;
}
@@ -1127,7 +1142,7 @@ copy_entries_to_user(unsigned int total_size,
struct ip6t_entry_match *m;
struct ip6t_entry_target *t;
- e = (struct ip6t_entry *)(table->private->entries + off);
+ e = (struct ip6t_entry *)(loc_cpu_entry + off);
if (copy_to_user(userptr + off
+ offsetof(struct ip6t_entry, counters),
&counters[num],
@@ -1196,6 +1211,46 @@ get_entries(const struct ip6t_get_entries *entries,
return ret;
}
+static void free_table_info(struct ip6t_table_info *info)
+{
+ int cpu;
+ for_each_cpu(cpu) {
+ if (info->size <= PAGE_SIZE)
+ kfree(info->entries[cpu]);
+ else
+ vfree(info->entries[cpu]);
+ }
+ kfree(info);
+}
+
+static struct ip6t_table_info *alloc_table_info(unsigned int size)
+{
+ struct ip6t_table_info *newinfo;
+ int cpu;
+
+ newinfo = kzalloc(sizeof(struct ip6t_table_info), GFP_KERNEL);
+ if (!newinfo)
+ return NULL;
+
+ newinfo->size = size;
+
+ for_each_cpu(cpu) {
+ if (size <= PAGE_SIZE)
+ newinfo->entries[cpu] = kmalloc_node(size,
+ GFP_KERNEL,
+ cpu_to_node(cpu));
+ else
+ newinfo->entries[cpu] = vmalloc_node(size,
+ cpu_to_node(cpu));
+ if (newinfo->entries[cpu] == NULL) {
+ free_table_info(newinfo);
+ return NULL;
+ }
+ }
+
+ return newinfo;
+}
+
static int
do_replace(void __user *user, unsigned int len)
{
@@ -1204,6 +1259,7 @@ do_replace(void __user *user, unsigned int len)
struct ip6t_table *t;
struct ip6t_table_info *newinfo, *oldinfo;
struct ip6t_counters *counters;
+ void *loc_cpu_entry, *loc_cpu_old_entry;
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
return -EFAULT;
@@ -1212,13 +1268,13 @@ do_replace(void __user *user, unsigned int len)
if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
return -ENOMEM;
- newinfo = vmalloc(sizeof(struct ip6t_table_info)
- + SMP_ALIGN(tmp.size) *
- (highest_possible_processor_id()+1));
+ newinfo = alloc_table_info(tmp.size);
if (!newinfo)
return -ENOMEM;
- if (copy_from_user(newinfo->entries, user + sizeof(tmp),
+ /* choose the copy that is on our node/cpu */
+ loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
+ if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
tmp.size) != 0) {
ret = -EFAULT;
goto free_newinfo;
@@ -1229,10 +1285,9 @@ do_replace(void __user *user, unsigned int len)
ret = -ENOMEM;
goto free_newinfo;
}
- memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters));
ret = translate_table(tmp.name, tmp.valid_hooks,
- newinfo, tmp.size, tmp.num_entries,
+ newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
tmp.hook_entry, tmp.underflow);
if (ret != 0)
goto free_newinfo_counters;
@@ -1271,8 +1326,9 @@ do_replace(void __user *user, unsigned int len)
/* Get the old counters. */
get_counters(oldinfo, counters);
/* Decrease module usage counts and free resource */
- IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
- vfree(oldinfo);
+ loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
+ IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
+ free_table_info(oldinfo);
if (copy_to_user(tmp.counters, counters,
sizeof(struct ip6t_counters) * tmp.num_counters) != 0)
ret = -EFAULT;
@@ -1284,11 +1340,11 @@ do_replace(void __user *user, unsigned int len)
module_put(t->me);
up(&ip6t_mutex);
free_newinfo_counters_untrans:
- IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
+ IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
free_newinfo_counters:
vfree(counters);
free_newinfo:
- vfree(newinfo);
+ free_table_info(newinfo);
return ret;
}
@@ -1321,6 +1377,7 @@ do_add_counters(void __user *user, unsigned int len)
struct ip6t_counters_info tmp, *paddc;
struct ip6t_table *t;
int ret = 0;
+ void *loc_cpu_entry;
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
return -EFAULT;
@@ -1350,7 +1407,9 @@ do_add_counters(void __user *user, unsigned int len)
}
i = 0;
- IP6T_ENTRY_ITERATE(t->private->entries,
+ /* Choose the copy that is on our node */
+ loc_cpu_entry = t->private->entries[smp_processor_id()];
+ IP6T_ENTRY_ITERATE(loc_cpu_entry,
t->private->size,
add_counter_to_entry,
paddc->counters,
@@ -1543,28 +1602,29 @@ int ip6t_register_table(struct ip6t_table *table,
struct ip6t_table_info *newinfo;
static struct ip6t_table_info bootstrap
= { 0, 0, 0, { 0 }, { 0 }, { } };
+ void *loc_cpu_entry;
- newinfo = vmalloc(sizeof(struct ip6t_table_info)
- + SMP_ALIGN(repl->size) *
- (highest_possible_processor_id()+1));
+ newinfo = alloc_table_info(repl->size);
if (!newinfo)
return -ENOMEM;
- memcpy(newinfo->entries, repl->entries, repl->size);
+ /* choose the copy on our node/cpu */
+ loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
+ memcpy(loc_cpu_entry, repl->entries, repl->size);
ret = translate_table(table->name, table->valid_hooks,
- newinfo, repl->size,
+ newinfo, loc_cpu_entry, repl->size,
repl->num_entries,
repl->hook_entry,
repl->underflow);
if (ret != 0) {
- vfree(newinfo);
+ free_table_info(newinfo);
return ret;
}
ret = down_interruptible(&ip6t_mutex);
if (ret != 0) {
- vfree(newinfo);
+ free_table_info(newinfo);
return ret;
}
@@ -1593,20 +1653,23 @@ int ip6t_register_table(struct ip6t_table *table,
return ret;
free_unlock:
- vfree(newinfo);
+ free_table_info(newinfo);
goto unlock;
}
void ip6t_unregister_table(struct ip6t_table *table)
{
+ void *loc_cpu_entry;
+
down(&ip6t_mutex);
LIST_DELETE(&ip6t_tables, table);
up(&ip6t_mutex);
/* Decrease module usage counts and free resources */
- IP6T_ENTRY_ITERATE(table->private->entries, table->private->size,
+ loc_cpu_entry = table->private->entries[raw_smp_processor_id()];
+ IP6T_ENTRY_ITERATE(loc_cpu_entry, table->private->size,
cleanup_entry, NULL);
- vfree(table->private);
+ free_table_info(table->private);
}
/* Returns 1 if the port is matched by the range, 0 otherwise */
diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c
index 0cd1d1bd9033..ae4653bfd654 100644
--- a/net/ipv6/netfilter/ip6t_LOG.c
+++ b/net/ipv6/netfilter/ip6t_LOG.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/skbuff.h>
+#include <linux/if_arp.h>
#include <linux/ip.h>
#include <linux/spinlock.h>
#include <linux/icmpv6.h>
diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c
index dde37793d20b..268918d5deea 100644
--- a/net/ipv6/netfilter/ip6t_ah.c
+++ b/net/ipv6/netfilter/ip6t_ah.c
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/skbuff.h>
+#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/types.h>
#include <net/checksum.h>
diff --git a/net/ipv6/netfilter/ip6t_esp.c b/net/ipv6/netfilter/ip6t_esp.c
index 24bc0cde43a1..65937de1b58c 100644
--- a/net/ipv6/netfilter/ip6t_esp.c
+++ b/net/ipv6/netfilter/ip6t_esp.c
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/skbuff.h>
+#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/types.h>
#include <net/checksum.h>
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index c2c52af9e560..f3e5ffbd592f 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -98,7 +98,7 @@ struct nf_ct_frag6_queue
#define FRAG6Q_HASHSZ 64
static struct nf_ct_frag6_queue *nf_ct_frag6_hash[FRAG6Q_HASHSZ];
-static rwlock_t nf_ct_frag6_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(nf_ct_frag6_lock);
static u32 nf_ct_frag6_hash_rnd;
static LIST_HEAD(nf_ct_frag6_lru_list);
int nf_ct_frag6_nqueues = 0;
@@ -371,7 +371,7 @@ nf_ct_frag6_create(unsigned int hash, u32 id, struct in6_addr *src, struct
init_timer(&fq->timer);
fq->timer.function = nf_ct_frag6_expire;
fq->timer.data = (long) fq;
- fq->lock = SPIN_LOCK_UNLOCKED;
+ spin_lock_init(&fq->lock);
atomic_set(&fq->refcnt, 1);
return nf_ct_frag6_intern(hash, fq);
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index a66900cda2af..66f1d12ea578 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -32,6 +32,7 @@
#include <linux/icmpv6.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
+#include <linux/skbuff.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
#include <asm/bug.h>
@@ -433,25 +434,14 @@ out:
return err;
csum_copy_err:
- /* Clear queue. */
- if (flags&MSG_PEEK) {
- int clear = 0;
- spin_lock_bh(&sk->sk_receive_queue.lock);
- if (skb == skb_peek(&sk->sk_receive_queue)) {
- __skb_unlink(skb, &sk->sk_receive_queue);
- clear = 1;
- }
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- if (clear)
- kfree_skb(skb);
- }
+ skb_kill_datagram(sk, skb, flags);
/* Error for blocking case is chosen to masquerade
as some normal condition.
*/
err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
/* FIXME: increment a raw6 drops counter here */
- goto out_free;
+ goto out;
}
static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index a7a537b50595..66140f13d119 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -413,11 +413,14 @@ static struct rt6_info *rt6_cow(struct rt6_info *ort, struct in6_addr *daddr,
rt = ip6_rt_copy(ort);
if (rt) {
- ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
-
- if (!(rt->rt6i_flags&RTF_GATEWAY))
+ if (!(rt->rt6i_flags&RTF_GATEWAY)) {
+ if (rt->rt6i_dst.plen != 128 &&
+ ipv6_addr_equal(&rt->rt6i_dst.addr, daddr))
+ rt->rt6i_flags |= RTF_ANYCAST;
ipv6_addr_copy(&rt->rt6i_gateway, daddr);
+ }
+ ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
rt->rt6i_dst.plen = 128;
rt->rt6i_flags |= RTF_CACHE;
rt->u.dst.flags |= DST_HOST;
@@ -829,7 +832,7 @@ int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh,
}
rt->u.dst.obsolete = -1;
- rt->rt6i_expires = clock_t_to_jiffies(rtmsg->rtmsg_info);
+ rt->rt6i_expires = jiffies + clock_t_to_jiffies(rtmsg->rtmsg_info);
if (nlh && (r = NLMSG_DATA(nlh))) {
rt->rt6i_protocol = r->rtm_protocol;
} else {
@@ -1413,7 +1416,9 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
rt->u.dst.obsolete = -1;
rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
- if (!anycast)
+ if (anycast)
+ rt->rt6i_flags |= RTF_ANYCAST;
+ else
rt->rt6i_flags |= RTF_LOCAL;
rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
if (rt->rt6i_nexthop == NULL) {
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 8827389abaf7..2947bc56d8a0 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -48,6 +48,7 @@
#include <net/tcp.h>
#include <net/ndisc.h>
#include <net/inet6_hashtables.h>
+#include <net/inet6_connection_sock.h>
#include <net/ipv6.h>
#include <net/transp_v6.h>
#include <net/addrconf.h>
@@ -59,6 +60,7 @@
#include <net/addrconf.h>
#include <net/snmp.h>
#include <net/dsfield.h>
+#include <net/timewait_sock.h>
#include <asm/uaccess.h>
@@ -67,224 +69,33 @@
static void tcp_v6_send_reset(struct sk_buff *skb);
static void tcp_v6_reqsk_send_ack(struct sk_buff *skb, struct request_sock *req);
-static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
+static void tcp_v6_send_check(struct sock *sk, int len,
struct sk_buff *skb);
static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
-static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok);
-static struct tcp_func ipv6_mapped;
-static struct tcp_func ipv6_specific;
+static struct inet_connection_sock_af_ops ipv6_mapped;
+static struct inet_connection_sock_af_ops ipv6_specific;
-static inline int tcp_v6_bind_conflict(const struct sock *sk,
- const struct inet_bind_bucket *tb)
-{
- const struct sock *sk2;
- const struct hlist_node *node;
-
- /* We must walk the whole port owner list in this case. -DaveM */
- sk_for_each_bound(sk2, node, &tb->owners) {
- if (sk != sk2 &&
- (!sk->sk_bound_dev_if ||
- !sk2->sk_bound_dev_if ||
- sk->sk_bound_dev_if == sk2->sk_bound_dev_if) &&
- (!sk->sk_reuse || !sk2->sk_reuse ||
- sk2->sk_state == TCP_LISTEN) &&
- ipv6_rcv_saddr_equal(sk, sk2))
- break;
- }
-
- return node != NULL;
-}
-
-/* Grrr, addr_type already calculated by caller, but I don't want
- * to add some silly "cookie" argument to this method just for that.
- * But it doesn't matter, the recalculation is in the rarest path
- * this function ever takes.
- */
static int tcp_v6_get_port(struct sock *sk, unsigned short snum)
{
- struct inet_bind_hashbucket *head;
- struct inet_bind_bucket *tb;
- struct hlist_node *node;
- int ret;
-
- local_bh_disable();
- if (snum == 0) {
- int low = sysctl_local_port_range[0];
- int high = sysctl_local_port_range[1];
- int remaining = (high - low) + 1;
- int rover = net_random() % (high - low) + low;
-
- do {
- head = &tcp_hashinfo.bhash[inet_bhashfn(rover, tcp_hashinfo.bhash_size)];
- spin_lock(&head->lock);
- inet_bind_bucket_for_each(tb, node, &head->chain)
- if (tb->port == rover)
- goto next;
- break;
- next:
- spin_unlock(&head->lock);
- if (++rover > high)
- rover = low;
- } while (--remaining > 0);
-
- /* Exhausted local port range during search? It is not
- * possible for us to be holding one of the bind hash
- * locks if this test triggers, because if 'remaining'
- * drops to zero, we broke out of the do/while loop at
- * the top level, not from the 'break;' statement.
- */
- ret = 1;
- if (unlikely(remaining <= 0))
- goto fail;
-
- /* OK, here is the one we will use. */
- snum = rover;
- } else {
- head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)];
- spin_lock(&head->lock);
- inet_bind_bucket_for_each(tb, node, &head->chain)
- if (tb->port == snum)
- goto tb_found;
- }
- tb = NULL;
- goto tb_not_found;
-tb_found:
- if (tb && !hlist_empty(&tb->owners)) {
- if (tb->fastreuse > 0 && sk->sk_reuse &&
- sk->sk_state != TCP_LISTEN) {
- goto success;
- } else {
- ret = 1;
- if (tcp_v6_bind_conflict(sk, tb))
- goto fail_unlock;
- }
- }
-tb_not_found:
- ret = 1;
- if (tb == NULL) {
- tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, snum);
- if (tb == NULL)
- goto fail_unlock;
- }
- if (hlist_empty(&tb->owners)) {
- if (sk->sk_reuse && sk->sk_state != TCP_LISTEN)
- tb->fastreuse = 1;
- else
- tb->fastreuse = 0;
- } else if (tb->fastreuse &&
- (!sk->sk_reuse || sk->sk_state == TCP_LISTEN))
- tb->fastreuse = 0;
-
-success:
- if (!inet_csk(sk)->icsk_bind_hash)
- inet_bind_hash(sk, tb, snum);
- BUG_TRAP(inet_csk(sk)->icsk_bind_hash == tb);
- ret = 0;
-
-fail_unlock:
- spin_unlock(&head->lock);
-fail:
- local_bh_enable();
- return ret;
-}
-
-static __inline__ void __tcp_v6_hash(struct sock *sk)
-{
- struct hlist_head *list;
- rwlock_t *lock;
-
- BUG_TRAP(sk_unhashed(sk));
-
- if (sk->sk_state == TCP_LISTEN) {
- list = &tcp_hashinfo.listening_hash[inet_sk_listen_hashfn(sk)];
- lock = &tcp_hashinfo.lhash_lock;
- inet_listen_wlock(&tcp_hashinfo);
- } else {
- unsigned int hash;
- sk->sk_hash = hash = inet6_sk_ehashfn(sk);
- hash &= (tcp_hashinfo.ehash_size - 1);
- list = &tcp_hashinfo.ehash[hash].chain;
- lock = &tcp_hashinfo.ehash[hash].lock;
- write_lock(lock);
- }
-
- __sk_add_node(sk, list);
- sock_prot_inc_use(sk->sk_prot);
- write_unlock(lock);
+ return inet_csk_get_port(&tcp_hashinfo, sk, snum,
+ inet6_csk_bind_conflict);
}
-
static void tcp_v6_hash(struct sock *sk)
{
if (sk->sk_state != TCP_CLOSE) {
- struct tcp_sock *tp = tcp_sk(sk);
-
- if (tp->af_specific == &ipv6_mapped) {
+ if (inet_csk(sk)->icsk_af_ops == &ipv6_mapped) {
tcp_prot.hash(sk);
return;
}
local_bh_disable();
- __tcp_v6_hash(sk);
+ __inet6_hash(&tcp_hashinfo, sk);
local_bh_enable();
}
}
-/*
- * Open request hash tables.
- */
-
-static u32 tcp_v6_synq_hash(const struct in6_addr *raddr, const u16 rport, const u32 rnd)
-{
- u32 a, b, c;
-
- a = raddr->s6_addr32[0];
- b = raddr->s6_addr32[1];
- c = raddr->s6_addr32[2];
-
- a += JHASH_GOLDEN_RATIO;
- b += JHASH_GOLDEN_RATIO;
- c += rnd;
- __jhash_mix(a, b, c);
-
- a += raddr->s6_addr32[3];
- b += (u32) rport;
- __jhash_mix(a, b, c);
-
- return c & (TCP_SYNQ_HSIZE - 1);
-}
-
-static struct request_sock *tcp_v6_search_req(const struct sock *sk,
- struct request_sock ***prevp,
- __u16 rport,
- struct in6_addr *raddr,
- struct in6_addr *laddr,
- int iif)
-{
- const struct inet_connection_sock *icsk = inet_csk(sk);
- struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
- struct request_sock *req, **prev;
-
- for (prev = &lopt->syn_table[tcp_v6_synq_hash(raddr, rport, lopt->hash_rnd)];
- (req = *prev) != NULL;
- prev = &req->dl_next) {
- const struct tcp6_request_sock *treq = tcp6_rsk(req);
-
- if (inet_rsk(req)->rmt_port == rport &&
- req->rsk_ops->family == AF_INET6 &&
- ipv6_addr_equal(&treq->rmt_addr, raddr) &&
- ipv6_addr_equal(&treq->loc_addr, laddr) &&
- (!treq->iif || treq->iif == iif)) {
- BUG_TRAP(req->sk == NULL);
- *prevp = prev;
- return req;
- }
- }
-
- return NULL;
-}
-
static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len,
struct in6_addr *saddr,
struct in6_addr *daddr,
@@ -308,195 +119,12 @@ static __u32 tcp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
}
}
-static int __tcp_v6_check_established(struct sock *sk, const __u16 lport,
- struct inet_timewait_sock **twp)
-{
- struct inet_sock *inet = inet_sk(sk);
- const struct ipv6_pinfo *np = inet6_sk(sk);
- const struct in6_addr *daddr = &np->rcv_saddr;
- const struct in6_addr *saddr = &np->daddr;
- const int dif = sk->sk_bound_dev_if;
- const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
- unsigned int hash = inet6_ehashfn(daddr, inet->num, saddr, inet->dport);
- struct inet_ehash_bucket *head = inet_ehash_bucket(&tcp_hashinfo, hash);
- struct sock *sk2;
- const struct hlist_node *node;
- struct inet_timewait_sock *tw;
-
- prefetch(head->chain.first);
- write_lock(&head->lock);
-
- /* Check TIME-WAIT sockets first. */
- sk_for_each(sk2, node, &(head + tcp_hashinfo.ehash_size)->chain) {
- const struct tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk2);
-
- tw = inet_twsk(sk2);
-
- if(*((__u32 *)&(tw->tw_dport)) == ports &&
- sk2->sk_family == PF_INET6 &&
- ipv6_addr_equal(&tcp6tw->tw_v6_daddr, saddr) &&
- ipv6_addr_equal(&tcp6tw->tw_v6_rcv_saddr, daddr) &&
- sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
- const struct tcp_timewait_sock *tcptw = tcp_twsk(sk2);
- struct tcp_sock *tp = tcp_sk(sk);
-
- if (tcptw->tw_ts_recent_stamp &&
- (!twp ||
- (sysctl_tcp_tw_reuse &&
- xtime.tv_sec - tcptw->tw_ts_recent_stamp > 1))) {
- /* See comment in tcp_ipv4.c */
- tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
- if (!tp->write_seq)
- tp->write_seq = 1;
- tp->rx_opt.ts_recent = tcptw->tw_ts_recent;
- tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
- sock_hold(sk2);
- goto unique;
- } else
- goto not_unique;
- }
- }
- tw = NULL;
-
- /* And established part... */
- sk_for_each(sk2, node, &head->chain) {
- if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
- goto not_unique;
- }
-
-unique:
- BUG_TRAP(sk_unhashed(sk));
- __sk_add_node(sk, &head->chain);
- sk->sk_hash = hash;
- sock_prot_inc_use(sk->sk_prot);
- write_unlock(&head->lock);
-
- if (twp) {
- *twp = tw;
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
- } else if (tw) {
- /* Silly. Should hash-dance instead... */
- inet_twsk_deschedule(tw, &tcp_death_row);
- NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
-
- inet_twsk_put(tw);
- }
- return 0;
-
-not_unique:
- write_unlock(&head->lock);
- return -EADDRNOTAVAIL;
-}
-
-static inline u32 tcpv6_port_offset(const struct sock *sk)
-{
- const struct inet_sock *inet = inet_sk(sk);
- const struct ipv6_pinfo *np = inet6_sk(sk);
-
- return secure_tcpv6_port_ephemeral(np->rcv_saddr.s6_addr32,
- np->daddr.s6_addr32,
- inet->dport);
-}
-
-static int tcp_v6_hash_connect(struct sock *sk)
-{
- unsigned short snum = inet_sk(sk)->num;
- struct inet_bind_hashbucket *head;
- struct inet_bind_bucket *tb;
- int ret;
-
- if (!snum) {
- int low = sysctl_local_port_range[0];
- int high = sysctl_local_port_range[1];
- int range = high - low;
- int i;
- int port;
- static u32 hint;
- u32 offset = hint + tcpv6_port_offset(sk);
- struct hlist_node *node;
- struct inet_timewait_sock *tw = NULL;
-
- local_bh_disable();
- for (i = 1; i <= range; i++) {
- port = low + (i + offset) % range;
- head = &tcp_hashinfo.bhash[inet_bhashfn(port, tcp_hashinfo.bhash_size)];
- spin_lock(&head->lock);
-
- /* Does not bother with rcv_saddr checks,
- * because the established check is already
- * unique enough.
- */
- inet_bind_bucket_for_each(tb, node, &head->chain) {
- if (tb->port == port) {
- BUG_TRAP(!hlist_empty(&tb->owners));
- if (tb->fastreuse >= 0)
- goto next_port;
- if (!__tcp_v6_check_established(sk,
- port,
- &tw))
- goto ok;
- goto next_port;
- }
- }
-
- tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, port);
- if (!tb) {
- spin_unlock(&head->lock);
- break;
- }
- tb->fastreuse = -1;
- goto ok;
-
- next_port:
- spin_unlock(&head->lock);
- }
- local_bh_enable();
-
- return -EADDRNOTAVAIL;
-
-ok:
- hint += i;
-
- /* Head lock still held and bh's disabled */
- inet_bind_hash(sk, tb, port);
- if (sk_unhashed(sk)) {
- inet_sk(sk)->sport = htons(port);
- __tcp_v6_hash(sk);
- }
- spin_unlock(&head->lock);
-
- if (tw) {
- inet_twsk_deschedule(tw, &tcp_death_row);
- inet_twsk_put(tw);
- }
-
- ret = 0;
- goto out;
- }
-
- head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)];
- tb = inet_csk(sk)->icsk_bind_hash;
- spin_lock_bh(&head->lock);
-
- if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
- __tcp_v6_hash(sk);
- spin_unlock_bh(&head->lock);
- return 0;
- } else {
- spin_unlock(&head->lock);
- /* No definite answer... Walk to established hash table */
- ret = __tcp_v6_check_established(sk, snum, NULL);
-out:
- local_bh_enable();
- return ret;
- }
-}
-
static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len)
{
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
- struct inet_sock *inet = inet_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
struct tcp_sock *tp = tcp_sk(sk);
struct in6_addr *saddr = NULL, *final_p = NULL, final;
@@ -571,7 +199,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
*/
if (addr_type == IPV6_ADDR_MAPPED) {
- u32 exthdrlen = tp->ext_header_len;
+ u32 exthdrlen = icsk->icsk_ext_hdr_len;
struct sockaddr_in sin;
SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
@@ -583,14 +211,14 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
sin.sin_port = usin->sin6_port;
sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
- tp->af_specific = &ipv6_mapped;
+ icsk->icsk_af_ops = &ipv6_mapped;
sk->sk_backlog_rcv = tcp_v4_do_rcv;
err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
if (err) {
- tp->ext_header_len = exthdrlen;
- tp->af_specific = &ipv6_specific;
+ icsk->icsk_ext_hdr_len = exthdrlen;
+ icsk->icsk_af_ops = &ipv6_specific;
sk->sk_backlog_rcv = tcp_v6_do_rcv;
goto failure;
} else {
@@ -643,16 +271,17 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
sk->sk_route_caps = dst->dev->features &
~(NETIF_F_IP_CSUM | NETIF_F_TSO);
- tp->ext_header_len = 0;
+ icsk->icsk_ext_hdr_len = 0;
if (np->opt)
- tp->ext_header_len = np->opt->opt_flen + np->opt->opt_nflen;
+ icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
+ np->opt->opt_nflen);
tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
inet->dport = usin->sin6_port;
tcp_set_state(sk, TCP_SYN_SENT);
- err = tcp_v6_hash_connect(sk);
+ err = inet6_hash_connect(&tcp_death_row, sk);
if (err)
goto late_failure;
@@ -758,7 +387,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
} else
dst_hold(dst);
- if (tp->pmtu_cookie > dst_mtu(dst)) {
+ if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) {
tcp_sync_mss(sk, dst_mtu(dst));
tcp_simple_retransmit(sk);
} /* else let the usual retransmit timer handle it */
@@ -775,8 +404,8 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (sock_owned_by_user(sk))
goto out;
- req = tcp_v6_search_req(sk, &prev, th->dest, &hdr->daddr,
- &hdr->saddr, inet6_iif(skb));
+ req = inet6_csk_search_req(sk, &prev, th->dest, &hdr->daddr,
+ &hdr->saddr, inet6_iif(skb));
if (!req)
goto out;
@@ -822,7 +451,7 @@ out:
static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
struct dst_entry *dst)
{
- struct tcp6_request_sock *treq = tcp6_rsk(req);
+ struct inet6_request_sock *treq = inet6_rsk(req);
struct ipv6_pinfo *np = inet6_sk(sk);
struct sk_buff * skb;
struct ipv6_txoptions *opt = NULL;
@@ -888,8 +517,8 @@ done:
static void tcp_v6_reqsk_destructor(struct request_sock *req)
{
- if (tcp6_rsk(req)->pktopts)
- kfree_skb(tcp6_rsk(req)->pktopts);
+ if (inet6_rsk(req)->pktopts)
+ kfree_skb(inet6_rsk(req)->pktopts);
}
static struct request_sock_ops tcp6_request_sock_ops = {
@@ -901,26 +530,15 @@ static struct request_sock_ops tcp6_request_sock_ops = {
.send_reset = tcp_v6_send_reset
};
-static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb)
-{
- struct ipv6_pinfo *np = inet6_sk(sk);
- struct inet6_skb_parm *opt = IP6CB(skb);
-
- if (np->rxopt.all) {
- if ((opt->hop && (np->rxopt.bits.hopopts || np->rxopt.bits.ohopopts)) ||
- ((IPV6_FLOWINFO_MASK & *(u32*)skb->nh.raw) && np->rxopt.bits.rxflow) ||
- (opt->srcrt && (np->rxopt.bits.srcrt || np->rxopt.bits.osrcrt)) ||
- ((opt->dst1 || opt->dst0) && (np->rxopt.bits.dstopts || np->rxopt.bits.odstopts)))
- return 1;
- }
- return 0;
-}
-
+static struct timewait_sock_ops tcp6_timewait_sock_ops = {
+ .twsk_obj_size = sizeof(struct tcp6_timewait_sock),
+ .twsk_unique = tcp_twsk_unique,
+};
-static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
- struct sk_buff *skb)
+static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
{
struct ipv6_pinfo *np = inet6_sk(sk);
+ struct tcphdr *th = skb->h.th;
if (skb->ip_summed == CHECKSUM_HW) {
th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 0);
@@ -1091,8 +709,9 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
struct sock *nsk;
/* Find possible connection requests. */
- req = tcp_v6_search_req(sk, &prev, th->source, &skb->nh.ipv6h->saddr,
- &skb->nh.ipv6h->daddr, inet6_iif(skb));
+ req = inet6_csk_search_req(sk, &prev, th->source,
+ &skb->nh.ipv6h->saddr,
+ &skb->nh.ipv6h->daddr, inet6_iif(skb));
if (req)
return tcp_check_req(sk, skb, req, prev);
@@ -1116,23 +735,12 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
return sk;
}
-static void tcp_v6_synq_add(struct sock *sk, struct request_sock *req)
-{
- struct inet_connection_sock *icsk = inet_csk(sk);
- struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
- const u32 h = tcp_v6_synq_hash(&tcp6_rsk(req)->rmt_addr, inet_rsk(req)->rmt_port, lopt->hash_rnd);
-
- reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, TCP_TIMEOUT_INIT);
- inet_csk_reqsk_queue_added(sk, TCP_TIMEOUT_INIT);
-}
-
-
/* FIXME: this is substantially similar to the ipv4 code.
* Can some kind of merge be done? -- erics
*/
static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
{
- struct tcp6_request_sock *treq;
+ struct inet6_request_sock *treq;
struct ipv6_pinfo *np = inet6_sk(sk);
struct tcp_options_received tmp_opt;
struct tcp_sock *tp = tcp_sk(sk);
@@ -1157,7 +765,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
goto drop;
- req = reqsk_alloc(&tcp6_request_sock_ops);
+ req = inet6_reqsk_alloc(&tcp6_request_sock_ops);
if (req == NULL)
goto drop;
@@ -1170,7 +778,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
tcp_openreq_init(req, &tmp_opt, skb);
- treq = tcp6_rsk(req);
+ treq = inet6_rsk(req);
ipv6_addr_copy(&treq->rmt_addr, &skb->nh.ipv6h->saddr);
ipv6_addr_copy(&treq->loc_addr, &skb->nh.ipv6h->daddr);
TCP_ECN_create_request(req, skb->h.th);
@@ -1196,8 +804,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (tcp_v6_send_synack(sk, req, NULL))
goto drop;
- tcp_v6_synq_add(sk, req);
-
+ inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
return 0;
drop:
@@ -1212,7 +819,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct dst_entry *dst)
{
- struct tcp6_request_sock *treq = tcp6_rsk(req);
+ struct inet6_request_sock *treq = inet6_rsk(req);
struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
struct tcp6_sock *newtcp6sk;
struct inet_sock *newinet;
@@ -1247,7 +854,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
ipv6_addr_copy(&newnp->rcv_saddr, &newnp->saddr);
- newtp->af_specific = &ipv6_mapped;
+ inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
newsk->sk_backlog_rcv = tcp_v4_do_rcv;
newnp->pktoptions = NULL;
newnp->opt = NULL;
@@ -1261,10 +868,10 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
*/
/* It is tricky place. Until this moment IPv4 tcp
- worked with IPv6 af_tcp.af_specific.
+ worked with IPv6 icsk.icsk_af_ops.
Sync it now.
*/
- tcp_sync_mss(newsk, newtp->pmtu_cookie);
+ tcp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
return newsk;
}
@@ -1371,10 +978,10 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
sock_kfree_s(sk, opt, opt->tot_len);
}
- newtp->ext_header_len = 0;
+ inet_csk(newsk)->icsk_ext_hdr_len = 0;
if (newnp->opt)
- newtp->ext_header_len = newnp->opt->opt_nflen +
- newnp->opt->opt_flen;
+ inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
+ newnp->opt->opt_flen);
tcp_sync_mss(newsk, dst_mtu(dst));
newtp->advmss = dst_metric(dst, RTAX_ADVMSS);
@@ -1382,7 +989,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newinet->daddr = newinet->saddr = newinet->rcv_saddr = LOOPBACK4_IPV6;
- __tcp_v6_hash(newsk);
+ __inet6_hash(&tcp_hashinfo, newsk);
inet_inherit_port(&tcp_hashinfo, sk, newsk);
return newsk;
@@ -1679,139 +1286,16 @@ do_time_wait:
goto discard_it;
}
-static int tcp_v6_rebuild_header(struct sock *sk)
-{
- int err;
- struct dst_entry *dst;
- struct ipv6_pinfo *np = inet6_sk(sk);
-
- dst = __sk_dst_check(sk, np->dst_cookie);
-
- if (dst == NULL) {
- struct inet_sock *inet = inet_sk(sk);
- struct in6_addr *final_p = NULL, final;
- struct flowi fl;
-
- memset(&fl, 0, sizeof(fl));
- fl.proto = IPPROTO_TCP;
- ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
- ipv6_addr_copy(&fl.fl6_src, &np->saddr);
- fl.fl6_flowlabel = np->flow_label;
- fl.oif = sk->sk_bound_dev_if;
- fl.fl_ip_dport = inet->dport;
- fl.fl_ip_sport = inet->sport;
-
- if (np->opt && np->opt->srcrt) {
- struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
- ipv6_addr_copy(&final, &fl.fl6_dst);
- ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
- final_p = &final;
- }
-
- err = ip6_dst_lookup(sk, &dst, &fl);
- if (err) {
- sk->sk_route_caps = 0;
- return err;
- }
- if (final_p)
- ipv6_addr_copy(&fl.fl6_dst, final_p);
-
- if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
- sk->sk_err_soft = -err;
- return err;
- }
-
- ip6_dst_store(sk, dst, NULL);
- sk->sk_route_caps = dst->dev->features &
- ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
- }
-
- return 0;
-}
-
-static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok)
-{
- struct sock *sk = skb->sk;
- struct inet_sock *inet = inet_sk(sk);
- struct ipv6_pinfo *np = inet6_sk(sk);
- struct flowi fl;
- struct dst_entry *dst;
- struct in6_addr *final_p = NULL, final;
-
- memset(&fl, 0, sizeof(fl));
- fl.proto = IPPROTO_TCP;
- ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
- ipv6_addr_copy(&fl.fl6_src, &np->saddr);
- fl.fl6_flowlabel = np->flow_label;
- IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel);
- fl.oif = sk->sk_bound_dev_if;
- fl.fl_ip_sport = inet->sport;
- fl.fl_ip_dport = inet->dport;
-
- if (np->opt && np->opt->srcrt) {
- struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
- ipv6_addr_copy(&final, &fl.fl6_dst);
- ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
- final_p = &final;
- }
-
- dst = __sk_dst_check(sk, np->dst_cookie);
-
- if (dst == NULL) {
- int err = ip6_dst_lookup(sk, &dst, &fl);
-
- if (err) {
- sk->sk_err_soft = -err;
- return err;
- }
-
- if (final_p)
- ipv6_addr_copy(&fl.fl6_dst, final_p);
-
- if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
- sk->sk_route_caps = 0;
- return err;
- }
-
- ip6_dst_store(sk, dst, NULL);
- sk->sk_route_caps = dst->dev->features &
- ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
- }
-
- skb->dst = dst_clone(dst);
-
- /* Restore final destination back after routing done */
- ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
-
- return ip6_xmit(sk, skb, &fl, np->opt, 0);
-}
-
-static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
-{
- struct ipv6_pinfo *np = inet6_sk(sk);
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) uaddr;
-
- sin6->sin6_family = AF_INET6;
- ipv6_addr_copy(&sin6->sin6_addr, &np->daddr);
- sin6->sin6_port = inet_sk(sk)->dport;
- /* We do not store received flowlabel for TCP */
- sin6->sin6_flowinfo = 0;
- sin6->sin6_scope_id = 0;
- if (sk->sk_bound_dev_if &&
- ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
- sin6->sin6_scope_id = sk->sk_bound_dev_if;
-}
-
static int tcp_v6_remember_stamp(struct sock *sk)
{
/* Alas, not yet... */
return 0;
}
-static struct tcp_func ipv6_specific = {
- .queue_xmit = tcp_v6_xmit,
+static struct inet_connection_sock_af_ops ipv6_specific = {
+ .queue_xmit = inet6_csk_xmit,
.send_check = tcp_v6_send_check,
- .rebuild_header = tcp_v6_rebuild_header,
+ .rebuild_header = inet6_sk_rebuild_header,
.conn_request = tcp_v6_conn_request,
.syn_recv_sock = tcp_v6_syn_recv_sock,
.remember_stamp = tcp_v6_remember_stamp,
@@ -1819,7 +1303,7 @@ static struct tcp_func ipv6_specific = {
.setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt,
- .addr2sockaddr = v6_addr2sockaddr,
+ .addr2sockaddr = inet6_csk_addr2sockaddr,
.sockaddr_len = sizeof(struct sockaddr_in6)
};
@@ -1827,7 +1311,7 @@ static struct tcp_func ipv6_specific = {
* TCP over IPv4 via INET6 API
*/
-static struct tcp_func ipv6_mapped = {
+static struct inet_connection_sock_af_ops ipv6_mapped = {
.queue_xmit = ip_queue_xmit,
.send_check = tcp_v4_send_check,
.rebuild_header = inet_sk_rebuild_header,
@@ -1838,7 +1322,7 @@ static struct tcp_func ipv6_mapped = {
.setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt,
- .addr2sockaddr = v6_addr2sockaddr,
+ .addr2sockaddr = inet6_csk_addr2sockaddr,
.sockaddr_len = sizeof(struct sockaddr_in6)
};
@@ -1877,8 +1361,9 @@ static int tcp_v6_init_sock(struct sock *sk)
sk->sk_state = TCP_CLOSE;
- tp->af_specific = &ipv6_specific;
+ icsk->icsk_af_ops = &ipv6_specific;
icsk->icsk_ca_ops = &tcp_init_congestion_ops;
+ icsk->icsk_sync_mss = tcp_sync_mss;
sk->sk_write_space = sk_stream_write_space;
sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
@@ -1900,14 +1385,13 @@ static int tcp_v6_destroy_sock(struct sock *sk)
static void get_openreq6(struct seq_file *seq,
struct sock *sk, struct request_sock *req, int i, int uid)
{
- struct in6_addr *dest, *src;
int ttd = req->expires - jiffies;
+ struct in6_addr *src = &inet6_rsk(req)->loc_addr;
+ struct in6_addr *dest = &inet6_rsk(req)->rmt_addr;
if (ttd < 0)
ttd = 0;
- src = &tcp6_rsk(req)->loc_addr;
- dest = &tcp6_rsk(req)->rmt_addr;
seq_printf(seq,
"%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
"%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %p\n",
@@ -1988,14 +1472,14 @@ static void get_timewait6_sock(struct seq_file *seq,
{
struct in6_addr *dest, *src;
__u16 destp, srcp;
- struct tcp6_timewait_sock *tcp6tw = tcp6_twsk((struct sock *)tw);
+ struct inet6_timewait_sock *tw6 = inet6_twsk((struct sock *)tw);
int ttd = tw->tw_ttd - jiffies;
if (ttd < 0)
ttd = 0;
- dest = &tcp6tw->tw_v6_daddr;
- src = &tcp6tw->tw_v6_rcv_saddr;
+ dest = &tw6->tw_v6_daddr;
+ src = &tw6->tw_v6_rcv_saddr;
destp = ntohs(tw->tw_dport);
srcp = ntohs(tw->tw_sport);
@@ -2093,7 +1577,7 @@ struct proto tcpv6_prot = {
.sysctl_rmem = sysctl_tcp_rmem,
.max_header = MAX_TCP_HEADER,
.obj_size = sizeof(struct tcp6_sock),
- .twsk_obj_size = sizeof(struct tcp6_timewait_sock),
+ .twsk_prot = &tcp6_timewait_sock_ops,
.rsk_prot = &tcp6_request_sock_ops,
};
@@ -2110,7 +1594,8 @@ static struct inet_protosw tcpv6_protosw = {
.ops = &inet6_stream_ops,
.capability = -1,
.no_check = 0,
- .flags = INET_PROTOSW_PERMANENT,
+ .flags = INET_PROTOSW_PERMANENT |
+ INET_PROTOSW_ICSK,
};
void __init tcpv6_init(void)
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 5cc8731eb55b..d8538dcea813 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -36,6 +36,7 @@
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
#include <linux/init.h>
+#include <linux/skbuff.h>
#include <asm/uaccess.h>
#include <net/sock.h>
@@ -300,20 +301,7 @@ out:
return err;
csum_copy_err:
- /* Clear queue. */
- if (flags&MSG_PEEK) {
- int clear = 0;
- spin_lock_bh(&sk->sk_receive_queue.lock);
- if (skb == skb_peek(&sk->sk_receive_queue)) {
- __skb_unlink(skb, &sk->sk_receive_queue);
- clear = 1;
- }
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- if (clear)
- kfree_skb(skb);
- }
-
- skb_free_datagram(sk, skb);
+ skb_kill_datagram(sk, skb, flags);
if (flags & MSG_DONTWAIT) {
UDP6_INC_STATS_USER(UDP_MIB_INERRORS);
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index cf1d91e74c82..69bd957380e7 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -214,6 +214,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl)
case IPPROTO_UDP:
case IPPROTO_TCP:
case IPPROTO_SCTP:
+ case IPPROTO_DCCP:
if (pskb_may_pull(skb, skb->nh.raw + offset + 4 - skb->data)) {
u16 *ports = (u16 *)exthdr;
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
index 34b3bb868409..0dc519b40404 100644
--- a/net/ipx/af_ipx.c
+++ b/net/ipx/af_ipx.c
@@ -75,7 +75,7 @@ static struct datalink_proto *pEII_datalink;
static struct datalink_proto *p8023_datalink;
static struct datalink_proto *pSNAP_datalink;
-static struct proto_ops ipx_dgram_ops;
+static const struct proto_ops ipx_dgram_ops;
LIST_HEAD(ipx_interfaces);
DEFINE_SPINLOCK(ipx_interfaces_lock);
@@ -1884,7 +1884,7 @@ static int ipx_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
rc = -EINVAL;
break;
default:
- rc = dev_ioctl(cmd, argp);
+ rc = -ENOIOCTLCMD;
break;
}
@@ -1901,7 +1901,7 @@ static struct net_proto_family ipx_family_ops = {
.owner = THIS_MODULE,
};
-static struct proto_ops SOCKOPS_WRAPPED(ipx_dgram_ops) = {
+static const struct proto_ops SOCKOPS_WRAPPED(ipx_dgram_ops) = {
.family = PF_IPX,
.owner = THIS_MODULE,
.release = ipx_release,
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
index 6f92f9c62990..fbfa96754417 100644
--- a/net/irda/af_irda.c
+++ b/net/irda/af_irda.c
@@ -62,12 +62,12 @@
static int irda_create(struct socket *sock, int protocol);
-static struct proto_ops irda_stream_ops;
-static struct proto_ops irda_seqpacket_ops;
-static struct proto_ops irda_dgram_ops;
+static const struct proto_ops irda_stream_ops;
+static const struct proto_ops irda_seqpacket_ops;
+static const struct proto_ops irda_dgram_ops;
#ifdef CONFIG_IRDA_ULTRA
-static struct proto_ops irda_ultra_ops;
+static const struct proto_ops irda_ultra_ops;
#define ULTRA_MAX_DATA 382
#endif /* CONFIG_IRDA_ULTRA */
@@ -1438,8 +1438,9 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock,
/*
* POSIX 1003.1g mandates this order.
*/
- if (sk->sk_err)
- ret = sock_error(sk);
+ ret = sock_error(sk);
+ if (ret)
+ break;
else if (sk->sk_shutdown & RCV_SHUTDOWN)
;
else if (noblock)
@@ -1821,7 +1822,7 @@ static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return -EINVAL;
default:
IRDA_DEBUG(1, "%s(), doing device ioctl!\n", __FUNCTION__);
- return dev_ioctl(cmd, (void __user *) arg);
+ return -ENOIOCTLCMD;
}
/*NOTREACHED*/
@@ -2463,7 +2464,7 @@ static struct net_proto_family irda_family_ops = {
.owner = THIS_MODULE,
};
-static struct proto_ops SOCKOPS_WRAPPED(irda_stream_ops) = {
+static const struct proto_ops SOCKOPS_WRAPPED(irda_stream_ops) = {
.family = PF_IRDA,
.owner = THIS_MODULE,
.release = irda_release,
@@ -2484,7 +2485,7 @@ static struct proto_ops SOCKOPS_WRAPPED(irda_stream_ops) = {
.sendpage = sock_no_sendpage,
};
-static struct proto_ops SOCKOPS_WRAPPED(irda_seqpacket_ops) = {
+static const struct proto_ops SOCKOPS_WRAPPED(irda_seqpacket_ops) = {
.family = PF_IRDA,
.owner = THIS_MODULE,
.release = irda_release,
@@ -2505,7 +2506,7 @@ static struct proto_ops SOCKOPS_WRAPPED(irda_seqpacket_ops) = {
.sendpage = sock_no_sendpage,
};
-static struct proto_ops SOCKOPS_WRAPPED(irda_dgram_ops) = {
+static const struct proto_ops SOCKOPS_WRAPPED(irda_dgram_ops) = {
.family = PF_IRDA,
.owner = THIS_MODULE,
.release = irda_release,
@@ -2527,7 +2528,7 @@ static struct proto_ops SOCKOPS_WRAPPED(irda_dgram_ops) = {
};
#ifdef CONFIG_IRDA_ULTRA
-static struct proto_ops SOCKOPS_WRAPPED(irda_ultra_ops) = {
+static const struct proto_ops SOCKOPS_WRAPPED(irda_ultra_ops) = {
.family = PF_IRDA,
.owner = THIS_MODULE,
.release = irda_release,
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 39031684b65c..52efd04cbedb 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -113,7 +113,7 @@ static __inline__ void pfkey_unlock_table(void)
}
-static struct proto_ops pfkey_ops;
+static const struct proto_ops pfkey_ops;
static void pfkey_insert(struct sock *sk)
{
@@ -336,6 +336,7 @@ static u8 sadb_ext_min_len[] = {
[SADB_X_EXT_NAT_T_SPORT] = (u8) sizeof(struct sadb_x_nat_t_port),
[SADB_X_EXT_NAT_T_DPORT] = (u8) sizeof(struct sadb_x_nat_t_port),
[SADB_X_EXT_NAT_T_OA] = (u8) sizeof(struct sadb_address),
+ [SADB_X_EXT_SEC_CTX] = (u8) sizeof(struct sadb_x_sec_ctx),
};
/* Verify sadb_address_{len,prefixlen} against sa_family. */
@@ -383,6 +384,55 @@ static int verify_address_len(void *p)
return 0;
}
+static inline int pfkey_sec_ctx_len(struct sadb_x_sec_ctx *sec_ctx)
+{
+ int len = 0;
+
+ len += sizeof(struct sadb_x_sec_ctx);
+ len += sec_ctx->sadb_x_ctx_len;
+ len += sizeof(uint64_t) - 1;
+ len /= sizeof(uint64_t);
+
+ return len;
+}
+
+static inline int verify_sec_ctx_len(void *p)
+{
+ struct sadb_x_sec_ctx *sec_ctx = (struct sadb_x_sec_ctx *)p;
+ int len;
+
+ if (sec_ctx->sadb_x_ctx_len > PAGE_SIZE)
+ return -EINVAL;
+
+ len = pfkey_sec_ctx_len(sec_ctx);
+
+ if (sec_ctx->sadb_x_sec_len != len)
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline struct xfrm_user_sec_ctx *pfkey_sadb2xfrm_user_sec_ctx(struct sadb_x_sec_ctx *sec_ctx)
+{
+ struct xfrm_user_sec_ctx *uctx = NULL;
+ int ctx_size = sec_ctx->sadb_x_ctx_len;
+
+ uctx = kmalloc((sizeof(*uctx)+ctx_size), GFP_KERNEL);
+
+ if (!uctx)
+ return NULL;
+
+ uctx->len = pfkey_sec_ctx_len(sec_ctx);
+ uctx->exttype = sec_ctx->sadb_x_sec_exttype;
+ uctx->ctx_doi = sec_ctx->sadb_x_ctx_doi;
+ uctx->ctx_alg = sec_ctx->sadb_x_ctx_alg;
+ uctx->ctx_len = sec_ctx->sadb_x_ctx_len;
+ memcpy(uctx + 1, sec_ctx + 1,
+ uctx->ctx_len);
+
+ return uctx;
+}
+
static int present_and_same_family(struct sadb_address *src,
struct sadb_address *dst)
{
@@ -438,6 +488,10 @@ static int parse_exthdrs(struct sk_buff *skb, struct sadb_msg *hdr, void **ext_h
if (verify_address_len(p))
return -EINVAL;
}
+ if (ext_type == SADB_X_EXT_SEC_CTX) {
+ if (verify_sec_ctx_len(p))
+ return -EINVAL;
+ }
ext_hdrs[ext_type-1] = p;
}
p += ext_len;
@@ -586,6 +640,9 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
struct sadb_key *key;
struct sadb_x_sa2 *sa2;
struct sockaddr_in *sin;
+ struct sadb_x_sec_ctx *sec_ctx;
+ struct xfrm_sec_ctx *xfrm_ctx;
+ int ctx_size = 0;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct sockaddr_in6 *sin6;
#endif
@@ -609,6 +666,12 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
sizeof(struct sadb_address)*2 +
sockaddr_size*2 +
sizeof(struct sadb_x_sa2);
+
+ if ((xfrm_ctx = x->security)) {
+ ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
+ size += sizeof(struct sadb_x_sec_ctx) + ctx_size;
+ }
+
/* identity & sensitivity */
if ((x->props.family == AF_INET &&
@@ -899,6 +962,20 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
n_port->sadb_x_nat_t_port_reserved = 0;
}
+ /* security context */
+ if (xfrm_ctx) {
+ sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb,
+ sizeof(struct sadb_x_sec_ctx) + ctx_size);
+ sec_ctx->sadb_x_sec_len =
+ (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
+ sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
+ sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
+ sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
+ sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
+ memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
+ xfrm_ctx->ctx_len);
+ }
+
return skb;
}
@@ -909,6 +986,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
struct sadb_lifetime *lifetime;
struct sadb_sa *sa;
struct sadb_key *key;
+ struct sadb_x_sec_ctx *sec_ctx;
uint16_t proto;
int err;
@@ -993,6 +1071,21 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
x->lft.soft_add_expires_seconds = lifetime->sadb_lifetime_addtime;
x->lft.soft_use_expires_seconds = lifetime->sadb_lifetime_usetime;
}
+
+ sec_ctx = (struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1];
+ if (sec_ctx != NULL) {
+ struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
+
+ if (!uctx)
+ goto out;
+
+ err = security_xfrm_state_alloc(x, uctx);
+ kfree(uctx);
+
+ if (err)
+ goto out;
+ }
+
key = (struct sadb_key*) ext_hdrs[SADB_EXT_KEY_AUTH-1];
if (sa->sadb_sa_auth) {
int keysize = 0;
@@ -1720,6 +1813,18 @@ parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol)
return 0;
}
+static inline int pfkey_xfrm_policy2sec_ctx_size(struct xfrm_policy *xp)
+{
+ struct xfrm_sec_ctx *xfrm_ctx = xp->security;
+
+ if (xfrm_ctx) {
+ int len = sizeof(struct sadb_x_sec_ctx);
+ len += xfrm_ctx->ctx_len;
+ return PFKEY_ALIGN8(len);
+ }
+ return 0;
+}
+
static int pfkey_xfrm_policy2msg_size(struct xfrm_policy *xp)
{
int sockaddr_size = pfkey_sockaddr_size(xp->family);
@@ -1733,7 +1838,8 @@ static int pfkey_xfrm_policy2msg_size(struct xfrm_policy *xp)
(sockaddr_size * 2) +
sizeof(struct sadb_x_policy) +
(xp->xfrm_nr * (sizeof(struct sadb_x_ipsecrequest) +
- (socklen * 2)));
+ (socklen * 2))) +
+ pfkey_xfrm_policy2sec_ctx_size(xp);
}
static struct sk_buff * pfkey_xfrm_policy2msg_prep(struct xfrm_policy *xp)
@@ -1757,6 +1863,8 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i
struct sadb_lifetime *lifetime;
struct sadb_x_policy *pol;
struct sockaddr_in *sin;
+ struct sadb_x_sec_ctx *sec_ctx;
+ struct xfrm_sec_ctx *xfrm_ctx;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct sockaddr_in6 *sin6;
#endif
@@ -1941,6 +2049,21 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i
}
}
}
+
+ /* security context */
+ if ((xfrm_ctx = xp->security)) {
+ int ctx_size = pfkey_xfrm_policy2sec_ctx_size(xp);
+
+ sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb, ctx_size);
+ sec_ctx->sadb_x_sec_len = ctx_size / sizeof(uint64_t);
+ sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
+ sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
+ sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
+ sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
+ memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
+ xfrm_ctx->ctx_len);
+ }
+
hdr->sadb_msg_len = size / sizeof(uint64_t);
hdr->sadb_msg_reserved = atomic_read(&xp->refcnt);
}
@@ -1976,12 +2099,13 @@ out:
static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
- int err;
+ int err = 0;
struct sadb_lifetime *lifetime;
struct sadb_address *sa;
struct sadb_x_policy *pol;
struct xfrm_policy *xp;
struct km_event c;
+ struct sadb_x_sec_ctx *sec_ctx;
if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
@@ -2028,6 +2152,22 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
if (xp->selector.dport)
xp->selector.dport_mask = ~0;
+ sec_ctx = (struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1];
+ if (sec_ctx != NULL) {
+ struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
+
+ if (!uctx) {
+ err = -ENOBUFS;
+ goto out;
+ }
+
+ err = security_xfrm_policy_alloc(xp, uctx);
+ kfree(uctx);
+
+ if (err)
+ goto out;
+ }
+
xp->lft.soft_byte_limit = XFRM_INF;
xp->lft.hard_byte_limit = XFRM_INF;
xp->lft.soft_packet_limit = XFRM_INF;
@@ -2051,10 +2191,9 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
err = xfrm_policy_insert(pol->sadb_x_policy_dir-1, xp,
hdr->sadb_msg_type != SADB_X_SPDUPDATE);
- if (err) {
- kfree(xp);
- return err;
- }
+
+ if (err)
+ goto out;
if (hdr->sadb_msg_type == SADB_X_SPDUPDATE)
c.event = XFRM_MSG_UPDPOLICY;
@@ -2069,6 +2208,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
return 0;
out:
+ security_xfrm_policy_free(xp);
kfree(xp);
return err;
}
@@ -2078,9 +2218,10 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
int err;
struct sadb_address *sa;
struct sadb_x_policy *pol;
- struct xfrm_policy *xp;
+ struct xfrm_policy *xp, tmp;
struct xfrm_selector sel;
struct km_event c;
+ struct sadb_x_sec_ctx *sec_ctx;
if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
@@ -2109,7 +2250,24 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
if (sel.dport)
sel.dport_mask = ~0;
- xp = xfrm_policy_bysel(pol->sadb_x_policy_dir-1, &sel, 1);
+ sec_ctx = (struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1];
+ memset(&tmp, 0, sizeof(struct xfrm_policy));
+
+ if (sec_ctx != NULL) {
+ struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
+
+ if (!uctx)
+ return -ENOMEM;
+
+ err = security_xfrm_policy_alloc(&tmp, uctx);
+ kfree(uctx);
+
+ if (err)
+ return err;
+ }
+
+ xp = xfrm_policy_bysel_ctx(pol->sadb_x_policy_dir-1, &sel, tmp.security, 1);
+ security_xfrm_policy_free(&tmp);
if (xp == NULL)
return -ENOENT;
@@ -2660,6 +2818,7 @@ static struct xfrm_policy *pfkey_compile_policy(u16 family, int opt,
{
struct xfrm_policy *xp;
struct sadb_x_policy *pol = (struct sadb_x_policy*)data;
+ struct sadb_x_sec_ctx *sec_ctx;
switch (family) {
case AF_INET:
@@ -2709,10 +2868,32 @@ static struct xfrm_policy *pfkey_compile_policy(u16 family, int opt,
(*dir = parse_ipsecrequests(xp, pol)) < 0)
goto out;
+ /* security context too */
+ if (len >= (pol->sadb_x_policy_len*8 +
+ sizeof(struct sadb_x_sec_ctx))) {
+ char *p = (char *)pol;
+ struct xfrm_user_sec_ctx *uctx;
+
+ p += pol->sadb_x_policy_len*8;
+ sec_ctx = (struct sadb_x_sec_ctx *)p;
+ if (len < pol->sadb_x_policy_len*8 +
+ sec_ctx->sadb_x_sec_len)
+ goto out;
+ if ((*dir = verify_sec_ctx_len(p)))
+ goto out;
+ uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
+ *dir = security_xfrm_policy_alloc(xp, uctx);
+ kfree(uctx);
+
+ if (*dir)
+ goto out;
+ }
+
*dir = pol->sadb_x_policy_dir-1;
return xp;
out:
+ security_xfrm_policy_free(xp);
kfree(xp);
return NULL;
}
@@ -2946,7 +3127,7 @@ out:
return err;
}
-static struct proto_ops pfkey_ops = {
+static const struct proto_ops pfkey_ops = {
.family = PF_KEY,
.owner = THIS_MODULE,
/* Operations that make no sense on pfkey sockets. */
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index c3f0b0783453..8171c53bc0ed 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -36,7 +36,7 @@
static u16 llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
static u16 llc_ui_sap_link_no_max[256];
static struct sockaddr_llc llc_ui_addrnull;
-static struct proto_ops llc_ui_ops;
+static const struct proto_ops llc_ui_ops;
static int llc_ui_wait_for_conn(struct sock *sk, long timeout);
static int llc_ui_wait_for_disc(struct sock *sk, long timeout);
@@ -566,10 +566,9 @@ static int llc_wait_data(struct sock *sk, long timeo)
/*
* POSIX 1003.1g mandates this order.
*/
- if (sk->sk_err) {
- rc = sock_error(sk);
+ rc = sock_error(sk);
+ if (rc)
break;
- }
rc = 0;
if (sk->sk_shutdown & RCV_SHUTDOWN)
break;
@@ -960,7 +959,7 @@ out:
static int llc_ui_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
- return dev_ioctl(cmd, (void __user *)arg);
+ return -ENOIOCTLCMD;
}
/**
@@ -1099,7 +1098,7 @@ static struct net_proto_family llc_ui_family_ops = {
.owner = THIS_MODULE,
};
-static struct proto_ops llc_ui_ops = {
+static const struct proto_ops llc_ui_ops = {
.family = PF_LLC,
.owner = THIS_MODULE,
.release = llc_ui_release,
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index cba63729313d..e10512e229b6 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -151,7 +151,7 @@ instance_create(u_int16_t group_num, int pid)
goto out_unlock;
INIT_HLIST_NODE(&inst->hlist);
- inst->lock = SPIN_LOCK_UNLOCKED;
+ spin_lock_init(&inst->lock);
/* needs to be two, since we _put() after creation */
atomic_set(&inst->use, 2);
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index f28460b61e47..55afdda3d940 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -148,7 +148,7 @@ instance_create(u_int16_t queue_num, int pid)
atomic_set(&inst->id_sequence, 0);
/* needs to be two, since we _put() after creation */
atomic_set(&inst->use, 2);
- inst->lock = SPIN_LOCK_UNLOCKED;
+ spin_lock_init(&inst->lock);
INIT_LIST_HEAD(&inst->queue_list);
if (!try_module_get(THIS_MODULE))
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 96020d7087e8..7849cac14d3a 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -293,7 +293,7 @@ static inline int nl_pid_hash_dilute(struct nl_pid_hash *hash, int len)
return 0;
}
-static struct proto_ops netlink_ops;
+static const struct proto_ops netlink_ops;
static int netlink_insert(struct sock *sk, u32 pid)
{
@@ -1656,7 +1656,7 @@ int netlink_unregister_notifier(struct notifier_block *nb)
return notifier_chain_unregister(&netlink_chain, nb);
}
-static struct proto_ops netlink_ops = {
+static const struct proto_ops netlink_ops = {
.family = PF_NETLINK,
.owner = THIS_MODULE,
.release = netlink_release,
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index 287cfcc56951..3b1378498d50 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -441,7 +441,7 @@ errout:
}
static struct sk_buff *ctrl_build_msg(struct genl_family *family, u32 pid,
- int seq, int cmd)
+ int seq, u8 cmd)
{
struct sk_buff *skb;
int err;
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index e5d82d711cae..63b0e4afeb33 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -63,7 +63,7 @@ static unsigned short circuit = 0x101;
static HLIST_HEAD(nr_list);
static DEFINE_SPINLOCK(nr_list_lock);
-static struct proto_ops nr_proto_ops;
+static const struct proto_ops nr_proto_ops;
/*
* Socket removal during an interrupt is now safe.
@@ -1166,10 +1166,11 @@ static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
void __user *argp = (void __user *)arg;
int ret;
- lock_sock(sk);
switch (cmd) {
case TIOCOUTQ: {
long amount;
+
+ lock_sock(sk);
amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc);
if (amount < 0)
amount = 0;
@@ -1180,6 +1181,8 @@ static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case TIOCINQ: {
struct sk_buff *skb;
long amount = 0L;
+
+ lock_sock(sk);
/* These two are safe on a single CPU system as only user tasks fiddle here */
if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL)
amount = skb->len;
@@ -1188,6 +1191,7 @@ static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
}
case SIOCGSTAMP:
+ lock_sock(sk);
ret = sock_get_timestamp(sk, argp);
release_sock(sk);
return ret;
@@ -1202,21 +1206,17 @@ static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCSIFNETMASK:
case SIOCGIFMETRIC:
case SIOCSIFMETRIC:
- release_sock(sk);
return -EINVAL;
case SIOCADDRT:
case SIOCDELRT:
case SIOCNRDECOBS:
- release_sock(sk);
if (!capable(CAP_NET_ADMIN)) return -EPERM;
return nr_rt_ioctl(cmd, argp);
default:
- release_sock(sk);
- return dev_ioctl(cmd, argp);
+ return -ENOIOCTLCMD;
}
- release_sock(sk);
return 0;
}
@@ -1337,7 +1337,7 @@ static struct net_proto_family nr_family_ops = {
.owner = THIS_MODULE,
};
-static struct proto_ops nr_proto_ops = {
+static const struct proto_ops nr_proto_ops = {
.family = PF_NETROM,
.owner = THIS_MODULE,
.release = nr_release,
diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c
index 004e8599b8fe..a7d88b5ad756 100644
--- a/net/netrom/nr_in.c
+++ b/net/netrom/nr_in.c
@@ -99,7 +99,7 @@ static int nr_state1_machine(struct sock *sk, struct sk_buff *skb,
break;
case NR_RESET:
- if (sysctl_netrom_reset_circuit);
+ if (sysctl_netrom_reset_circuit)
nr_disconnect(sk, ECONNRESET);
break;
@@ -130,7 +130,7 @@ static int nr_state2_machine(struct sock *sk, struct sk_buff *skb,
break;
case NR_RESET:
- if (sysctl_netrom_reset_circuit);
+ if (sysctl_netrom_reset_circuit)
nr_disconnect(sk, ECONNRESET);
break;
@@ -265,7 +265,7 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype
break;
case NR_RESET:
- if (sysctl_netrom_reset_circuit);
+ if (sysctl_netrom_reset_circuit)
nr_disconnect(sk, ECONNRESET);
break;
diff --git a/net/nonet.c b/net/nonet.c
index e5241dceaa57..1230f0ae832e 100644
--- a/net/nonet.c
+++ b/net/nonet.c
@@ -14,11 +14,6 @@
#include <linux/init.h>
#include <linux/kernel.h>
-void __init sock_init(void)
-{
- printk(KERN_INFO "Linux NoNET1.0 for Linux 2.6\n");
-}
-
static int sock_no_open(struct inode *irrelevant, struct file *dontcare)
{
return -ENXIO;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 3e2462760413..f69e5ed9bd06 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -251,10 +251,10 @@ static void packet_sock_destruct(struct sock *sk)
}
-static struct proto_ops packet_ops;
+static const struct proto_ops packet_ops;
#ifdef CONFIG_SOCK_PACKET
-static struct proto_ops packet_ops_spkt;
+static const struct proto_ops packet_ops_spkt;
static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
@@ -1521,7 +1521,7 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd,
#endif
default:
- return dev_ioctl(cmd, (void __user *)arg);
+ return -ENOIOCTLCMD;
}
return 0;
}
@@ -1784,7 +1784,7 @@ out:
#ifdef CONFIG_SOCK_PACKET
-static struct proto_ops packet_ops_spkt = {
+static const struct proto_ops packet_ops_spkt = {
.family = PF_PACKET,
.owner = THIS_MODULE,
.release = packet_release,
@@ -1806,7 +1806,7 @@ static struct proto_ops packet_ops_spkt = {
};
#endif
-static struct proto_ops packet_ops = {
+static const struct proto_ops packet_ops = {
.family = PF_PACKET,
.owner = THIS_MODULE,
.release = packet_release,
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index 829fdbc4400b..63090be2315a 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -1320,7 +1320,7 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return 0;
default:
- return dev_ioctl(cmd, argp);
+ return -ENOIOCTLCMD;
}
return 0;
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 82fb07aa06a5..ba5283204837 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -25,7 +25,7 @@
#include <net/pkt_sched.h>
-#define VERSION "1.1"
+#define VERSION "1.2"
/* Network Emulation Queuing algorithm.
====================================
@@ -65,11 +65,12 @@ struct netem_sched_data {
u32 jitter;
u32 duplicate;
u32 reorder;
+ u32 corrupt;
struct crndstate {
unsigned long last;
unsigned long rho;
- } delay_cor, loss_cor, dup_cor, reorder_cor;
+ } delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
struct disttable {
u32 size;
@@ -183,6 +184,23 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
q->duplicate = dupsave;
}
+ /*
+ * Randomized packet corruption.
+ * Make copy if needed since we are modifying
+ * If packet is going to be hardware checksummed, then
+ * do it now in software before we mangle it.
+ */
+ if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) {
+ if (!(skb = skb_unshare(skb, GFP_ATOMIC))
+ || (skb->ip_summed == CHECKSUM_HW
+ && skb_checksum_help(skb, 0))) {
+ sch->qstats.drops++;
+ return NET_XMIT_DROP;
+ }
+
+ skb->data[net_random() % skb_headlen(skb)] ^= 1<<(net_random() % 8);
+ }
+
if (q->gap == 0 /* not doing reordering */
|| q->counter < q->gap /* inside last reordering gap */
|| q->reorder < get_crandom(&q->reorder_cor)) {
@@ -382,6 +400,20 @@ static int get_reorder(struct Qdisc *sch, const struct rtattr *attr)
return 0;
}
+static int get_corrupt(struct Qdisc *sch, const struct rtattr *attr)
+{
+ struct netem_sched_data *q = qdisc_priv(sch);
+ const struct tc_netem_corrupt *r = RTA_DATA(attr);
+
+ if (RTA_PAYLOAD(attr) != sizeof(*r))
+ return -EINVAL;
+
+ q->corrupt = r->probability;
+ init_crandom(&q->corrupt_cor, r->correlation);
+ return 0;
+}
+
+/* Parse netlink message to set options */
static int netem_change(struct Qdisc *sch, struct rtattr *opt)
{
struct netem_sched_data *q = qdisc_priv(sch);
@@ -432,13 +464,19 @@ static int netem_change(struct Qdisc *sch, struct rtattr *opt)
if (ret)
return ret;
}
+
if (tb[TCA_NETEM_REORDER-1]) {
ret = get_reorder(sch, tb[TCA_NETEM_REORDER-1]);
if (ret)
return ret;
}
- }
+ if (tb[TCA_NETEM_CORRUPT-1]) {
+ ret = get_corrupt(sch, tb[TCA_NETEM_CORRUPT-1]);
+ if (ret)
+ return ret;
+ }
+ }
return 0;
}
@@ -564,6 +602,7 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
struct tc_netem_qopt qopt;
struct tc_netem_corr cor;
struct tc_netem_reorder reorder;
+ struct tc_netem_corrupt corrupt;
qopt.latency = q->latency;
qopt.jitter = q->jitter;
@@ -582,6 +621,10 @@ static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
reorder.correlation = q->reorder_cor.rho;
RTA_PUT(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
+ corrupt.probability = q->corrupt;
+ corrupt.correlation = q->corrupt_cor.rho;
+ RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
+
rta->rta_len = skb->tail - b;
return skb->len;
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index 6cf0342706b5..c4a2a8c4c339 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -22,6 +22,7 @@
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
+#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index dec68a604773..9d05e13e92f6 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -110,7 +110,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->cookie_life.tv_sec = sp->assocparams.sasoc_cookie_life / 1000;
asoc->cookie_life.tv_usec = (sp->assocparams.sasoc_cookie_life % 1000)
* 1000;
- asoc->pmtu = 0;
asoc->frag_point = 0;
/* Set the association max_retrans and RTO values from the
@@ -123,6 +122,25 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->overall_error_count = 0;
+ /* Initialize the association's heartbeat interval based on the
+ * sock configured value.
+ */
+ asoc->hbinterval = msecs_to_jiffies(sp->hbinterval);
+
+ /* Initialize path max retrans value. */
+ asoc->pathmaxrxt = sp->pathmaxrxt;
+
+ /* Initialize default path MTU. */
+ asoc->pathmtu = sp->pathmtu;
+
+ /* Set association default SACK delay */
+ asoc->sackdelay = msecs_to_jiffies(sp->sackdelay);
+
+ /* Set the association default flags controlling
+ * Heartbeat, SACK delay, and Path MTU Discovery.
+ */
+ asoc->param_flags = sp->param_flags;
+
/* Initialize the maximum mumber of new data packets that can be sent
* in a burst.
*/
@@ -144,8 +162,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
= 5 * asoc->rto_max;
asoc->timeouts[SCTP_EVENT_TIMEOUT_HEARTBEAT] = 0;
- asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] =
- SCTP_DEFAULT_TIMEOUT_SACK;
+ asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] = asoc->sackdelay;
asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] =
sp->autoclose * HZ;
@@ -540,23 +557,46 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
sctp_transport_set_owner(peer, asoc);
+ /* Initialize the peer's heartbeat interval based on the
+ * association configured value.
+ */
+ peer->hbinterval = asoc->hbinterval;
+
+ /* Set the path max_retrans. */
+ peer->pathmaxrxt = asoc->pathmaxrxt;
+
+ /* Initialize the peer's SACK delay timeout based on the
+ * association configured value.
+ */
+ peer->sackdelay = asoc->sackdelay;
+
+ /* Enable/disable heartbeat, SACK delay, and path MTU discovery
+ * based on association setting.
+ */
+ peer->param_flags = asoc->param_flags;
+
/* Initialize the pmtu of the transport. */
- sctp_transport_pmtu(peer);
+ if (peer->param_flags & SPP_PMTUD_ENABLE)
+ sctp_transport_pmtu(peer);
+ else if (asoc->pathmtu)
+ peer->pathmtu = asoc->pathmtu;
+ else
+ peer->pathmtu = SCTP_DEFAULT_MAXSEGMENT;
/* If this is the first transport addr on this association,
* initialize the association PMTU to the peer's PMTU.
* If not and the current association PMTU is higher than the new
* peer's PMTU, reset the association PMTU to the new peer's PMTU.
*/
- if (asoc->pmtu)
- asoc->pmtu = min_t(int, peer->pmtu, asoc->pmtu);
+ if (asoc->pathmtu)
+ asoc->pathmtu = min_t(int, peer->pathmtu, asoc->pathmtu);
else
- asoc->pmtu = peer->pmtu;
+ asoc->pathmtu = peer->pathmtu;
SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to "
- "%d\n", asoc, asoc->pmtu);
+ "%d\n", asoc, asoc->pathmtu);
- asoc->frag_point = sctp_frag_point(sp, asoc->pmtu);
+ asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu);
/* The asoc->peer.port might not be meaningful yet, but
* initialize the packet structure anyway.
@@ -574,7 +614,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
* (for example, implementations MAY use the size of the
* receiver advertised window).
*/
- peer->cwnd = min(4*asoc->pmtu, max_t(__u32, 2*asoc->pmtu, 4380));
+ peer->cwnd = min(4*asoc->pathmtu, max_t(__u32, 2*asoc->pathmtu, 4380));
/* At this point, we may not have the receiver's advertised window,
* so initialize ssthresh to the default value and it will be set
@@ -585,17 +625,6 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
peer->partial_bytes_acked = 0;
peer->flight_size = 0;
- /* By default, enable heartbeat for peer address. */
- peer->hb_allowed = 1;
-
- /* Initialize the peer's heartbeat interval based on the
- * sock configured value.
- */
- peer->hb_interval = msecs_to_jiffies(sp->paddrparam.spp_hbinterval);
-
- /* Set the path max_retrans. */
- peer->max_retrans = sp->paddrparam.spp_pathmaxrxt;
-
/* Set the transport's RTO.initial value */
peer->rto = asoc->rto_initial;
@@ -1155,18 +1184,18 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc)
/* Get the lowest pmtu of all the transports. */
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, struct sctp_transport, transports);
- if (!pmtu || (t->pmtu < pmtu))
- pmtu = t->pmtu;
+ if (!pmtu || (t->pathmtu < pmtu))
+ pmtu = t->pathmtu;
}
if (pmtu) {
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
- asoc->pmtu = pmtu;
+ asoc->pathmtu = pmtu;
asoc->frag_point = sctp_frag_point(sp, pmtu);
}
SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n",
- __FUNCTION__, asoc, asoc->pmtu, asoc->frag_point);
+ __FUNCTION__, asoc, asoc->pathmtu, asoc->frag_point);
}
/* Should we send a SACK to update our peer? */
@@ -1179,7 +1208,7 @@ static inline int sctp_peer_needs_update(struct sctp_association *asoc)
case SCTP_STATE_SHUTDOWN_SENT:
if ((asoc->rwnd > asoc->a_rwnd) &&
((asoc->rwnd - asoc->a_rwnd) >=
- min_t(__u32, (asoc->base.sk->sk_rcvbuf >> 1), asoc->pmtu)))
+ min_t(__u32, (asoc->base.sk->sk_rcvbuf >> 1), asoc->pathmtu)))
return 1;
break;
default:
diff --git a/net/sctp/input.c b/net/sctp/input.c
index b24ff2c1aef5..238f1bffa684 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -305,18 +305,36 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc,
struct sctp_transport *t, __u32 pmtu)
{
- if (unlikely(pmtu < SCTP_DEFAULT_MINSEGMENT)) {
- printk(KERN_WARNING "%s: Reported pmtu %d too low, "
- "using default minimum of %d\n", __FUNCTION__, pmtu,
- SCTP_DEFAULT_MINSEGMENT);
- pmtu = SCTP_DEFAULT_MINSEGMENT;
- }
+ if (sock_owned_by_user(sk) || !t || (t->pathmtu == pmtu))
+ return;
- if (!sock_owned_by_user(sk) && t && (t->pmtu != pmtu)) {
- t->pmtu = pmtu;
+ if (t->param_flags & SPP_PMTUD_ENABLE) {
+ if (unlikely(pmtu < SCTP_DEFAULT_MINSEGMENT)) {
+ printk(KERN_WARNING "%s: Reported pmtu %d too low, "
+ "using default minimum of %d\n",
+ __FUNCTION__, pmtu,
+ SCTP_DEFAULT_MINSEGMENT);
+ /* Use default minimum segment size and disable
+ * pmtu discovery on this transport.
+ */
+ t->pathmtu = SCTP_DEFAULT_MINSEGMENT;
+ t->param_flags = (t->param_flags & ~SPP_HB) |
+ SPP_PMTUD_DISABLE;
+ } else {
+ t->pathmtu = pmtu;
+ }
+
+ /* Update association pmtu. */
sctp_assoc_sync_pmtu(asoc);
- sctp_retransmit(&asoc->outqueue, t, SCTP_RTXR_PMTUD);
}
+
+ /* Retransmit with the new pmtu setting.
+ * Normally, if PMTU discovery is disabled, an ICMP Fragmentation
+ * Needed will never be sent, but if a message was sent before
+ * PMTU discovery was disabled that was larger than the PMTU, it
+ * would not be fragmented, so it must be re-transmitted fragmented.
+ */
+ sctp_retransmit(&asoc->outqueue, t, SCTP_RTXR_PMTUD);
}
/*
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index fa3be2b8fb5f..15c05165c905 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -866,7 +866,7 @@ static int sctp_inet6_supported_addrs(const struct sctp_sock *opt,
return 2;
}
-static struct proto_ops inet6_seqpacket_ops = {
+static const struct proto_ops inet6_seqpacket_ops = {
.family = PF_INET6,
.owner = THIS_MODULE,
.release = inet6_release,
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 931371633464..a40991ef72c9 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -234,8 +234,8 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
goto finish;
pmtu = ((packet->transport->asoc) ?
- (packet->transport->asoc->pmtu) :
- (packet->transport->pmtu));
+ (packet->transport->asoc->pathmtu) :
+ (packet->transport->pathmtu));
too_big = (psize + chunk_len > pmtu);
@@ -482,7 +482,9 @@ int sctp_packet_transmit(struct sctp_packet *packet)
if (!dst || (dst->obsolete > 1)) {
dst_release(dst);
sctp_transport_route(tp, NULL, sctp_sk(sk));
- sctp_assoc_sync_pmtu(asoc);
+ if (asoc->param_flags & SPP_PMTUD_ENABLE) {
+ sctp_assoc_sync_pmtu(asoc);
+ }
}
nskb->dst = dst_clone(tp->dst);
@@ -492,7 +494,10 @@ int sctp_packet_transmit(struct sctp_packet *packet)
SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb len %d\n",
nskb->len);
- (*tp->af_specific->sctp_xmit)(nskb, tp, packet->ipfragok);
+ if (tp->param_flags & SPP_PMTUD_ENABLE)
+ (*tp->af_specific->sctp_xmit)(nskb, tp, packet->ipfragok);
+ else
+ (*tp->af_specific->sctp_xmit)(nskb, tp, 1);
out:
packet->size = packet->overhead;
@@ -577,7 +582,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
* if ((flightsize + Max.Burst * MTU) < cwnd)
* cwnd = flightsize + Max.Burst * MTU
*/
- max_burst_bytes = asoc->max_burst * asoc->pmtu;
+ max_burst_bytes = asoc->max_burst * asoc->pathmtu;
if ((transport->flight_size + max_burst_bytes) < transport->cwnd) {
transport->cwnd = transport->flight_size + max_burst_bytes;
SCTP_DEBUG_PRINTK("%s: cwnd limited by max_burst: "
@@ -622,7 +627,7 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
* data will fit or delay in hopes of bundling a full
* sized packet.
*/
- if (len < asoc->pmtu - packet->overhead) {
+ if (len < asoc->pathmtu - packet->overhead) {
retval = SCTP_XMIT_NAGLE_DELAY;
goto finish;
}
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index f775d78aa59d..de693b43c8ea 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -54,6 +54,7 @@
#include <net/protocol.h>
#include <net/ip.h>
#include <net/ipv6.h>
+#include <net/route.h>
#include <net/sctp/sctp.h>
#include <net/addrconf.h>
#include <net/inet_common.h>
@@ -829,7 +830,7 @@ static struct notifier_block sctp_inetaddr_notifier = {
};
/* Socket operations. */
-static struct proto_ops inet_seqpacket_ops = {
+static const struct proto_ops inet_seqpacket_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release, /* Needs to be wrapped... */
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 823947170a33..2d7d8a5db2ac 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -157,9 +157,12 @@ static int sctp_gen_sack(struct sctp_association *asoc, int force,
{
__u32 ctsn, max_tsn_seen;
struct sctp_chunk *sack;
+ struct sctp_transport *trans = asoc->peer.last_data_from;
int error = 0;
- if (force)
+ if (force ||
+ (!trans && (asoc->param_flags & SPP_SACKDELAY_DISABLE)) ||
+ (trans && (trans->param_flags & SPP_SACKDELAY_DISABLE)))
asoc->peer.sack_needed = 1;
ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
@@ -189,7 +192,22 @@ static int sctp_gen_sack(struct sctp_association *asoc, int force,
if (!asoc->peer.sack_needed) {
/* We will need a SACK for the next packet. */
asoc->peer.sack_needed = 1;
- goto out;
+
+ /* Set the SACK delay timeout based on the
+ * SACK delay for the last transport
+ * data was received from, or the default
+ * for the association.
+ */
+ if (trans)
+ asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] =
+ trans->sackdelay;
+ else
+ asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] =
+ asoc->sackdelay;
+
+ /* Restart the SACK timer. */
+ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
+ SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
} else {
if (asoc->a_rwnd > asoc->rwnd)
asoc->a_rwnd = asoc->rwnd;
@@ -205,7 +223,7 @@ static int sctp_gen_sack(struct sctp_association *asoc, int force,
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
}
-out:
+
return error;
nomem:
error = -ENOMEM;
@@ -415,7 +433,7 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc,
asoc->overall_error_count++;
if (transport->state != SCTP_INACTIVE &&
- (transport->error_count++ >= transport->max_retrans)) {
+ (transport->error_count++ >= transport->pathmaxrxt)) {
SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p",
" transport IP: port:%d failed.\n",
asoc,
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 475bfb4972d9..557a7d90b92a 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -900,7 +900,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep,
* HEARTBEAT is sent (see Section 8.3).
*/
- if (transport->hb_allowed) {
+ if (transport->param_flags & SPP_HB_ENABLE) {
if (SCTP_DISPOSITION_NOMEM ==
sctp_sf_heartbeat(ep, asoc, type, arg,
commands))
@@ -1051,7 +1051,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
return SCTP_DISPOSITION_DISCARD;
}
- max_interval = link->hb_interval + link->rto;
+ max_interval = link->hbinterval + link->rto;
/* Check if the timestamp looks valid. */
if (time_after(hbinfo->sent_at, jiffies) ||
@@ -2691,14 +2691,9 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
* document allow. However, an SCTP transmitter MUST NOT be
* more aggressive than the following algorithms allow.
*/
- if (chunk->end_of_packet) {
+ if (chunk->end_of_packet)
sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE());
- /* Start the SACK timer. */
- sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
- SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
- }
-
return SCTP_DISPOSITION_CONSUME;
discard_force:
@@ -2721,13 +2716,9 @@ discard_force:
return SCTP_DISPOSITION_DISCARD;
discard_noforce:
- if (chunk->end_of_packet) {
+ if (chunk->end_of_packet)
sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE());
- /* Start the SACK timer. */
- sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
- SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
- }
return SCTP_DISPOSITION_DISCARD;
consume:
return SCTP_DISPOSITION_CONSUME;
@@ -3442,9 +3433,6 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn(const struct sctp_endpoint *ep,
* send another.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE());
- /* Start the SACK timer. */
- sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
- SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
return SCTP_DISPOSITION_CONSUME;
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 1f7f244806b7..fc04d185fa33 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -156,10 +156,6 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
sizeof(struct sk_buff) +
sizeof(struct sctp_chunk);
- sk->sk_wmem_queued += SCTP_DATA_SNDSIZE(chunk) +
- sizeof(struct sk_buff) +
- sizeof(struct sctp_chunk);
-
atomic_add(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
}
@@ -1945,107 +1941,379 @@ static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval,
* address's parameters:
*
* struct sctp_paddrparams {
- * sctp_assoc_t spp_assoc_id;
- * struct sockaddr_storage spp_address;
- * uint32_t spp_hbinterval;
- * uint16_t spp_pathmaxrxt;
- * };
- *
- * spp_assoc_id - (UDP style socket) This is filled in the application,
- * and identifies the association for this query.
+ * sctp_assoc_t spp_assoc_id;
+ * struct sockaddr_storage spp_address;
+ * uint32_t spp_hbinterval;
+ * uint16_t spp_pathmaxrxt;
+ * uint32_t spp_pathmtu;
+ * uint32_t spp_sackdelay;
+ * uint32_t spp_flags;
+ * };
+ *
+ * spp_assoc_id - (one-to-many style socket) This is filled in the
+ * application, and identifies the association for
+ * this query.
* spp_address - This specifies which address is of interest.
* spp_hbinterval - This contains the value of the heartbeat interval,
- * in milliseconds. A value of 0, when modifying the
- * parameter, specifies that the heartbeat on this
- * address should be disabled. A value of UINT32_MAX
- * (4294967295), when modifying the parameter,
- * specifies that a heartbeat should be sent
- * immediately to the peer address, and the current
- * interval should remain unchanged.
+ * in milliseconds. If a value of zero
+ * is present in this field then no changes are to
+ * be made to this parameter.
* spp_pathmaxrxt - This contains the maximum number of
* retransmissions before this address shall be
- * considered unreachable.
+ * considered unreachable. If a value of zero
+ * is present in this field then no changes are to
+ * be made to this parameter.
+ * spp_pathmtu - When Path MTU discovery is disabled the value
+ * specified here will be the "fixed" path mtu.
+ * Note that if the spp_address field is empty
+ * then all associations on this address will
+ * have this fixed path mtu set upon them.
+ *
+ * spp_sackdelay - When delayed sack is enabled, this value specifies
+ * the number of milliseconds that sacks will be delayed
+ * for. This value will apply to all addresses of an
+ * association if the spp_address field is empty. Note
+ * also, that if delayed sack is enabled and this
+ * value is set to 0, no change is made to the last
+ * recorded delayed sack timer value.
+ *
+ * spp_flags - These flags are used to control various features
+ * on an association. The flag field may contain
+ * zero or more of the following options.
+ *
+ * SPP_HB_ENABLE - Enable heartbeats on the
+ * specified address. Note that if the address
+ * field is empty all addresses for the association
+ * have heartbeats enabled upon them.
+ *
+ * SPP_HB_DISABLE - Disable heartbeats on the
+ * speicifed address. Note that if the address
+ * field is empty all addresses for the association
+ * will have their heartbeats disabled. Note also
+ * that SPP_HB_ENABLE and SPP_HB_DISABLE are
+ * mutually exclusive, only one of these two should
+ * be specified. Enabling both fields will have
+ * undetermined results.
+ *
+ * SPP_HB_DEMAND - Request a user initiated heartbeat
+ * to be made immediately.
+ *
+ * SPP_PMTUD_ENABLE - This field will enable PMTU
+ * discovery upon the specified address. Note that
+ * if the address feild is empty then all addresses
+ * on the association are effected.
+ *
+ * SPP_PMTUD_DISABLE - This field will disable PMTU
+ * discovery upon the specified address. Note that
+ * if the address feild is empty then all addresses
+ * on the association are effected. Not also that
+ * SPP_PMTUD_ENABLE and SPP_PMTUD_DISABLE are mutually
+ * exclusive. Enabling both will have undetermined
+ * results.
+ *
+ * SPP_SACKDELAY_ENABLE - Setting this flag turns
+ * on delayed sack. The time specified in spp_sackdelay
+ * is used to specify the sack delay for this address. Note
+ * that if spp_address is empty then all addresses will
+ * enable delayed sack and take on the sack delay
+ * value specified in spp_sackdelay.
+ * SPP_SACKDELAY_DISABLE - Setting this flag turns
+ * off delayed sack. If the spp_address field is blank then
+ * delayed sack is disabled for the entire association. Note
+ * also that this field is mutually exclusive to
+ * SPP_SACKDELAY_ENABLE, setting both will have undefined
+ * results.
*/
+int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
+ struct sctp_transport *trans,
+ struct sctp_association *asoc,
+ struct sctp_sock *sp,
+ int hb_change,
+ int pmtud_change,
+ int sackdelay_change)
+{
+ int error;
+
+ if (params->spp_flags & SPP_HB_DEMAND && trans) {
+ error = sctp_primitive_REQUESTHEARTBEAT (trans->asoc, trans);
+ if (error)
+ return error;
+ }
+
+ if (params->spp_hbinterval) {
+ if (trans) {
+ trans->hbinterval = msecs_to_jiffies(params->spp_hbinterval);
+ } else if (asoc) {
+ asoc->hbinterval = msecs_to_jiffies(params->spp_hbinterval);
+ } else {
+ sp->hbinterval = params->spp_hbinterval;
+ }
+ }
+
+ if (hb_change) {
+ if (trans) {
+ trans->param_flags =
+ (trans->param_flags & ~SPP_HB) | hb_change;
+ } else if (asoc) {
+ asoc->param_flags =
+ (asoc->param_flags & ~SPP_HB) | hb_change;
+ } else {
+ sp->param_flags =
+ (sp->param_flags & ~SPP_HB) | hb_change;
+ }
+ }
+
+ if (params->spp_pathmtu) {
+ if (trans) {
+ trans->pathmtu = params->spp_pathmtu;
+ sctp_assoc_sync_pmtu(asoc);
+ } else if (asoc) {
+ asoc->pathmtu = params->spp_pathmtu;
+ sctp_frag_point(sp, params->spp_pathmtu);
+ } else {
+ sp->pathmtu = params->spp_pathmtu;
+ }
+ }
+
+ if (pmtud_change) {
+ if (trans) {
+ int update = (trans->param_flags & SPP_PMTUD_DISABLE) &&
+ (params->spp_flags & SPP_PMTUD_ENABLE);
+ trans->param_flags =
+ (trans->param_flags & ~SPP_PMTUD) | pmtud_change;
+ if (update) {
+ sctp_transport_pmtu(trans);
+ sctp_assoc_sync_pmtu(asoc);
+ }
+ } else if (asoc) {
+ asoc->param_flags =
+ (asoc->param_flags & ~SPP_PMTUD) | pmtud_change;
+ } else {
+ sp->param_flags =
+ (sp->param_flags & ~SPP_PMTUD) | pmtud_change;
+ }
+ }
+
+ if (params->spp_sackdelay) {
+ if (trans) {
+ trans->sackdelay =
+ msecs_to_jiffies(params->spp_sackdelay);
+ } else if (asoc) {
+ asoc->sackdelay =
+ msecs_to_jiffies(params->spp_sackdelay);
+ } else {
+ sp->sackdelay = params->spp_sackdelay;
+ }
+ }
+
+ if (sackdelay_change) {
+ if (trans) {
+ trans->param_flags =
+ (trans->param_flags & ~SPP_SACKDELAY) |
+ sackdelay_change;
+ } else if (asoc) {
+ asoc->param_flags =
+ (asoc->param_flags & ~SPP_SACKDELAY) |
+ sackdelay_change;
+ } else {
+ sp->param_flags =
+ (sp->param_flags & ~SPP_SACKDELAY) |
+ sackdelay_change;
+ }
+ }
+
+ if (params->spp_pathmaxrxt) {
+ if (trans) {
+ trans->pathmaxrxt = params->spp_pathmaxrxt;
+ } else if (asoc) {
+ asoc->pathmaxrxt = params->spp_pathmaxrxt;
+ } else {
+ sp->pathmaxrxt = params->spp_pathmaxrxt;
+ }
+ }
+
+ return 0;
+}
+
static int sctp_setsockopt_peer_addr_params(struct sock *sk,
char __user *optval, int optlen)
{
- struct sctp_paddrparams params;
- struct sctp_transport *trans;
+ struct sctp_paddrparams params;
+ struct sctp_transport *trans = NULL;
+ struct sctp_association *asoc = NULL;
+ struct sctp_sock *sp = sctp_sk(sk);
int error;
+ int hb_change, pmtud_change, sackdelay_change;
if (optlen != sizeof(struct sctp_paddrparams))
- return -EINVAL;
+ return - EINVAL;
+
if (copy_from_user(&params, optval, optlen))
return -EFAULT;
- /*
- * API 7. Socket Options (setting the default value for the endpoint)
- * All options that support specific settings on an association by
- * filling in either an association id variable or a sockaddr_storage
- * SHOULD also support setting of the same value for the entire endpoint
- * (i.e. future associations). To accomplish this the following logic is
- * used when setting one of these options:
-
- * c) If neither the sockaddr_storage or association identification is
- * set i.e. the sockaddr_storage is set to all 0's (INADDR_ANY) and
- * the association identification is 0, the settings are a default
- * and to be applied to the endpoint (all future associations).
- */
+ /* Validate flags and value parameters. */
+ hb_change = params.spp_flags & SPP_HB;
+ pmtud_change = params.spp_flags & SPP_PMTUD;
+ sackdelay_change = params.spp_flags & SPP_SACKDELAY;
+
+ if (hb_change == SPP_HB ||
+ pmtud_change == SPP_PMTUD ||
+ sackdelay_change == SPP_SACKDELAY ||
+ params.spp_sackdelay > 500 ||
+ (params.spp_pathmtu
+ && params.spp_pathmtu < SCTP_DEFAULT_MINSEGMENT))
+ return -EINVAL;
- /* update default value for endpoint (all future associations) */
- if (!params.spp_assoc_id &&
- sctp_is_any(( union sctp_addr *)&params.spp_address)) {
- /* Manual heartbeat on an endpoint is invalid. */
- if (0xffffffff == params.spp_hbinterval)
+ /* If an address other than INADDR_ANY is specified, and
+ * no transport is found, then the request is invalid.
+ */
+ if (!sctp_is_any(( union sctp_addr *)&params.spp_address)) {
+ trans = sctp_addr_id2transport(sk, &params.spp_address,
+ params.spp_assoc_id);
+ if (!trans)
return -EINVAL;
- else if (params.spp_hbinterval)
- sctp_sk(sk)->paddrparam.spp_hbinterval =
- params.spp_hbinterval;
- if (params.spp_pathmaxrxt)
- sctp_sk(sk)->paddrparam.spp_pathmaxrxt =
- params.spp_pathmaxrxt;
- return 0;
}
- trans = sctp_addr_id2transport(sk, &params.spp_address,
- params.spp_assoc_id);
- if (!trans)
+ /* Get association, if assoc_id != 0 and the socket is a one
+ * to many style socket, and an association was not found, then
+ * the id was invalid.
+ */
+ asoc = sctp_id2assoc(sk, params.spp_assoc_id);
+ if (!asoc && params.spp_assoc_id && sctp_style(sk, UDP))
return -EINVAL;
- /* Applications can enable or disable heartbeats for any peer address
- * of an association, modify an address's heartbeat interval, force a
- * heartbeat to be sent immediately, and adjust the address's maximum
- * number of retransmissions sent before an address is considered
- * unreachable.
- *
- * The value of the heartbeat interval, in milliseconds. A value of
- * UINT32_MAX (4294967295), when modifying the parameter, specifies
- * that a heartbeat should be sent immediately to the peer address,
- * and the current interval should remain unchanged.
+ /* Heartbeat demand can only be sent on a transport or
+ * association, but not a socket.
*/
- if (0xffffffff == params.spp_hbinterval) {
- error = sctp_primitive_REQUESTHEARTBEAT (trans->asoc, trans);
- if (error)
- return error;
- } else {
- /* The value of the heartbeat interval, in milliseconds. A value of 0,
- * when modifying the parameter, specifies that the heartbeat on this
- * address should be disabled.
+ if (params.spp_flags & SPP_HB_DEMAND && !trans && !asoc)
+ return -EINVAL;
+
+ /* Process parameters. */
+ error = sctp_apply_peer_addr_params(&params, trans, asoc, sp,
+ hb_change, pmtud_change,
+ sackdelay_change);
+
+ if (error)
+ return error;
+
+ /* If changes are for association, also apply parameters to each
+ * transport.
*/
- if (params.spp_hbinterval) {
- trans->hb_allowed = 1;
- trans->hb_interval =
- msecs_to_jiffies(params.spp_hbinterval);
- } else
- trans->hb_allowed = 0;
+ if (!trans && asoc) {
+ struct list_head *pos;
+
+ list_for_each(pos, &asoc->peer.transport_addr_list) {
+ trans = list_entry(pos, struct sctp_transport,
+ transports);
+ sctp_apply_peer_addr_params(&params, trans, asoc, sp,
+ hb_change, pmtud_change,
+ sackdelay_change);
+ }
}
- /* spp_pathmaxrxt contains the maximum number of retransmissions
- * before this address shall be considered unreachable.
- */
- if (params.spp_pathmaxrxt)
- trans->max_retrans = params.spp_pathmaxrxt;
+ return 0;
+}
+
+/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
+ *
+ * This options will get or set the delayed ack timer. The time is set
+ * in milliseconds. If the assoc_id is 0, then this sets or gets the
+ * endpoints default delayed ack timer value. If the assoc_id field is
+ * non-zero, then the set or get effects the specified association.
+ *
+ * struct sctp_assoc_value {
+ * sctp_assoc_t assoc_id;
+ * uint32_t assoc_value;
+ * };
+ *
+ * assoc_id - This parameter, indicates which association the
+ * user is preforming an action upon. Note that if
+ * this field's value is zero then the endpoints
+ * default value is changed (effecting future
+ * associations only).
+ *
+ * assoc_value - This parameter contains the number of milliseconds
+ * that the user is requesting the delayed ACK timer
+ * be set to. Note that this value is defined in
+ * the standard to be between 200 and 500 milliseconds.
+ *
+ * Note: a value of zero will leave the value alone,
+ * but disable SACK delay. A non-zero value will also
+ * enable SACK delay.
+ */
+static int sctp_setsockopt_delayed_ack_time(struct sock *sk,
+ char __user *optval, int optlen)
+{
+ struct sctp_assoc_value params;
+ struct sctp_transport *trans = NULL;
+ struct sctp_association *asoc = NULL;
+ struct sctp_sock *sp = sctp_sk(sk);
+
+ if (optlen != sizeof(struct sctp_assoc_value))
+ return - EINVAL;
+
+ if (copy_from_user(&params, optval, optlen))
+ return -EFAULT;
+
+ /* Validate value parameter. */
+ if (params.assoc_value > 500)
+ return -EINVAL;
+
+ /* Get association, if assoc_id != 0 and the socket is a one
+ * to many style socket, and an association was not found, then
+ * the id was invalid.
+ */
+ asoc = sctp_id2assoc(sk, params.assoc_id);
+ if (!asoc && params.assoc_id && sctp_style(sk, UDP))
+ return -EINVAL;
+
+ if (params.assoc_value) {
+ if (asoc) {
+ asoc->sackdelay =
+ msecs_to_jiffies(params.assoc_value);
+ asoc->param_flags =
+ (asoc->param_flags & ~SPP_SACKDELAY) |
+ SPP_SACKDELAY_ENABLE;
+ } else {
+ sp->sackdelay = params.assoc_value;
+ sp->param_flags =
+ (sp->param_flags & ~SPP_SACKDELAY) |
+ SPP_SACKDELAY_ENABLE;
+ }
+ } else {
+ if (asoc) {
+ asoc->param_flags =
+ (asoc->param_flags & ~SPP_SACKDELAY) |
+ SPP_SACKDELAY_DISABLE;
+ } else {
+ sp->param_flags =
+ (sp->param_flags & ~SPP_SACKDELAY) |
+ SPP_SACKDELAY_DISABLE;
+ }
+ }
+
+ /* If change is for association, also apply to each transport. */
+ if (asoc) {
+ struct list_head *pos;
+
+ list_for_each(pos, &asoc->peer.transport_addr_list) {
+ trans = list_entry(pos, struct sctp_transport,
+ transports);
+ if (params.assoc_value) {
+ trans->sackdelay =
+ msecs_to_jiffies(params.assoc_value);
+ trans->param_flags =
+ (trans->param_flags & ~SPP_SACKDELAY) |
+ SPP_SACKDELAY_ENABLE;
+ } else {
+ trans->param_flags =
+ (trans->param_flags & ~SPP_SACKDELAY) |
+ SPP_SACKDELAY_DISABLE;
+ }
+ }
+ }
+
return 0;
}
@@ -2338,7 +2606,7 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optl
/* Update the frag_point of the existing associations. */
list_for_each(pos, &(sp->ep->asocs)) {
asoc = list_entry(pos, struct sctp_association, asocs);
- asoc->frag_point = sctp_frag_point(sp, asoc->pmtu);
+ asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu);
}
return 0;
@@ -2495,6 +2763,10 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen);
break;
+ case SCTP_DELAYED_ACK_TIME:
+ retval = sctp_setsockopt_delayed_ack_time(sk, optval, optlen);
+ break;
+
case SCTP_INITMSG:
retval = sctp_setsockopt_initmsg(sk, optval, optlen);
break;
@@ -2719,8 +2991,13 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
/* Default Peer Address Parameters. These defaults can
* be modified via SCTP_PEER_ADDR_PARAMS
*/
- sp->paddrparam.spp_hbinterval = jiffies_to_msecs(sctp_hb_interval);
- sp->paddrparam.spp_pathmaxrxt = sctp_max_retrans_path;
+ sp->hbinterval = jiffies_to_msecs(sctp_hb_interval);
+ sp->pathmaxrxt = sctp_max_retrans_path;
+ sp->pathmtu = 0; // allow default discovery
+ sp->sackdelay = sctp_sack_timeout;
+ sp->param_flags = SPP_HB_ENABLE |
+ SPP_PMTUD_ENABLE |
+ SPP_SACKDELAY_ENABLE;
/* If enabled no SCTP message fragmentation will be performed.
* Configure through SCTP_DISABLE_FRAGMENTS socket option.
@@ -2869,7 +3146,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len,
status.sstat_primary.spinfo_cwnd = transport->cwnd;
status.sstat_primary.spinfo_srtt = transport->srtt;
status.sstat_primary.spinfo_rto = jiffies_to_msecs(transport->rto);
- status.sstat_primary.spinfo_mtu = transport->pmtu;
+ status.sstat_primary.spinfo_mtu = transport->pathmtu;
if (status.sstat_primary.spinfo_state == SCTP_UNKNOWN)
status.sstat_primary.spinfo_state = SCTP_ACTIVE;
@@ -2928,7 +3205,7 @@ static int sctp_getsockopt_peer_addr_info(struct sock *sk, int len,
pinfo.spinfo_cwnd = transport->cwnd;
pinfo.spinfo_srtt = transport->srtt;
pinfo.spinfo_rto = jiffies_to_msecs(transport->rto);
- pinfo.spinfo_mtu = transport->pmtu;
+ pinfo.spinfo_mtu = transport->pathmtu;
if (pinfo.spinfo_state == SCTP_UNKNOWN)
pinfo.spinfo_state = SCTP_ACTIVE;
@@ -3090,69 +3367,227 @@ out:
* address's parameters:
*
* struct sctp_paddrparams {
- * sctp_assoc_t spp_assoc_id;
- * struct sockaddr_storage spp_address;
- * uint32_t spp_hbinterval;
- * uint16_t spp_pathmaxrxt;
- * };
- *
- * spp_assoc_id - (UDP style socket) This is filled in the application,
- * and identifies the association for this query.
+ * sctp_assoc_t spp_assoc_id;
+ * struct sockaddr_storage spp_address;
+ * uint32_t spp_hbinterval;
+ * uint16_t spp_pathmaxrxt;
+ * uint32_t spp_pathmtu;
+ * uint32_t spp_sackdelay;
+ * uint32_t spp_flags;
+ * };
+ *
+ * spp_assoc_id - (one-to-many style socket) This is filled in the
+ * application, and identifies the association for
+ * this query.
* spp_address - This specifies which address is of interest.
* spp_hbinterval - This contains the value of the heartbeat interval,
- * in milliseconds. A value of 0, when modifying the
- * parameter, specifies that the heartbeat on this
- * address should be disabled. A value of UINT32_MAX
- * (4294967295), when modifying the parameter,
- * specifies that a heartbeat should be sent
- * immediately to the peer address, and the current
- * interval should remain unchanged.
+ * in milliseconds. If a value of zero
+ * is present in this field then no changes are to
+ * be made to this parameter.
* spp_pathmaxrxt - This contains the maximum number of
* retransmissions before this address shall be
- * considered unreachable.
+ * considered unreachable. If a value of zero
+ * is present in this field then no changes are to
+ * be made to this parameter.
+ * spp_pathmtu - When Path MTU discovery is disabled the value
+ * specified here will be the "fixed" path mtu.
+ * Note that if the spp_address field is empty
+ * then all associations on this address will
+ * have this fixed path mtu set upon them.
+ *
+ * spp_sackdelay - When delayed sack is enabled, this value specifies
+ * the number of milliseconds that sacks will be delayed
+ * for. This value will apply to all addresses of an
+ * association if the spp_address field is empty. Note
+ * also, that if delayed sack is enabled and this
+ * value is set to 0, no change is made to the last
+ * recorded delayed sack timer value.
+ *
+ * spp_flags - These flags are used to control various features
+ * on an association. The flag field may contain
+ * zero or more of the following options.
+ *
+ * SPP_HB_ENABLE - Enable heartbeats on the
+ * specified address. Note that if the address
+ * field is empty all addresses for the association
+ * have heartbeats enabled upon them.
+ *
+ * SPP_HB_DISABLE - Disable heartbeats on the
+ * speicifed address. Note that if the address
+ * field is empty all addresses for the association
+ * will have their heartbeats disabled. Note also
+ * that SPP_HB_ENABLE and SPP_HB_DISABLE are
+ * mutually exclusive, only one of these two should
+ * be specified. Enabling both fields will have
+ * undetermined results.
+ *
+ * SPP_HB_DEMAND - Request a user initiated heartbeat
+ * to be made immediately.
+ *
+ * SPP_PMTUD_ENABLE - This field will enable PMTU
+ * discovery upon the specified address. Note that
+ * if the address feild is empty then all addresses
+ * on the association are effected.
+ *
+ * SPP_PMTUD_DISABLE - This field will disable PMTU
+ * discovery upon the specified address. Note that
+ * if the address feild is empty then all addresses
+ * on the association are effected. Not also that
+ * SPP_PMTUD_ENABLE and SPP_PMTUD_DISABLE are mutually
+ * exclusive. Enabling both will have undetermined
+ * results.
+ *
+ * SPP_SACKDELAY_ENABLE - Setting this flag turns
+ * on delayed sack. The time specified in spp_sackdelay
+ * is used to specify the sack delay for this address. Note
+ * that if spp_address is empty then all addresses will
+ * enable delayed sack and take on the sack delay
+ * value specified in spp_sackdelay.
+ * SPP_SACKDELAY_DISABLE - Setting this flag turns
+ * off delayed sack. If the spp_address field is blank then
+ * delayed sack is disabled for the entire association. Note
+ * also that this field is mutually exclusive to
+ * SPP_SACKDELAY_ENABLE, setting both will have undefined
+ * results.
*/
static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
- char __user *optval, int __user *optlen)
+ char __user *optval, int __user *optlen)
{
- struct sctp_paddrparams params;
- struct sctp_transport *trans;
+ struct sctp_paddrparams params;
+ struct sctp_transport *trans = NULL;
+ struct sctp_association *asoc = NULL;
+ struct sctp_sock *sp = sctp_sk(sk);
if (len != sizeof(struct sctp_paddrparams))
return -EINVAL;
+
if (copy_from_user(&params, optval, len))
return -EFAULT;
- /* If no association id is specified retrieve the default value
- * for the endpoint that will be used for all future associations
+ /* If an address other than INADDR_ANY is specified, and
+ * no transport is found, then the request is invalid.
*/
- if (!params.spp_assoc_id &&
- sctp_is_any(( union sctp_addr *)&params.spp_address)) {
- params.spp_hbinterval = sctp_sk(sk)->paddrparam.spp_hbinterval;
- params.spp_pathmaxrxt = sctp_sk(sk)->paddrparam.spp_pathmaxrxt;
-
- goto done;
+ if (!sctp_is_any(( union sctp_addr *)&params.spp_address)) {
+ trans = sctp_addr_id2transport(sk, &params.spp_address,
+ params.spp_assoc_id);
+ if (!trans) {
+ SCTP_DEBUG_PRINTK("Failed no transport\n");
+ return -EINVAL;
+ }
}
- trans = sctp_addr_id2transport(sk, &params.spp_address,
- params.spp_assoc_id);
- if (!trans)
+ /* Get association, if assoc_id != 0 and the socket is a one
+ * to many style socket, and an association was not found, then
+ * the id was invalid.
+ */
+ asoc = sctp_id2assoc(sk, params.spp_assoc_id);
+ if (!asoc && params.spp_assoc_id && sctp_style(sk, UDP)) {
+ SCTP_DEBUG_PRINTK("Failed no association\n");
return -EINVAL;
+ }
- /* The value of the heartbeat interval, in milliseconds. A value of 0,
- * when modifying the parameter, specifies that the heartbeat on this
- * address should be disabled.
- */
- if (!trans->hb_allowed)
- params.spp_hbinterval = 0;
- else
- params.spp_hbinterval = jiffies_to_msecs(trans->hb_interval);
+ if (trans) {
+ /* Fetch transport values. */
+ params.spp_hbinterval = jiffies_to_msecs(trans->hbinterval);
+ params.spp_pathmtu = trans->pathmtu;
+ params.spp_pathmaxrxt = trans->pathmaxrxt;
+ params.spp_sackdelay = jiffies_to_msecs(trans->sackdelay);
+
+ /*draft-11 doesn't say what to return in spp_flags*/
+ params.spp_flags = trans->param_flags;
+ } else if (asoc) {
+ /* Fetch association values. */
+ params.spp_hbinterval = jiffies_to_msecs(asoc->hbinterval);
+ params.spp_pathmtu = asoc->pathmtu;
+ params.spp_pathmaxrxt = asoc->pathmaxrxt;
+ params.spp_sackdelay = jiffies_to_msecs(asoc->sackdelay);
+
+ /*draft-11 doesn't say what to return in spp_flags*/
+ params.spp_flags = asoc->param_flags;
+ } else {
+ /* Fetch socket values. */
+ params.spp_hbinterval = sp->hbinterval;
+ params.spp_pathmtu = sp->pathmtu;
+ params.spp_sackdelay = sp->sackdelay;
+ params.spp_pathmaxrxt = sp->pathmaxrxt;
+
+ /*draft-11 doesn't say what to return in spp_flags*/
+ params.spp_flags = sp->param_flags;
+ }
- /* spp_pathmaxrxt contains the maximum number of retransmissions
- * before this address shall be considered unreachable.
- */
- params.spp_pathmaxrxt = trans->max_retrans;
+ if (copy_to_user(optval, &params, len))
+ return -EFAULT;
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+
+ return 0;
+}
+
+/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
+ *
+ * This options will get or set the delayed ack timer. The time is set
+ * in milliseconds. If the assoc_id is 0, then this sets or gets the
+ * endpoints default delayed ack timer value. If the assoc_id field is
+ * non-zero, then the set or get effects the specified association.
+ *
+ * struct sctp_assoc_value {
+ * sctp_assoc_t assoc_id;
+ * uint32_t assoc_value;
+ * };
+ *
+ * assoc_id - This parameter, indicates which association the
+ * user is preforming an action upon. Note that if
+ * this field's value is zero then the endpoints
+ * default value is changed (effecting future
+ * associations only).
+ *
+ * assoc_value - This parameter contains the number of milliseconds
+ * that the user is requesting the delayed ACK timer
+ * be set to. Note that this value is defined in
+ * the standard to be between 200 and 500 milliseconds.
+ *
+ * Note: a value of zero will leave the value alone,
+ * but disable SACK delay. A non-zero value will also
+ * enable SACK delay.
+ */
+static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len,
+ char __user *optval,
+ int __user *optlen)
+{
+ struct sctp_assoc_value params;
+ struct sctp_association *asoc = NULL;
+ struct sctp_sock *sp = sctp_sk(sk);
+
+ if (len != sizeof(struct sctp_assoc_value))
+ return - EINVAL;
+
+ if (copy_from_user(&params, optval, len))
+ return -EFAULT;
+
+ /* Get association, if assoc_id != 0 and the socket is a one
+ * to many style socket, and an association was not found, then
+ * the id was invalid.
+ */
+ asoc = sctp_id2assoc(sk, params.assoc_id);
+ if (!asoc && params.assoc_id && sctp_style(sk, UDP))
+ return -EINVAL;
+
+ if (asoc) {
+ /* Fetch association values. */
+ if (asoc->param_flags & SPP_SACKDELAY_ENABLE)
+ params.assoc_value = jiffies_to_msecs(
+ asoc->sackdelay);
+ else
+ params.assoc_value = 0;
+ } else {
+ /* Fetch socket values. */
+ if (sp->param_flags & SPP_SACKDELAY_ENABLE)
+ params.assoc_value = sp->sackdelay;
+ else
+ params.assoc_value = 0;
+ }
-done:
if (copy_to_user(optval, &params, len))
return -EFAULT;
@@ -4019,6 +4454,10 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_peer_addr_params(sk, len, optval,
optlen);
break;
+ case SCTP_DELAYED_ACK_TIME:
+ retval = sctp_getsockopt_delayed_ack_time(sk, len, optval,
+ optlen);
+ break;
case SCTP_INITMSG:
retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
break;
@@ -4426,7 +4865,7 @@ cleanup:
* tcp_poll(). Note that, based on these implementations, we don't
* lock the socket in this function, even though it seems that,
* ideally, locking or some other mechanisms can be used to ensure
- * the integrity of the counters (sndbuf and wmem_queued) used
+ * the integrity of the counters (sndbuf and wmem_alloc) used
* in this place. We assume that we don't need locks either until proven
* otherwise.
*
@@ -4833,10 +5272,6 @@ static void sctp_wfree(struct sk_buff *skb)
sizeof(struct sk_buff) +
sizeof(struct sctp_chunk);
- sk->sk_wmem_queued -= SCTP_DATA_SNDSIZE(chunk) +
- sizeof(struct sk_buff) +
- sizeof(struct sctp_chunk);
-
atomic_sub(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
sock_wfree(skb);
@@ -4920,7 +5355,7 @@ void sctp_write_space(struct sock *sk)
/* Is there any sndbuf space available on the socket?
*
- * Note that wmem_queued is the sum of the send buffers on all of the
+ * Note that sk_wmem_alloc is the sum of the send buffers on all of the
* associations on the same socket. For a UDP-style socket with
* multiple associations, it is possible for it to be "unwriteable"
* prematurely. I assume that this is acceptable because
@@ -4933,7 +5368,7 @@ static int sctp_writeable(struct sock *sk)
{
int amt = 0;
- amt = sk->sk_sndbuf - sk->sk_wmem_queued;
+ amt = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc);
if (amt < 0)
amt = 0;
return amt;
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 268ddaf2dc0f..68d73e2dd155 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -86,10 +86,13 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
peer->init_sent_count = 0;
peer->state = SCTP_ACTIVE;
- peer->hb_allowed = 0;
+ peer->param_flags = SPP_HB_DISABLE |
+ SPP_PMTUD_ENABLE |
+ SPP_SACKDELAY_ENABLE;
+ peer->hbinterval = 0;
/* Initialize the default path max_retrans. */
- peer->max_retrans = sctp_max_retrans_path;
+ peer->pathmaxrxt = sctp_max_retrans_path;
peer->error_count = 0;
INIT_LIST_HEAD(&peer->transmitted);
@@ -229,10 +232,10 @@ void sctp_transport_pmtu(struct sctp_transport *transport)
dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL);
if (dst) {
- transport->pmtu = dst_mtu(dst);
+ transport->pathmtu = dst_mtu(dst);
dst_release(dst);
} else
- transport->pmtu = SCTP_DEFAULT_MAXSEGMENT;
+ transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT;
}
/* Caches the dst entry and source address for a transport's destination
@@ -254,8 +257,11 @@ void sctp_transport_route(struct sctp_transport *transport,
af->get_saddr(asoc, dst, daddr, &transport->saddr);
transport->dst = dst;
+ if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) {
+ return;
+ }
if (dst) {
- transport->pmtu = dst_mtu(dst);
+ transport->pathmtu = dst_mtu(dst);
/* Initialize sk->sk_rcv_saddr, if the transport is the
* association's active path for getsockname().
@@ -264,7 +270,7 @@ void sctp_transport_route(struct sctp_transport *transport,
opt->pf->af->to_sk_saddr(&transport->saddr,
asoc->base.sk);
} else
- transport->pmtu = SCTP_DEFAULT_MAXSEGMENT;
+ transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT;
}
/* Hold a reference to a transport. */
@@ -369,7 +375,7 @@ void sctp_transport_raise_cwnd(struct sctp_transport *transport,
ssthresh = transport->ssthresh;
pba = transport->partial_bytes_acked;
- pmtu = transport->asoc->pmtu;
+ pmtu = transport->asoc->pathmtu;
if (cwnd <= ssthresh) {
/* RFC 2960 7.2.1, sctpimpguide-05 2.14.2 When cwnd is less
@@ -441,8 +447,8 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport,
* partial_bytes_acked = 0
*/
transport->ssthresh = max(transport->cwnd/2,
- 4*transport->asoc->pmtu);
- transport->cwnd = transport->asoc->pmtu;
+ 4*transport->asoc->pathmtu);
+ transport->cwnd = transport->asoc->pathmtu;
break;
case SCTP_LOWER_CWND_FAST_RTX:
@@ -459,7 +465,7 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport,
* partial_bytes_acked = 0
*/
transport->ssthresh = max(transport->cwnd/2,
- 4*transport->asoc->pmtu);
+ 4*transport->asoc->pathmtu);
transport->cwnd = transport->ssthresh;
break;
@@ -479,7 +485,7 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport,
if ((jiffies - transport->last_time_ecne_reduced) >
transport->rtt) {
transport->ssthresh = max(transport->cwnd/2,
- 4*transport->asoc->pmtu);
+ 4*transport->asoc->pathmtu);
transport->cwnd = transport->ssthresh;
transport->last_time_ecne_reduced = jiffies;
}
@@ -496,7 +502,7 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport,
*/
if ((jiffies - transport->last_time_used) > transport->rto)
transport->cwnd = max(transport->cwnd/2,
- 4*transport->asoc->pmtu);
+ 4*transport->asoc->pathmtu);
break;
};
@@ -511,7 +517,7 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport,
unsigned long sctp_transport_timeout(struct sctp_transport *t)
{
unsigned long timeout;
- timeout = t->hb_interval + t->rto + sctp_jitter(t->rto);
+ timeout = t->hbinterval + t->rto + sctp_jitter(t->rto);
timeout += jiffies;
return timeout;
}
diff --git a/net/socket.c b/net/socket.c
index 3145103cdf54..06fa217f58a9 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -640,154 +640,150 @@ static void sock_aio_dtor(struct kiocb *iocb)
kfree(iocb->private);
}
-/*
- * Read data from a socket. ubuf is a user mode pointer. We make sure the user
- * area ubuf...ubuf+size-1 is writable before asking the protocol.
- */
-
-static ssize_t sock_aio_read(struct kiocb *iocb, char __user *ubuf,
- size_t size, loff_t pos)
+static ssize_t sock_sendpage(struct file *file, struct page *page,
+ int offset, size_t size, loff_t *ppos, int more)
{
- struct sock_iocb *x, siocb;
struct socket *sock;
int flags;
- if (pos != 0)
- return -ESPIPE;
- if (size==0) /* Match SYS5 behaviour */
- return 0;
+ sock = file->private_data;
- if (is_sync_kiocb(iocb))
- x = &siocb;
- else {
- x = kmalloc(sizeof(struct sock_iocb), GFP_KERNEL);
- if (!x)
- return -ENOMEM;
+ flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
+ if (more)
+ flags |= MSG_MORE;
+
+ return sock->ops->sendpage(sock, page, offset, size, flags);
+}
+
+static struct sock_iocb *alloc_sock_iocb(struct kiocb *iocb,
+ char __user *ubuf, size_t size, struct sock_iocb *siocb)
+{
+ if (!is_sync_kiocb(iocb)) {
+ siocb = kmalloc(sizeof(*siocb), GFP_KERNEL);
+ if (!siocb)
+ return NULL;
iocb->ki_dtor = sock_aio_dtor;
}
- iocb->private = x;
- x->kiocb = iocb;
- sock = iocb->ki_filp->private_data;
- x->async_msg.msg_name = NULL;
- x->async_msg.msg_namelen = 0;
- x->async_msg.msg_iov = &x->async_iov;
- x->async_msg.msg_iovlen = 1;
- x->async_msg.msg_control = NULL;
- x->async_msg.msg_controllen = 0;
- x->async_iov.iov_base = ubuf;
- x->async_iov.iov_len = size;
- flags = !(iocb->ki_filp->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
+ siocb->kiocb = iocb;
+ siocb->async_iov.iov_base = ubuf;
+ siocb->async_iov.iov_len = size;
- return __sock_recvmsg(iocb, sock, &x->async_msg, size, flags);
+ iocb->private = siocb;
+ return siocb;
}
+static ssize_t do_sock_read(struct msghdr *msg, struct kiocb *iocb,
+ struct file *file, struct iovec *iov, unsigned long nr_segs)
+{
+ struct socket *sock = file->private_data;
+ size_t size = 0;
+ int i;
-/*
- * Write data to a socket. We verify that the user area ubuf..ubuf+size-1
- * is readable by the user process.
- */
+ for (i = 0 ; i < nr_segs ; i++)
+ size += iov[i].iov_len;
-static ssize_t sock_aio_write(struct kiocb *iocb, const char __user *ubuf,
- size_t size, loff_t pos)
+ msg->msg_name = NULL;
+ msg->msg_namelen = 0;
+ msg->msg_control = NULL;
+ msg->msg_controllen = 0;
+ msg->msg_iov = (struct iovec *) iov;
+ msg->msg_iovlen = nr_segs;
+ msg->msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0;
+
+ return __sock_recvmsg(iocb, sock, msg, size, msg->msg_flags);
+}
+
+static ssize_t sock_readv(struct file *file, const struct iovec *iov,
+ unsigned long nr_segs, loff_t *ppos)
{
- struct sock_iocb *x, siocb;
- struct socket *sock;
-
+ struct kiocb iocb;
+ struct sock_iocb siocb;
+ struct msghdr msg;
+ int ret;
+
+ init_sync_kiocb(&iocb, NULL);
+ iocb.private = &siocb;
+
+ ret = do_sock_read(&msg, &iocb, file, (struct iovec *)iov, nr_segs);
+ if (-EIOCBQUEUED == ret)
+ ret = wait_on_sync_kiocb(&iocb);
+ return ret;
+}
+
+static ssize_t sock_aio_read(struct kiocb *iocb, char __user *ubuf,
+ size_t count, loff_t pos)
+{
+ struct sock_iocb siocb, *x;
+
if (pos != 0)
return -ESPIPE;
- if(size==0) /* Match SYS5 behaviour */
+ if (count == 0) /* Match SYS5 behaviour */
return 0;
- if (is_sync_kiocb(iocb))
- x = &siocb;
- else {
- x = kmalloc(sizeof(struct sock_iocb), GFP_KERNEL);
- if (!x)
- return -ENOMEM;
- iocb->ki_dtor = sock_aio_dtor;
- }
- iocb->private = x;
- x->kiocb = iocb;
- sock = iocb->ki_filp->private_data;
-
- x->async_msg.msg_name = NULL;
- x->async_msg.msg_namelen = 0;
- x->async_msg.msg_iov = &x->async_iov;
- x->async_msg.msg_iovlen = 1;
- x->async_msg.msg_control = NULL;
- x->async_msg.msg_controllen = 0;
- x->async_msg.msg_flags = !(iocb->ki_filp->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
- if (sock->type == SOCK_SEQPACKET)
- x->async_msg.msg_flags |= MSG_EOR;
- x->async_iov.iov_base = (void __user *)ubuf;
- x->async_iov.iov_len = size;
-
- return __sock_sendmsg(iocb, sock, &x->async_msg, size);
+ x = alloc_sock_iocb(iocb, ubuf, count, &siocb);
+ if (!x)
+ return -ENOMEM;
+ return do_sock_read(&x->async_msg, iocb, iocb->ki_filp,
+ &x->async_iov, 1);
}
-static ssize_t sock_sendpage(struct file *file, struct page *page,
- int offset, size_t size, loff_t *ppos, int more)
+static ssize_t do_sock_write(struct msghdr *msg, struct kiocb *iocb,
+ struct file *file, struct iovec *iov, unsigned long nr_segs)
{
- struct socket *sock;
- int flags;
+ struct socket *sock = file->private_data;
+ size_t size = 0;
+ int i;
- sock = file->private_data;
+ for (i = 0 ; i < nr_segs ; i++)
+ size += iov[i].iov_len;
- flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
- if (more)
- flags |= MSG_MORE;
+ msg->msg_name = NULL;
+ msg->msg_namelen = 0;
+ msg->msg_control = NULL;
+ msg->msg_controllen = 0;
+ msg->msg_iov = (struct iovec *) iov;
+ msg->msg_iovlen = nr_segs;
+ msg->msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0;
+ if (sock->type == SOCK_SEQPACKET)
+ msg->msg_flags |= MSG_EOR;
- return sock->ops->sendpage(sock, page, offset, size, flags);
+ return __sock_sendmsg(iocb, sock, msg, size);
}
-static int sock_readv_writev(int type,
- struct file * file, const struct iovec * iov,
- long count, size_t size)
+static ssize_t sock_writev(struct file *file, const struct iovec *iov,
+ unsigned long nr_segs, loff_t *ppos)
{
struct msghdr msg;
- struct socket *sock;
+ struct kiocb iocb;
+ struct sock_iocb siocb;
+ int ret;
- sock = file->private_data;
+ init_sync_kiocb(&iocb, NULL);
+ iocb.private = &siocb;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_iov = (struct iovec *) iov;
- msg.msg_iovlen = count;
- msg.msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0;
+ ret = do_sock_write(&msg, &iocb, file, (struct iovec *)iov, nr_segs);
+ if (-EIOCBQUEUED == ret)
+ ret = wait_on_sync_kiocb(&iocb);
+ return ret;
+}
- /* read() does a VERIFY_WRITE */
- if (type == VERIFY_WRITE)
- return sock_recvmsg(sock, &msg, size, msg.msg_flags);
+static ssize_t sock_aio_write(struct kiocb *iocb, const char __user *ubuf,
+ size_t count, loff_t pos)
+{
+ struct sock_iocb siocb, *x;
- if (sock->type == SOCK_SEQPACKET)
- msg.msg_flags |= MSG_EOR;
+ if (pos != 0)
+ return -ESPIPE;
+ if (count == 0) /* Match SYS5 behaviour */
+ return 0;
- return sock_sendmsg(sock, &msg, size);
-}
+ x = alloc_sock_iocb(iocb, (void __user *)ubuf, count, &siocb);
+ if (!x)
+ return -ENOMEM;
-static ssize_t sock_readv(struct file *file, const struct iovec *vector,
- unsigned long count, loff_t *ppos)
-{
- size_t tot_len = 0;
- int i;
- for (i = 0 ; i < count ; i++)
- tot_len += vector[i].iov_len;
- return sock_readv_writev(VERIFY_WRITE,
- file, vector, count, tot_len);
-}
-
-static ssize_t sock_writev(struct file *file, const struct iovec *vector,
- unsigned long count, loff_t *ppos)
-{
- size_t tot_len = 0;
- int i;
- for (i = 0 ; i < count ; i++)
- tot_len += vector[i].iov_len;
- return sock_readv_writev(VERIFY_READ,
- file, vector, count, tot_len);
+ return do_sock_write(&x->async_msg, iocb, iocb->ki_filp,
+ &x->async_iov, 1);
}
@@ -904,6 +900,13 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
break;
default:
err = sock->ops->ioctl(sock, cmd, arg);
+
+ /*
+ * If this ioctl is unknown try to hand it down
+ * to the NIC driver.
+ */
+ if (err == -ENOIOCTLCMD)
+ err = dev_ioctl(cmd, argp);
break;
}
return err;
@@ -2036,7 +2039,7 @@ int sock_unregister(int family)
return 0;
}
-void __init sock_init(void)
+static int __init sock_init(void)
{
/*
* Initialize sock SLAB cache.
@@ -2044,12 +2047,10 @@ void __init sock_init(void)
sk_init();
-#ifdef SLAB_SKB
/*
* Initialize skbuff SLAB cache
*/
skb_init();
-#endif
/*
* Initialize the protocols module.
@@ -2058,15 +2059,19 @@ void __init sock_init(void)
init_inodecache();
register_filesystem(&sock_fs_type);
sock_mnt = kern_mount(&sock_fs_type);
- /* The real protocol initialization is performed when
- * do_initcalls is run.
+
+ /* The real protocol initialization is performed in later initcalls.
*/
#ifdef CONFIG_NETFILTER
netfilter_init();
#endif
+
+ return 0;
}
+core_initcall(sock_init); /* early initcall */
+
#ifdef CONFIG_PROC_FS
void socket_seq_show(struct seq_file *seq)
{
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index f44f46f1d8e0..8d782282ec19 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -638,7 +638,7 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
gss_msg);
atomic_inc(&gss_msg->count);
gss_unhash_msg(gss_msg);
- if (msg->errno == -ETIMEDOUT || msg->errno == -EPIPE) {
+ if (msg->errno == -ETIMEDOUT) {
unsigned long now = jiffies;
if (time_after(now, ratelimit)) {
printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n"
@@ -786,7 +786,9 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int taskflags)
cred->gc_flags = 0;
cred->gc_base.cr_ops = &gss_credops;
cred->gc_service = gss_auth->service;
- err = gss_create_upcall(gss_auth, cred);
+ do {
+ err = gss_create_upcall(gss_auth, cred);
+ } while (err == -EAGAIN);
if (err < 0)
goto out_err;
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index c76ea221798c..16a2458f38f7 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -174,7 +174,7 @@ rpc_pipe_release(struct inode *inode, struct file *filp)
goto out;
msg = (struct rpc_pipe_msg *)filp->private_data;
if (msg != NULL) {
- msg->errno = -EPIPE;
+ msg->errno = -EAGAIN;
list_del_init(&msg->list);
rpci->ops->destroy_msg(msg);
}
@@ -183,7 +183,7 @@ rpc_pipe_release(struct inode *inode, struct file *filp)
if (filp->f_mode & FMODE_READ)
rpci->nreaders --;
if (!rpci->nreaders)
- __rpc_purge_upcall(inode, -EPIPE);
+ __rpc_purge_upcall(inode, -EAGAIN);
if (rpci->ops->release_pipe)
rpci->ops->release_pipe(inode);
out:
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index c6a51911e71e..d68eba481291 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -758,7 +758,7 @@ svc_tcp_accept(struct svc_sock *svsk)
struct svc_serv *serv = svsk->sk_server;
struct socket *sock = svsk->sk_sock;
struct socket *newsock;
- struct proto_ops *ops;
+ const struct proto_ops *ops;
struct svc_sock *newsvsk;
int err, slen;
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 0a51fd46a848..77e8800d4127 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -990,6 +990,7 @@ static void xs_udp_connect_worker(void *args)
sk->sk_data_ready = xs_udp_data_ready;
sk->sk_write_space = xs_udp_write_space;
sk->sk_no_check = UDP_CSUM_NORCV;
+ sk->sk_allocation = GFP_ATOMIC;
xprt_set_connected(xprt);
@@ -1074,6 +1075,7 @@ static void xs_tcp_connect_worker(void *args)
sk->sk_data_ready = xs_tcp_data_ready;
sk->sk_state_change = xs_tcp_state_change;
sk->sk_write_space = xs_tcp_write_space;
+ sk->sk_allocation = GFP_ATOMIC;
/* socket options */
sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index acc73ba8bade..5f6ae79b8b16 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -121,7 +121,7 @@
int sysctl_unix_max_dgram_qlen = 10;
struct hlist_head unix_socket_table[UNIX_HASH_SIZE + 1];
-DEFINE_RWLOCK(unix_table_lock);
+DEFINE_SPINLOCK(unix_table_lock);
static atomic_t unix_nr_socks = ATOMIC_INIT(0);
#define unix_sockets_unbound (&unix_socket_table[UNIX_HASH_SIZE])
@@ -130,7 +130,7 @@ static atomic_t unix_nr_socks = ATOMIC_INIT(0);
/*
* SMP locking strategy:
- * hash table is protected with rwlock unix_table_lock
+ * hash table is protected with spinlock unix_table_lock
* each socket state is protected by separate rwlock.
*/
@@ -214,16 +214,16 @@ static void __unix_insert_socket(struct hlist_head *list, struct sock *sk)
static inline void unix_remove_socket(struct sock *sk)
{
- write_lock(&unix_table_lock);
+ spin_lock(&unix_table_lock);
__unix_remove_socket(sk);
- write_unlock(&unix_table_lock);
+ spin_unlock(&unix_table_lock);
}
static inline void unix_insert_socket(struct hlist_head *list, struct sock *sk)
{
- write_lock(&unix_table_lock);
+ spin_lock(&unix_table_lock);
__unix_insert_socket(list, sk);
- write_unlock(&unix_table_lock);
+ spin_unlock(&unix_table_lock);
}
static struct sock *__unix_find_socket_byname(struct sockaddr_un *sunname,
@@ -250,11 +250,11 @@ static inline struct sock *unix_find_socket_byname(struct sockaddr_un *sunname,
{
struct sock *s;
- read_lock(&unix_table_lock);
+ spin_lock(&unix_table_lock);
s = __unix_find_socket_byname(sunname, len, type, hash);
if (s)
sock_hold(s);
- read_unlock(&unix_table_lock);
+ spin_unlock(&unix_table_lock);
return s;
}
@@ -263,7 +263,7 @@ static struct sock *unix_find_socket_byinode(struct inode *i)
struct sock *s;
struct hlist_node *node;
- read_lock(&unix_table_lock);
+ spin_lock(&unix_table_lock);
sk_for_each(s, node,
&unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]) {
struct dentry *dentry = unix_sk(s)->dentry;
@@ -276,7 +276,7 @@ static struct sock *unix_find_socket_byinode(struct inode *i)
}
s = NULL;
found:
- read_unlock(&unix_table_lock);
+ spin_unlock(&unix_table_lock);
return s;
}
@@ -473,7 +473,7 @@ static int unix_dgram_connect(struct socket *, struct sockaddr *,
static int unix_seqpacket_sendmsg(struct kiocb *, struct socket *,
struct msghdr *, size_t);
-static struct proto_ops unix_stream_ops = {
+static const struct proto_ops unix_stream_ops = {
.family = PF_UNIX,
.owner = THIS_MODULE,
.release = unix_release,
@@ -494,7 +494,7 @@ static struct proto_ops unix_stream_ops = {
.sendpage = sock_no_sendpage,
};
-static struct proto_ops unix_dgram_ops = {
+static const struct proto_ops unix_dgram_ops = {
.family = PF_UNIX,
.owner = THIS_MODULE,
.release = unix_release,
@@ -515,7 +515,7 @@ static struct proto_ops unix_dgram_ops = {
.sendpage = sock_no_sendpage,
};
-static struct proto_ops unix_seqpacket_ops = {
+static const struct proto_ops unix_seqpacket_ops = {
.family = PF_UNIX,
.owner = THIS_MODULE,
.release = unix_release,
@@ -564,7 +564,7 @@ static struct sock * unix_create1(struct socket *sock)
u = unix_sk(sk);
u->dentry = NULL;
u->mnt = NULL;
- rwlock_init(&u->lock);
+ spin_lock_init(&u->lock);
atomic_set(&u->inflight, sock ? 0 : -1);
init_MUTEX(&u->readsem); /* single task reading lock */
init_waitqueue_head(&u->peer_wait);
@@ -642,12 +642,12 @@ retry:
addr->len = sprintf(addr->name->sun_path+1, "%05x", ordernum) + 1 + sizeof(short);
addr->hash = unix_hash_fold(csum_partial((void*)addr->name, addr->len, 0));
- write_lock(&unix_table_lock);
+ spin_lock(&unix_table_lock);
ordernum = (ordernum+1)&0xFFFFF;
if (__unix_find_socket_byname(addr->name, addr->len, sock->type,
addr->hash)) {
- write_unlock(&unix_table_lock);
+ spin_unlock(&unix_table_lock);
/* Sanity yield. It is unusual case, but yet... */
if (!(ordernum&0xFF))
yield();
@@ -658,7 +658,7 @@ retry:
__unix_remove_socket(sk);
u->addr = addr;
__unix_insert_socket(&unix_socket_table[addr->hash], sk);
- write_unlock(&unix_table_lock);
+ spin_unlock(&unix_table_lock);
err = 0;
out: up(&u->readsem);
@@ -791,7 +791,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
addr->hash = UNIX_HASH_SIZE;
}
- write_lock(&unix_table_lock);
+ spin_lock(&unix_table_lock);
if (!sunaddr->sun_path[0]) {
err = -EADDRINUSE;
@@ -814,7 +814,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
__unix_insert_socket(list, sk);
out_unlock:
- write_unlock(&unix_table_lock);
+ spin_unlock(&unix_table_lock);
out_up:
up(&u->readsem);
out:
@@ -1063,10 +1063,12 @@ restart:
/* Set credentials */
sk->sk_peercred = other->sk_peercred;
- sock_hold(newsk);
- unix_peer(sk) = newsk;
sock->state = SS_CONNECTED;
sk->sk_state = TCP_ESTABLISHED;
+ sock_hold(newsk);
+
+ smp_mb__after_atomic_inc(); /* sock_hold() does an atomic_inc() */
+ unix_peer(sk) = newsk;
unix_state_wunlock(sk);
@@ -1414,7 +1416,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
} else {
sunaddr = NULL;
err = -ENOTCONN;
- other = unix_peer_get(sk);
+ other = unix_peer(sk);
if (!other)
goto out_err;
}
@@ -1476,7 +1478,6 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
other->sk_data_ready(other, size);
sent+=size;
}
- sock_put(other);
scm_destroy(siocb->scm);
siocb->scm = NULL;
@@ -1491,8 +1492,6 @@ pipe_err:
send_sig(SIGPIPE,current,0);
err = -EPIPE;
out_err:
- if (other)
- sock_put(other);
scm_destroy(siocb->scm);
siocb->scm = NULL;
return sent ? : err;
@@ -1860,7 +1859,7 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
}
default:
- err = dev_ioctl(cmd, (void __user *)arg);
+ err = -ENOIOCTLCMD;
break;
}
return err;
@@ -1917,7 +1916,7 @@ static struct sock *unix_seq_idx(int *iter, loff_t pos)
static void *unix_seq_start(struct seq_file *seq, loff_t *pos)
{
- read_lock(&unix_table_lock);
+ spin_lock(&unix_table_lock);
return *pos ? unix_seq_idx(seq->private, *pos - 1) : ((void *) 1);
}
@@ -1932,7 +1931,7 @@ static void *unix_seq_next(struct seq_file *seq, void *v, loff_t *pos)
static void unix_seq_stop(struct seq_file *seq, void *v)
{
- read_unlock(&unix_table_lock);
+ spin_unlock(&unix_table_lock);
}
static int unix_seq_show(struct seq_file *seq, void *v)
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index 6ffc64e1712d..411802bd4d37 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -182,7 +182,7 @@ void unix_gc(void)
if (down_trylock(&unix_gc_sem))
return;
- read_lock(&unix_table_lock);
+ spin_lock(&unix_table_lock);
forall_unix_sockets(i, s)
{
@@ -301,7 +301,7 @@ void unix_gc(void)
}
u->gc_tree = GC_ORPHAN;
}
- read_unlock(&unix_table_lock);
+ spin_unlock(&unix_table_lock);
/*
* Here we are. Hitlist is filled. Die.
diff --git a/net/wanrouter/af_wanpipe.c b/net/wanrouter/af_wanpipe.c
index 59fec59b2132..7a43ae4721ed 100644
--- a/net/wanrouter/af_wanpipe.c
+++ b/net/wanrouter/af_wanpipe.c
@@ -181,7 +181,7 @@ struct wanpipe_opt
#endif
static int sk_count;
-extern struct proto_ops wanpipe_ops;
+extern const struct proto_ops wanpipe_ops;
static unsigned long find_free_critical;
static void wanpipe_unlink_driver(struct sock *sk);
@@ -1839,7 +1839,7 @@ static int wanpipe_ioctl(struct socket *sock, unsigned int cmd, unsigned long ar
#endif
default:
- return dev_ioctl(cmd,(void __user *) arg);
+ return -ENOIOCTLCMD;
}
/*NOTREACHED*/
}
@@ -2546,7 +2546,7 @@ static int wanpipe_connect(struct socket *sock, struct sockaddr *uaddr, int addr
return 0;
}
-struct proto_ops wanpipe_ops = {
+const struct proto_ops wanpipe_ops = {
.family = PF_WANPIPE,
.owner = THIS_MODULE,
.release = wanpipe_release,
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index 020d73cc8414..16459c7f54b2 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -64,7 +64,7 @@ int sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2;
HLIST_HEAD(x25_list);
DEFINE_RWLOCK(x25_list_lock);
-static struct proto_ops x25_proto_ops;
+static const struct proto_ops x25_proto_ops;
static struct x25_address null_x25_address = {" "};
@@ -1378,7 +1378,7 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
}
default:
- rc = dev_ioctl(cmd, argp);
+ rc = -ENOIOCTLCMD;
break;
}
@@ -1391,7 +1391,7 @@ static struct net_proto_family x25_family_ops = {
.owner = THIS_MODULE,
};
-static struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = {
+static const struct proto_ops SOCKOPS_WRAPPED(x25_proto_ops) = {
.family = AF_X25,
.owner = THIS_MODULE,
.release = x25_release,
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 0db9e57013fd..64a447375fdb 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -10,7 +10,7 @@
* YOSHIFUJI Hideaki
* Split up af-specific portion
* Derek Atkins <derek@ihtfp.com> Add the post_input processor
- *
+ *
*/
#include <asm/bug.h>
@@ -256,6 +256,7 @@ void __xfrm_policy_destroy(struct xfrm_policy *policy)
if (del_timer(&policy->timer))
BUG();
+ security_xfrm_policy_free(policy);
kfree(policy);
}
EXPORT_SYMBOL(__xfrm_policy_destroy);
@@ -346,10 +347,12 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
struct xfrm_policy *pol, **p;
struct xfrm_policy *delpol = NULL;
struct xfrm_policy **newpos = NULL;
+ struct dst_entry *gc_list;
write_lock_bh(&xfrm_policy_lock);
for (p = &xfrm_policy_list[dir]; (pol=*p)!=NULL;) {
- if (!delpol && memcmp(&policy->selector, &pol->selector, sizeof(pol->selector)) == 0) {
+ if (!delpol && memcmp(&policy->selector, &pol->selector, sizeof(pol->selector)) == 0 &&
+ xfrm_sec_ctx_match(pol->security, policy->security)) {
if (excl) {
write_unlock_bh(&xfrm_policy_lock);
return -EEXIST;
@@ -381,21 +384,49 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
xfrm_pol_hold(policy);
write_unlock_bh(&xfrm_policy_lock);
- if (delpol) {
+ if (delpol)
xfrm_policy_kill(delpol);
+
+ read_lock_bh(&xfrm_policy_lock);
+ gc_list = NULL;
+ for (policy = policy->next; policy; policy = policy->next) {
+ struct dst_entry *dst;
+
+ write_lock(&policy->lock);
+ dst = policy->bundles;
+ if (dst) {
+ struct dst_entry *tail = dst;
+ while (tail->next)
+ tail = tail->next;
+ tail->next = gc_list;
+ gc_list = dst;
+
+ policy->bundles = NULL;
+ }
+ write_unlock(&policy->lock);
+ }
+ read_unlock_bh(&xfrm_policy_lock);
+
+ while (gc_list) {
+ struct dst_entry *dst = gc_list;
+
+ gc_list = dst->next;
+ dst_free(dst);
}
+
return 0;
}
EXPORT_SYMBOL(xfrm_policy_insert);
-struct xfrm_policy *xfrm_policy_bysel(int dir, struct xfrm_selector *sel,
- int delete)
+struct xfrm_policy *xfrm_policy_bysel_ctx(int dir, struct xfrm_selector *sel,
+ struct xfrm_sec_ctx *ctx, int delete)
{
struct xfrm_policy *pol, **p;
write_lock_bh(&xfrm_policy_lock);
for (p = &xfrm_policy_list[dir]; (pol=*p)!=NULL; p = &pol->next) {
- if (memcmp(sel, &pol->selector, sizeof(*sel)) == 0) {
+ if ((memcmp(sel, &pol->selector, sizeof(*sel)) == 0) &&
+ (xfrm_sec_ctx_match(ctx, pol->security))) {
xfrm_pol_hold(pol);
if (delete)
*p = pol->next;
@@ -410,7 +441,7 @@ struct xfrm_policy *xfrm_policy_bysel(int dir, struct xfrm_selector *sel,
}
return pol;
}
-EXPORT_SYMBOL(xfrm_policy_bysel);
+EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
struct xfrm_policy *xfrm_policy_byid(int dir, u32 id, int delete)
{
@@ -491,7 +522,7 @@ EXPORT_SYMBOL(xfrm_policy_walk);
/* Find policy to apply to this flow. */
-static void xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
+static void xfrm_policy_lookup(struct flowi *fl, u32 sk_sid, u16 family, u8 dir,
void **objp, atomic_t **obj_refp)
{
struct xfrm_policy *pol;
@@ -505,9 +536,12 @@ static void xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
continue;
match = xfrm_selector_match(sel, fl, family);
+
if (match) {
- xfrm_pol_hold(pol);
- break;
+ if (!security_xfrm_policy_lookup(pol, sk_sid, dir)) {
+ xfrm_pol_hold(pol);
+ break;
+ }
}
}
read_unlock_bh(&xfrm_policy_lock);
@@ -515,15 +549,37 @@ static void xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
*obj_refp = &pol->refcnt;
}
-static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl)
+static inline int policy_to_flow_dir(int dir)
+{
+ if (XFRM_POLICY_IN == FLOW_DIR_IN &&
+ XFRM_POLICY_OUT == FLOW_DIR_OUT &&
+ XFRM_POLICY_FWD == FLOW_DIR_FWD)
+ return dir;
+ switch (dir) {
+ default:
+ case XFRM_POLICY_IN:
+ return FLOW_DIR_IN;
+ case XFRM_POLICY_OUT:
+ return FLOW_DIR_OUT;
+ case XFRM_POLICY_FWD:
+ return FLOW_DIR_FWD;
+ };
+}
+
+static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl, u32 sk_sid)
{
struct xfrm_policy *pol;
read_lock_bh(&xfrm_policy_lock);
if ((pol = sk->sk_policy[dir]) != NULL) {
- int match = xfrm_selector_match(&pol->selector, fl,
+ int match = xfrm_selector_match(&pol->selector, fl,
sk->sk_family);
+ int err = 0;
+
if (match)
+ err = security_xfrm_policy_lookup(pol, sk_sid, policy_to_flow_dir(dir));
+
+ if (match && !err)
xfrm_pol_hold(pol);
else
pol = NULL;
@@ -596,6 +652,10 @@ static struct xfrm_policy *clone_policy(struct xfrm_policy *old, int dir)
if (newp) {
newp->selector = old->selector;
+ if (security_xfrm_policy_clone(old, newp)) {
+ kfree(newp);
+ return NULL; /* ENOMEM */
+ }
newp->lft = old->lft;
newp->curlft = old->curlft;
newp->action = old->action;
@@ -707,22 +767,6 @@ xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
return err;
}
-static inline int policy_to_flow_dir(int dir)
-{
- if (XFRM_POLICY_IN == FLOW_DIR_IN &&
- XFRM_POLICY_OUT == FLOW_DIR_OUT &&
- XFRM_POLICY_FWD == FLOW_DIR_FWD)
- return dir;
- switch (dir) {
- default:
- case XFRM_POLICY_IN:
- return FLOW_DIR_IN;
- case XFRM_POLICY_OUT:
- return FLOW_DIR_OUT;
- case XFRM_POLICY_FWD:
- return FLOW_DIR_FWD;
- };
-}
static int stale_bundle(struct dst_entry *dst);
@@ -741,19 +785,20 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
int err;
u32 genid;
u16 family = dst_orig->ops->family;
+ u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
+ u32 sk_sid = security_sk_sid(sk, fl, dir);
restart:
genid = atomic_read(&flow_cache_genid);
policy = NULL;
if (sk && sk->sk_policy[1])
- policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
+ policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, sk_sid);
if (!policy) {
/* To accelerate a bit... */
if ((dst_orig->flags & DST_NOXFRM) || !xfrm_policy_list[XFRM_POLICY_OUT])
return 0;
- policy = flow_cache_lookup(fl, family,
- policy_to_flow_dir(XFRM_POLICY_OUT),
+ policy = flow_cache_lookup(fl, sk_sid, family, dir,
xfrm_policy_lookup);
}
@@ -934,16 +979,20 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
{
struct xfrm_policy *pol;
struct flowi fl;
+ u8 fl_dir = policy_to_flow_dir(dir);
+ u32 sk_sid;
if (_decode_session(skb, &fl, family) < 0)
return 0;
+ sk_sid = security_sk_sid(sk, &fl, fl_dir);
+
/* First, check used SA against their selectors. */
if (skb->sp) {
int i;
for (i=skb->sp->len-1; i>=0; i--) {
- struct sec_decap_state *xvec = &(skb->sp->x[i]);
+ struct sec_decap_state *xvec = &(skb->sp->x[i]);
if (!xfrm_selector_match(&xvec->xvec->sel, &fl, family))
return 0;
@@ -958,11 +1007,10 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
pol = NULL;
if (sk && sk->sk_policy[dir])
- pol = xfrm_sk_policy_lookup(sk, dir, &fl);
+ pol = xfrm_sk_policy_lookup(sk, dir, &fl, sk_sid);
if (!pol)
- pol = flow_cache_lookup(&fl, family,
- policy_to_flow_dir(dir),
+ pol = flow_cache_lookup(&fl, sk_sid, family, fl_dir,
xfrm_policy_lookup);
if (!pol)
@@ -1014,13 +1062,12 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
}
EXPORT_SYMBOL(__xfrm_route_forward);
-/* Optimize later using cookies and generation ids. */
-
static struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie)
{
- if (!stale_bundle(dst))
- return dst;
-
+ /* If it is marked obsolete, which is how we even get here,
+ * then we have purged it from the policy bundle list and we
+ * did that for a good reason.
+ */
return NULL;
}
@@ -1104,6 +1151,16 @@ int xfrm_flush_bundles(void)
return 0;
}
+static int always_true(struct dst_entry *dst)
+{
+ return 1;
+}
+
+void xfrm_flush_all_bundles(void)
+{
+ xfrm_prune_bundles(always_true);
+}
+
void xfrm_init_pmtu(struct dst_entry *dst)
{
do {
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 7cf48aa6c95b..e12d0be5f976 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -10,7 +10,7 @@
* Split up af-specific functions
* Derek Atkins <derek@ihtfp.com>
* Add UDP Encapsulation
- *
+ *
*/
#include <linux/workqueue.h>
@@ -70,6 +70,7 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
x->type->destructor(x);
xfrm_put_type(x->type);
}
+ security_xfrm_state_free(x);
kfree(x);
}
@@ -343,7 +344,8 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
selector.
*/
if (x->km.state == XFRM_STATE_VALID) {
- if (!xfrm_selector_match(&x->sel, fl, family))
+ if (!xfrm_selector_match(&x->sel, fl, family) ||
+ !xfrm_sec_ctx_match(pol->security, x->security))
continue;
if (!best ||
best->km.dying > x->km.dying ||
@@ -354,7 +356,8 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
acquire_in_progress = 1;
} else if (x->km.state == XFRM_STATE_ERROR ||
x->km.state == XFRM_STATE_EXPIRED) {
- if (xfrm_selector_match(&x->sel, fl, family))
+ if (xfrm_selector_match(&x->sel, fl, family) &&
+ xfrm_sec_ctx_match(pol->security, x->security))
error = -ESRCH;
}
}
@@ -431,6 +434,8 @@ void xfrm_state_insert(struct xfrm_state *x)
spin_lock_bh(&xfrm_state_lock);
__xfrm_state_insert(x);
spin_unlock_bh(&xfrm_state_lock);
+
+ xfrm_flush_all_bundles();
}
EXPORT_SYMBOL(xfrm_state_insert);
@@ -478,6 +483,9 @@ out:
spin_unlock_bh(&xfrm_state_lock);
xfrm_state_put_afinfo(afinfo);
+ if (!err)
+ xfrm_flush_all_bundles();
+
if (x1) {
xfrm_state_delete(x1);
xfrm_state_put(x1);
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 0cdd9a07e043..92e2b804c606 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -7,7 +7,7 @@
* Kazunori MIYAZAWA @USAGI
* Kunihiro Ishiguro <kunihiro@ipinfusion.com>
* IPv6 support
- *
+ *
*/
#include <linux/module.h>
@@ -88,6 +88,34 @@ static int verify_encap_tmpl(struct rtattr **xfrma)
return 0;
}
+
+static inline int verify_sec_ctx_len(struct rtattr **xfrma)
+{
+ struct rtattr *rt = xfrma[XFRMA_SEC_CTX - 1];
+ struct xfrm_user_sec_ctx *uctx;
+ int len = 0;
+
+ if (!rt)
+ return 0;
+
+ if (rt->rta_len < sizeof(*uctx))
+ return -EINVAL;
+
+ uctx = RTA_DATA(rt);
+
+ if (uctx->ctx_len > PAGE_SIZE)
+ return -EINVAL;
+
+ len += sizeof(struct xfrm_user_sec_ctx);
+ len += uctx->ctx_len;
+
+ if (uctx->len != len)
+ return -EINVAL;
+
+ return 0;
+}
+
+
static int verify_newsa_info(struct xfrm_usersa_info *p,
struct rtattr **xfrma)
{
@@ -145,6 +173,8 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
goto out;
if ((err = verify_encap_tmpl(xfrma)))
goto out;
+ if ((err = verify_sec_ctx_len(xfrma)))
+ goto out;
err = -EINVAL;
switch (p->mode) {
@@ -209,6 +239,30 @@ static int attach_encap_tmpl(struct xfrm_encap_tmpl **encapp, struct rtattr *u_a
return 0;
}
+
+static inline int xfrm_user_sec_ctx_size(struct xfrm_policy *xp)
+{
+ struct xfrm_sec_ctx *xfrm_ctx = xp->security;
+ int len = 0;
+
+ if (xfrm_ctx) {
+ len += sizeof(struct xfrm_user_sec_ctx);
+ len += xfrm_ctx->ctx_len;
+ }
+ return len;
+}
+
+static int attach_sec_ctx(struct xfrm_state *x, struct rtattr *u_arg)
+{
+ struct xfrm_user_sec_ctx *uctx;
+
+ if (!u_arg)
+ return 0;
+
+ uctx = RTA_DATA(u_arg);
+ return security_xfrm_state_alloc(x, uctx);
+}
+
static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
{
memcpy(&x->id, &p->id, sizeof(x->id));
@@ -253,6 +307,9 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
if (err)
goto error;
+ if ((err = attach_sec_ctx(x, xfrma[XFRMA_SEC_CTX-1])))
+ goto error;
+
x->km.seq = p->seq;
return x;
@@ -272,11 +329,11 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma)
int err;
struct km_event c;
- err = verify_newsa_info(p, (struct rtattr **) xfrma);
+ err = verify_newsa_info(p, (struct rtattr **)xfrma);
if (err)
return err;
- x = xfrm_state_construct(p, (struct rtattr **) xfrma, &err);
+ x = xfrm_state_construct(p, (struct rtattr **)xfrma, &err);
if (!x)
return err;
@@ -390,6 +447,19 @@ static int dump_one_state(struct xfrm_state *x, int count, void *ptr)
if (x->encap)
RTA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap);
+ if (x->security) {
+ int ctx_size = sizeof(struct xfrm_sec_ctx) +
+ x->security->ctx_len;
+ struct rtattr *rt = __RTA_PUT(skb, XFRMA_SEC_CTX, ctx_size);
+ struct xfrm_user_sec_ctx *uctx = RTA_DATA(rt);
+
+ uctx->exttype = XFRMA_SEC_CTX;
+ uctx->len = ctx_size;
+ uctx->ctx_doi = x->security->ctx_doi;
+ uctx->ctx_alg = x->security->ctx_alg;
+ uctx->ctx_len = x->security->ctx_len;
+ memcpy(uctx + 1, x->security->ctx_str, x->security->ctx_len);
+ }
nlh->nlmsg_len = skb->tail - b;
out:
sp->this_idx++;
@@ -603,6 +673,18 @@ static int verify_newpolicy_info(struct xfrm_userpolicy_info *p)
return verify_policy_dir(p->dir);
}
+static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct rtattr **xfrma)
+{
+ struct rtattr *rt = xfrma[XFRMA_SEC_CTX-1];
+ struct xfrm_user_sec_ctx *uctx;
+
+ if (!rt)
+ return 0;
+
+ uctx = RTA_DATA(rt);
+ return security_xfrm_policy_alloc(pol, uctx);
+}
+
static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut,
int nr)
{
@@ -681,7 +763,10 @@ static struct xfrm_policy *xfrm_policy_construct(struct xfrm_userpolicy_info *p,
}
copy_from_user_policy(xp, p);
- err = copy_from_user_tmpl(xp, xfrma);
+
+ if (!(err = copy_from_user_tmpl(xp, xfrma)))
+ err = copy_from_user_sec_ctx(xp, xfrma);
+
if (err) {
*errp = err;
kfree(xp);
@@ -702,8 +787,11 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfr
err = verify_newpolicy_info(p);
if (err)
return err;
+ err = verify_sec_ctx_len((struct rtattr **)xfrma);
+ if (err)
+ return err;
- xp = xfrm_policy_construct(p, (struct rtattr **) xfrma, &err);
+ xp = xfrm_policy_construct(p, (struct rtattr **)xfrma, &err);
if (!xp)
return err;
@@ -761,6 +849,27 @@ rtattr_failure:
return -1;
}
+static int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb)
+{
+ if (xp->security) {
+ int ctx_size = sizeof(struct xfrm_sec_ctx) +
+ xp->security->ctx_len;
+ struct rtattr *rt = __RTA_PUT(skb, XFRMA_SEC_CTX, ctx_size);
+ struct xfrm_user_sec_ctx *uctx = RTA_DATA(rt);
+
+ uctx->exttype = XFRMA_SEC_CTX;
+ uctx->len = ctx_size;
+ uctx->ctx_doi = xp->security->ctx_doi;
+ uctx->ctx_alg = xp->security->ctx_alg;
+ uctx->ctx_len = xp->security->ctx_len;
+ memcpy(uctx + 1, xp->security->ctx_str, xp->security->ctx_len);
+ }
+ return 0;
+
+ rtattr_failure:
+ return -1;
+}
+
static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr)
{
struct xfrm_dump_info *sp = ptr;
@@ -782,6 +891,8 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr
copy_to_user_policy(xp, p, dir);
if (copy_to_user_tmpl(xp, skb) < 0)
goto nlmsg_failure;
+ if (copy_to_user_sec_ctx(xp, skb))
+ goto nlmsg_failure;
nlh->nlmsg_len = skb->tail - b;
out:
@@ -852,8 +963,25 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfr
if (p->index)
xp = xfrm_policy_byid(p->dir, p->index, delete);
- else
- xp = xfrm_policy_bysel(p->dir, &p->sel, delete);
+ else {
+ struct rtattr **rtattrs = (struct rtattr **)xfrma;
+ struct rtattr *rt = rtattrs[XFRMA_SEC_CTX-1];
+ struct xfrm_policy tmp;
+
+ err = verify_sec_ctx_len(rtattrs);
+ if (err)
+ return err;
+
+ memset(&tmp, 0, sizeof(struct xfrm_policy));
+ if (rt) {
+ struct xfrm_user_sec_ctx *uctx = RTA_DATA(rt);
+
+ if ((err = security_xfrm_policy_alloc(&tmp, uctx)))
+ return err;
+ }
+ xp = xfrm_policy_bysel_ctx(p->dir, &p->sel, tmp.security, delete);
+ security_xfrm_policy_free(&tmp);
+ }
if (xp == NULL)
return -ENOENT;
@@ -1224,6 +1352,8 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x,
if (copy_to_user_tmpl(xp, skb) < 0)
goto nlmsg_failure;
+ if (copy_to_user_sec_ctx(xp, skb))
+ goto nlmsg_failure;
nlh->nlmsg_len = skb->tail - b;
return skb->len;
@@ -1241,6 +1371,7 @@ static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt,
len = RTA_SPACE(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr);
len += NLMSG_SPACE(sizeof(struct xfrm_user_acquire));
+ len += RTA_SPACE(xfrm_user_sec_ctx_size(xp));
skb = alloc_skb(len, GFP_ATOMIC);
if (skb == NULL)
return -ENOMEM;
@@ -1324,6 +1455,8 @@ static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp,
copy_to_user_policy(xp, &upe->pol, dir);
if (copy_to_user_tmpl(xp, skb) < 0)
goto nlmsg_failure;
+ if (copy_to_user_sec_ctx(xp, skb))
+ goto nlmsg_failure;
upe->hard = !!hard;
nlh->nlmsg_len = skb->tail - b;
@@ -1341,6 +1474,7 @@ static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, struct km_eve
len = RTA_SPACE(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr);
len += NLMSG_SPACE(sizeof(struct xfrm_user_polexpire));
+ len += RTA_SPACE(xfrm_user_sec_ctx_size(xp));
skb = alloc_skb(len, GFP_ATOMIC);
if (skb == NULL)
return -ENOMEM;
diff --git a/security/Kconfig b/security/Kconfig
index 64d3f1e9ca85..34f593410d57 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -54,6 +54,19 @@ config SECURITY_NETWORK
implement socket and networking access controls.
If you are unsure how to answer this question, answer N.
+config SECURITY_NETWORK_XFRM
+ bool "XFRM (IPSec) Networking Security Hooks"
+ depends on XFRM && SECURITY_NETWORK
+ help
+ This enables the XFRM (IPSec) networking security hooks.
+ If enabled, a security module can use these hooks to
+ implement per-packet access controls based on labels
+ derived from IPSec policy. Non-IPSec communications are
+ designated as unlabelled, and only sockets authorized
+ to communicate unlabelled data can send without using
+ IPSec.
+ If you are unsure how to answer this question, answer N.
+
config SECURITY_CAPABILITIES
tristate "Default Linux Capabilities"
depends on SECURITY
diff --git a/security/dummy.c b/security/dummy.c
index 3ca5f2b828a0..a15c54709fde 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -776,8 +776,42 @@ static inline int dummy_sk_alloc_security (struct sock *sk, int family, gfp_t pr
static inline void dummy_sk_free_security (struct sock *sk)
{
}
+
+static unsigned int dummy_sk_getsid(struct sock *sk, struct flowi *fl, u8 dir)
+{
+ return 0;
+}
#endif /* CONFIG_SECURITY_NETWORK */
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+static int dummy_xfrm_policy_alloc_security(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx)
+{
+ return 0;
+}
+
+static inline int dummy_xfrm_policy_clone_security(struct xfrm_policy *old, struct xfrm_policy *new)
+{
+ return 0;
+}
+
+static void dummy_xfrm_policy_free_security(struct xfrm_policy *xp)
+{
+}
+
+static int dummy_xfrm_state_alloc_security(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx)
+{
+ return 0;
+}
+
+static void dummy_xfrm_state_free_security(struct xfrm_state *x)
+{
+}
+
+static int dummy_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
+{
+ return 0;
+}
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
static int dummy_register_security (const char *name, struct security_operations *ops)
{
return -EINVAL;
@@ -970,7 +1004,16 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, socket_getpeersec);
set_to_dummy_if_null(ops, sk_alloc_security);
set_to_dummy_if_null(ops, sk_free_security);
-#endif /* CONFIG_SECURITY_NETWORK */
+ set_to_dummy_if_null(ops, sk_getsid);
+ #endif /* CONFIG_SECURITY_NETWORK */
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+ set_to_dummy_if_null(ops, xfrm_policy_alloc_security);
+ set_to_dummy_if_null(ops, xfrm_policy_clone_security);
+ set_to_dummy_if_null(ops, xfrm_policy_free_security);
+ set_to_dummy_if_null(ops, xfrm_state_alloc_security);
+ set_to_dummy_if_null(ops, xfrm_state_free_security);
+ set_to_dummy_if_null(ops, xfrm_policy_lookup);
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
#ifdef CONFIG_KEYS
set_to_dummy_if_null(ops, key_alloc);
set_to_dummy_if_null(ops, key_free);
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index b038cd0fae2e..06d54d9d20a5 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -8,5 +8,7 @@ selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o
selinux-$(CONFIG_SECURITY_NETWORK) += netif.o
+selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
+
EXTRA_CFLAGS += -Isecurity/selinux/include
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index fc774436a264..3d496eae1b47 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -73,6 +73,7 @@
#include "avc.h"
#include "objsec.h"
#include "netif.h"
+#include "xfrm.h"
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
@@ -3349,6 +3350,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
err = avc_has_perm(sock_sid, port_sid,
sock_class, recv_perm, &ad);
}
+
+ if (!err)
+ err = selinux_xfrm_sock_rcv_skb(sock_sid, skb);
+
out:
return err;
}
@@ -3401,6 +3406,24 @@ static void selinux_sk_free_security(struct sock *sk)
sk_free_security(sk);
}
+static unsigned int selinux_sk_getsid_security(struct sock *sk, struct flowi *fl, u8 dir)
+{
+ struct inode_security_struct *isec;
+ u32 sock_sid = SECINITSID_ANY_SOCKET;
+
+ if (!sk)
+ return selinux_no_sk_sid(fl);
+
+ read_lock_bh(&sk->sk_callback_lock);
+ isec = get_sock_isec(sk);
+
+ if (isec)
+ sock_sid = isec->sid;
+
+ read_unlock_bh(&sk->sk_callback_lock);
+ return sock_sid;
+}
+
static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
{
int err = 0;
@@ -3536,6 +3559,11 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
send_perm, &ad) ? NF_DROP : NF_ACCEPT;
}
+ if (err != NF_ACCEPT)
+ goto out;
+
+ err = selinux_xfrm_postroute_last(isec->sid, skb);
+
out:
return err;
}
@@ -4380,6 +4408,16 @@ static struct security_operations selinux_ops = {
.socket_getpeersec = selinux_socket_getpeersec,
.sk_alloc_security = selinux_sk_alloc_security,
.sk_free_security = selinux_sk_free_security,
+ .sk_getsid = selinux_sk_getsid_security,
+#endif
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+ .xfrm_policy_alloc_security = selinux_xfrm_policy_alloc,
+ .xfrm_policy_clone_security = selinux_xfrm_policy_clone,
+ .xfrm_policy_free_security = selinux_xfrm_policy_free,
+ .xfrm_state_alloc_security = selinux_xfrm_state_alloc,
+ .xfrm_state_free_security = selinux_xfrm_state_free,
+ .xfrm_policy_lookup = selinux_xfrm_policy_lookup,
#endif
};
@@ -4491,6 +4529,7 @@ static int __init selinux_nf_ip_init(void)
panic("SELinux: nf_register_hook for IPv6: error %d\n", err);
#endif /* IPV6 */
+
out:
return err;
}
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
index 1deb59e1b762..71aeb12f07c8 100644
--- a/security/selinux/include/av_perm_to_string.h
+++ b/security/selinux/include/av_perm_to_string.h
@@ -238,3 +238,5 @@
S_(SECCLASS_NSCD, NSCD__SHMEMHOST, "shmemhost")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto")
S_(SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, "recvfrom")
+ S_(SECCLASS_ASSOCIATION, ASSOCIATION__RELABELFROM, "relabelfrom")
+ S_(SECCLASS_ASSOCIATION, ASSOCIATION__RELABELTO, "relabelto")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
index a78b5d59c9fc..d1d0996049e3 100644
--- a/security/selinux/include/av_permissions.h
+++ b/security/selinux/include/av_permissions.h
@@ -908,6 +908,8 @@
#define ASSOCIATION__SENDTO 0x00000001UL
#define ASSOCIATION__RECVFROM 0x00000002UL
+#define ASSOCIATION__RELABELFROM 0x00000004UL
+#define ASSOCIATION__RELABELTO 0x00000008UL
#define NETLINK_KOBJECT_UEVENT_SOCKET__IOCTL 0x00000001UL
#define NETLINK_KOBJECT_UEVENT_SOCKET__READ 0x00000002UL
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
new file mode 100644
index 000000000000..8e87996c6dd5
--- /dev/null
+++ b/security/selinux/include/xfrm.h
@@ -0,0 +1,54 @@
+/*
+ * SELinux support for the XFRM LSM hooks
+ *
+ * Author : Trent Jaeger, <jaegert@us.ibm.com>
+ */
+#ifndef _SELINUX_XFRM_H_
+#define _SELINUX_XFRM_H_
+
+int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx);
+int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new);
+void selinux_xfrm_policy_free(struct xfrm_policy *xp);
+int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx);
+void selinux_xfrm_state_free(struct xfrm_state *x);
+int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir);
+
+/*
+ * Extract the security blob from the sock (it's actually on the socket)
+ */
+static inline struct inode_security_struct *get_sock_isec(struct sock *sk)
+{
+ if (!sk->sk_socket)
+ return NULL;
+
+ return SOCK_INODE(sk->sk_socket)->i_security;
+}
+
+
+static inline u32 selinux_no_sk_sid(struct flowi *fl)
+{
+ /* NOTE: no sock occurs on ICMP reply, forwards, ... */
+ /* icmp_reply: authorize as kernel packet */
+ if (fl && fl->proto == IPPROTO_ICMP) {
+ return SECINITSID_KERNEL;
+ }
+
+ return SECINITSID_ANY_SOCKET;
+}
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb);
+int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb);
+#else
+static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
+{
+ return 0;
+}
+
+static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
+{
+ return NF_ACCEPT;
+}
+#endif
+
+#endif /* _SELINUX_XFRM_H_ */
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
new file mode 100644
index 000000000000..c4d87d4dca7b
--- /dev/null
+++ b/security/selinux/xfrm.c
@@ -0,0 +1,311 @@
+/*
+ * NSA Security-Enhanced Linux (SELinux) security module
+ *
+ * This file contains the SELinux XFRM hook function implementations.
+ *
+ * Authors: Serge Hallyn <sergeh@us.ibm.com>
+ * Trent Jaeger <jaegert@us.ibm.com>
+ *
+ * Copyright (C) 2005 International Business Machines Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+
+/*
+ * USAGE:
+ * NOTES:
+ * 1. Make sure to enable the following options in your kernel config:
+ * CONFIG_SECURITY=y
+ * CONFIG_SECURITY_NETWORK=y
+ * CONFIG_SECURITY_NETWORK_XFRM=y
+ * CONFIG_SECURITY_SELINUX=m/y
+ * ISSUES:
+ * 1. Caching packets, so they are not dropped during negotiation
+ * 2. Emulating a reasonable SO_PEERSEC across machines
+ * 3. Testing addition of sk_policy's with security context via setsockopt
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/security.h>
+#include <linux/types.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/xfrm.h>
+#include <net/xfrm.h>
+#include <net/checksum.h>
+#include <net/udp.h>
+#include <asm/semaphore.h>
+
+#include "avc.h"
+#include "objsec.h"
+#include "xfrm.h"
+
+
+/*
+ * Returns true if an LSM/SELinux context
+ */
+static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)
+{
+ return (ctx &&
+ (ctx->ctx_doi == XFRM_SC_DOI_LSM) &&
+ (ctx->ctx_alg == XFRM_SC_ALG_SELINUX));
+}
+
+/*
+ * Returns true if the xfrm contains a security blob for SELinux
+ */
+static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
+{
+ return selinux_authorizable_ctx(x->security);
+}
+
+/*
+ * LSM hook implementation that authorizes that a socket can be used
+ * with the corresponding xfrm_sec_ctx and direction.
+ */
+int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
+{
+ int rc = 0;
+ u32 sel_sid = SECINITSID_UNLABELED;
+ struct xfrm_sec_ctx *ctx;
+
+ /* Context sid is either set to label or ANY_ASSOC */
+ if ((ctx = xp->security)) {
+ if (!selinux_authorizable_ctx(ctx))
+ return -EINVAL;
+
+ sel_sid = ctx->ctx_sid;
+ }
+
+ rc = avc_has_perm(sk_sid, sel_sid, SECCLASS_ASSOCIATION,
+ ((dir == FLOW_DIR_IN) ? ASSOCIATION__RECVFROM :
+ ((dir == FLOW_DIR_OUT) ? ASSOCIATION__SENDTO :
+ (ASSOCIATION__SENDTO | ASSOCIATION__RECVFROM))),
+ NULL);
+
+ return rc;
+}
+
+/*
+ * Security blob allocation for xfrm_policy and xfrm_state
+ * CTX does not have a meaningful value on input
+ */
+static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_user_sec_ctx *uctx)
+{
+ int rc = 0;
+ struct task_security_struct *tsec = current->security;
+ struct xfrm_sec_ctx *ctx;
+
+ BUG_ON(!uctx);
+ BUG_ON(uctx->ctx_doi != XFRM_SC_ALG_SELINUX);
+
+ if (uctx->ctx_len >= PAGE_SIZE)
+ return -ENOMEM;
+
+ *ctxp = ctx = kmalloc(sizeof(*ctx) +
+ uctx->ctx_len,
+ GFP_KERNEL);
+
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->ctx_doi = uctx->ctx_doi;
+ ctx->ctx_len = uctx->ctx_len;
+ ctx->ctx_alg = uctx->ctx_alg;
+
+ memcpy(ctx->ctx_str,
+ uctx+1,
+ ctx->ctx_len);
+ rc = security_context_to_sid(ctx->ctx_str,
+ ctx->ctx_len,
+ &ctx->ctx_sid);
+
+ if (rc)
+ goto out;
+
+ /*
+ * Does the subject have permission to set security or permission to
+ * do the relabel?
+ * Must be permitted to relabel from default socket type (process type)
+ * to specified context
+ */
+ rc = avc_has_perm(tsec->sid, tsec->sid,
+ SECCLASS_ASSOCIATION,
+ ASSOCIATION__RELABELFROM, NULL);
+ if (rc)
+ goto out;
+
+ rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ SECCLASS_ASSOCIATION,
+ ASSOCIATION__RELABELTO, NULL);
+ if (rc)
+ goto out;
+
+ return rc;
+
+out:
+ *ctxp = 0;
+ kfree(ctx);
+ return rc;
+}
+
+/*
+ * LSM hook implementation that allocs and transfers uctx spec to
+ * xfrm_policy.
+ */
+int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *uctx)
+{
+ int err;
+
+ BUG_ON(!xp);
+
+ err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx);
+ return err;
+}
+
+
+/*
+ * LSM hook implementation that copies security data structure from old to
+ * new for policy cloning.
+ */
+int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new)
+{
+ struct xfrm_sec_ctx *old_ctx, *new_ctx;
+
+ old_ctx = old->security;
+
+ if (old_ctx) {
+ new_ctx = new->security = kmalloc(sizeof(*new_ctx) +
+ old_ctx->ctx_len,
+ GFP_KERNEL);
+
+ if (!new_ctx)
+ return -ENOMEM;
+
+ memcpy(new_ctx, old_ctx, sizeof(*new_ctx));
+ memcpy(new_ctx->ctx_str, old_ctx->ctx_str, new_ctx->ctx_len);
+ }
+ return 0;
+}
+
+/*
+ * LSM hook implementation that frees xfrm_policy security information.
+ */
+void selinux_xfrm_policy_free(struct xfrm_policy *xp)
+{
+ struct xfrm_sec_ctx *ctx = xp->security;
+ if (ctx)
+ kfree(ctx);
+}
+
+/*
+ * LSM hook implementation that allocs and transfers sec_ctx spec to
+ * xfrm_state.
+ */
+int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx)
+{
+ int err;
+
+ BUG_ON(!x);
+
+ err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx);
+ return err;
+}
+
+/*
+ * LSM hook implementation that frees xfrm_state security information.
+ */
+void selinux_xfrm_state_free(struct xfrm_state *x)
+{
+ struct xfrm_sec_ctx *ctx = x->security;
+ if (ctx)
+ kfree(ctx);
+}
+
+/*
+ * LSM hook that controls access to unlabelled packets. If
+ * a xfrm_state is authorizable (defined by macro) then it was
+ * already authorized by the IPSec process. If not, then
+ * we need to check for unlabelled access since this may not have
+ * gone thru the IPSec process.
+ */
+int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
+{
+ int i, rc = 0;
+ struct sec_path *sp;
+
+ sp = skb->sp;
+
+ if (sp) {
+ /*
+ * __xfrm_policy_check does not approve unless xfrm_policy_ok
+ * says that spi's match for policy and the socket.
+ *
+ * Only need to verify the existence of an authorizable sp.
+ */
+ for (i = 0; i < sp->len; i++) {
+ struct xfrm_state *x = sp->x[i].xvec;
+
+ if (x && selinux_authorizable_xfrm(x))
+ goto accept;
+ }
+ }
+
+ /* check SELinux sock for unlabelled access */
+ rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
+ ASSOCIATION__RECVFROM, NULL);
+ if (rc)
+ goto drop;
+
+accept:
+ return 0;
+
+drop:
+ return rc;
+}
+
+/*
+ * POSTROUTE_LAST hook's XFRM processing:
+ * If we have no security association, then we need to determine
+ * whether the socket is allowed to send to an unlabelled destination.
+ * If we do have a authorizable security association, then it has already been
+ * checked in xfrm_policy_lookup hook.
+ */
+int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
+{
+ struct dst_entry *dst;
+ int rc = 0;
+
+ dst = skb->dst;
+
+ if (dst) {
+ struct dst_entry *dst_test;
+
+ for (dst_test = dst; dst_test != 0;
+ dst_test = dst_test->child) {
+ struct xfrm_state *x = dst_test->xfrm;
+
+ if (x && selinux_authorizable_xfrm(x))
+ goto accept;
+ }
+ }
+
+ rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
+ ASSOCIATION__SENDTO, NULL);
+ if (rc)
+ goto drop;
+
+accept:
+ return NF_ACCEPT;
+
+drop:
+ return NF_DROP;
+}
diff --git a/sound/sparc/Kconfig b/sound/sparc/Kconfig
index 09ab138646a6..ef022a846b06 100644
--- a/sound/sparc/Kconfig
+++ b/sound/sparc/Kconfig
@@ -1,7 +1,7 @@
# ALSA Sparc drivers
menu "ALSA Sparc devices"
- depends on SND!=n && (SPARC32 || SPARC64)
+ depends on SND!=n && SPARC
config SND_SUN_AMD7930
tristate "Sun AMD7930"
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 99dae024b640..22f8bb612bff 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -1996,7 +1996,6 @@ static struct usb_device_id usb_audio_ids [] = {
MODULE_DEVICE_TABLE (usb, usb_audio_ids);
static struct usb_driver usb_audio_driver = {
- .owner = THIS_MODULE,
.name = "snd-usb-audio",
.probe = usb_audio_probe,
.disconnect = usb_audio_disconnect,
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index cf77313c609d..a3967f72ab4e 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -409,7 +409,6 @@ static void snd_usX2Y_disconnect(struct usb_interface *intf)
MODULE_DEVICE_TABLE(usb, snd_usX2Y_usb_id_table);
static struct usb_driver snd_usX2Y_usb_driver = {
- .owner = THIS_MODULE,
.name = "snd-usb-usx2y",
.probe = snd_usX2Y_probe,
.disconnect = snd_usX2Y_disconnect,